[clang] Improve nested name specifier AST representation (#147835)

This is a major change on how we represent nested name qualifications in
the AST.

* The nested name specifier itself and how it's stored is changed. The
prefixes for types are handled within the type hierarchy, which makes
canonicalization for them super cheap, no memory allocation required.
Also translating a type into nested name specifier form becomes a no-op.
An identifier is stored as a DependentNameType. The nested name
specifier gains a lightweight handle class, to be used instead of
passing around pointers, which is similar to what is implemented for
TemplateName. There is still one free bit available, and this handle can
be used within a PointerUnion and PointerIntPair, which should keep
bit-packing aficionados happy.
* The ElaboratedType node is removed, all type nodes in which it could
previously apply to can now store the elaborated keyword and name
qualifier, tail allocating when present.
* TagTypes can now point to the exact declaration found when producing
these, as opposed to the previous situation of there only existing one
TagType per entity. This increases the amount of type sugar retained,
and can have several applications, for example in tracking module
ownership, and other tools which care about source file origins, such as
IWYU. These TagTypes are lazily allocated, in order to limit the
increase in AST size.

This patch offers a great performance benefit.

It greatly improves compilation time for
[stdexec](https://github.com/NVIDIA/stdexec). For one datapoint, for
`test_on2.cpp` in that project, which is the slowest compiling test,
this patch improves `-c` compilation time by about 7.2%, with the
`-fsyntax-only` improvement being at ~12%.

This has great results on compile-time-tracker as well:

![image](https://github.com/user-attachments/assets/700dce98-2cab-4aa8-97d1-b038c0bee831)

This patch also further enables other optimziations in the future, and
will reduce the performance impact of template specialization resugaring
when that lands.

It has some other miscelaneous drive-by fixes.

About the review: Yes the patch is huge, sorry about that. Part of the
reason is that I started by the nested name specifier part, before the
ElaboratedType part, but that had a huge performance downside, as
ElaboratedType is a big performance hog. I didn't have the steam to go
back and change the patch after the fact.

There is also a lot of internal API changes, and it made sense to remove
ElaboratedType in one go, versus removing it from one type at a time, as
that would present much more churn to the users. Also, the nested name
specifier having a different API avoids missing changes related to how
prefixes work now, which could make existing code compile but not work.

How to review: The important changes are all in
`clang/include/clang/AST` and `clang/lib/AST`, with also important
changes in `clang/lib/Sema/TreeTransform.h`.

The rest and bulk of the changes are mostly consequences of the changes
in API.

PS: TagType::getDecl is renamed to `getOriginalDecl` in this patch, just
for easier to rebasing. I plan to rename it back after this lands.

Fixes #136624
Fixes https://github.com/llvm/llvm-project/issues/43179
Fixes https://github.com/llvm/llvm-project/issues/68670
Fixes https://github.com/llvm/llvm-project/issues/92757
This commit is contained in:
Matheus Izvekov 2025-08-09 05:06:53 -03:00 committed by GitHub
parent fc44a4fcd3
commit 91cdd35008
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
539 changed files with 11503 additions and 10894 deletions

View File

@ -31,24 +31,9 @@ llvm::SmallVector<llvm::StringRef, 4> splitSymbolName(llvm::StringRef Name) {
return Splitted; return Splitted;
} }
SourceLocation startLocationForType(TypeLoc TLoc) {
// For elaborated types (e.g. `struct a::A`) we want the portion after the
// `struct` but including the namespace qualifier, `a::`.
if (TLoc.getTypeLocClass() == TypeLoc::Elaborated) {
NestedNameSpecifierLoc NestedNameSpecifier =
TLoc.castAs<ElaboratedTypeLoc>().getQualifierLoc();
if (NestedNameSpecifier.getNestedNameSpecifier())
return NestedNameSpecifier.getBeginLoc();
TLoc = TLoc.getNextTypeLoc();
}
return TLoc.getBeginLoc();
}
SourceLocation endLocationForType(TypeLoc TLoc) { SourceLocation endLocationForType(TypeLoc TLoc) {
// Dig past any namespace or keyword qualifications. if (auto QTL = TLoc.getAs<QualifiedTypeLoc>())
while (TLoc.getTypeLocClass() == TypeLoc::Elaborated || TLoc = QTL.getUnqualifiedLoc();
TLoc.getTypeLocClass() == TypeLoc::Qualified)
TLoc = TLoc.getNextTypeLoc();
// The location for template specializations (e.g. Foo<int>) includes the // The location for template specializations (e.g. Foo<int>) includes the
// templated types in its location range. We want to restrict this to just // templated types in its location range. We want to restrict this to just
@ -550,8 +535,8 @@ void ChangeNamespaceTool::run(
Result.Nodes.getNodeAs<NestedNameSpecifierLoc>( Result.Nodes.getNodeAs<NestedNameSpecifierLoc>(
"nested_specifier_loc")) { "nested_specifier_loc")) {
SourceLocation Start = Specifier->getBeginLoc(); SourceLocation Start = Specifier->getBeginLoc();
SourceLocation End = endLocationForType(Specifier->getTypeLoc()); SourceLocation End = endLocationForType(Specifier->castAsTypeLoc());
fixTypeLoc(Result, Start, End, Specifier->getTypeLoc()); fixTypeLoc(Result, Start, End, Specifier->castAsTypeLoc());
} else if (const auto *BaseInitializer = } else if (const auto *BaseInitializer =
Result.Nodes.getNodeAs<CXXCtorInitializer>( Result.Nodes.getNodeAs<CXXCtorInitializer>(
"base_initializer")) { "base_initializer")) {
@ -562,19 +547,16 @@ void ChangeNamespaceTool::run(
// filtered by matchers in some cases, e.g. the type is templated. We should // filtered by matchers in some cases, e.g. the type is templated. We should
// handle the record type qualifier instead. // handle the record type qualifier instead.
TypeLoc Loc = *TLoc; TypeLoc Loc = *TLoc;
while (Loc.getTypeLocClass() == TypeLoc::Qualified) if (auto QTL = Loc.getAs<QualifiedTypeLoc>())
Loc = Loc.getNextTypeLoc(); Loc = QTL.getUnqualifiedLoc();
if (Loc.getTypeLocClass() == TypeLoc::Elaborated) { // FIXME: avoid changing injected class names.
NestedNameSpecifierLoc NestedNameSpecifier = if (NestedNameSpecifier NestedNameSpecifier =
Loc.castAs<ElaboratedTypeLoc>().getQualifierLoc(); Loc.getPrefix().getNestedNameSpecifier();
// FIXME: avoid changing injected class names. NestedNameSpecifier.getKind() == NestedNameSpecifier::Kind::Type &&
if (auto *NNS = NestedNameSpecifier.getNestedNameSpecifier()) { NestedNameSpecifier.getAsType()->isRecordType())
const Type *SpecifierType = NNS->getAsType(); return;
if (SpecifierType && SpecifierType->isRecordType()) fixTypeLoc(Result, Loc.getNonElaboratedBeginLoc(), endLocationForType(Loc),
return; Loc);
}
}
fixTypeLoc(Result, startLocationForType(Loc), endLocationForType(Loc), Loc);
} else if (const auto *VarRef = } else if (const auto *VarRef =
Result.Nodes.getNodeAs<DeclRefExpr>("var_ref")) { Result.Nodes.getNodeAs<DeclRefExpr>("var_ref")) {
const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var_decl"); const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var_decl");
@ -588,10 +570,9 @@ void ChangeNamespaceTool::run(
} else if (const auto *EnumConstRef = } else if (const auto *EnumConstRef =
Result.Nodes.getNodeAs<DeclRefExpr>("enum_const_ref")) { Result.Nodes.getNodeAs<DeclRefExpr>("enum_const_ref")) {
// Do not rename the reference if it is already scoped by the EnumDecl name. // Do not rename the reference if it is already scoped by the EnumDecl name.
if (EnumConstRef->hasQualifier() && if (NestedNameSpecifier Qualifier = EnumConstRef->getQualifier();
EnumConstRef->getQualifier()->getKind() == Qualifier.getKind() == NestedNameSpecifier::Kind::Type &&
NestedNameSpecifier::SpecifierKind::TypeSpec && Qualifier.getAsType()->isEnumeralType())
EnumConstRef->getQualifier()->getAsType()->isEnumeralType())
return; return;
const auto *EnumConstDecl = const auto *EnumConstDecl =
Result.Nodes.getNodeAs<EnumConstantDecl>("enum_const_decl"); Result.Nodes.getNodeAs<EnumConstantDecl>("enum_const_decl");

View File

@ -902,8 +902,8 @@ parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,
return; return;
for (const CXXBaseSpecifier &B : D->bases()) { for (const CXXBaseSpecifier &B : D->bases()) {
if (const RecordType *Ty = B.getType()->getAs<RecordType>()) { if (const RecordType *Ty = B.getType()->getAs<RecordType>()) {
if (const CXXRecordDecl *Base = if (const CXXRecordDecl *Base = cast_or_null<CXXRecordDecl>(
cast_or_null<CXXRecordDecl>(Ty->getDecl()->getDefinition())) { Ty->getOriginalDecl()->getDefinition())) {
// Initialized without USR and name, this will be set in the following // Initialized without USR and name, this will be set in the following
// if-else stmt. // if-else stmt.
BaseRecordInfo BI( BaseRecordInfo BI(

View File

@ -216,8 +216,7 @@ void FindAllSymbols::registerMatchers(MatchFinder *MatchFinder) {
// Uses of most types: just look at what the typeLoc refers to. // Uses of most types: just look at what the typeLoc refers to.
MatchFinder->addMatcher( MatchFinder->addMatcher(
typeLoc(isExpansionInMainFile(), typeLoc(isExpansionInMainFile(),
loc(qualType(allOf(unless(elaboratedType()), loc(qualType(hasDeclaration(Types.bind("use"))))),
hasDeclaration(Types.bind("use")))))),
this); this);
// Uses of typedefs: these are often transparent to hasDeclaration, so we need // Uses of typedefs: these are often transparent to hasDeclaration, so we need
// to handle them explicitly. // to handle them explicitly.

View File

@ -533,7 +533,8 @@ void ClangTidyDiagnosticConsumer::forwardDiagnostic(const Diagnostic &Info) {
Builder << reinterpret_cast<const NamedDecl *>(Info.getRawArg(Index)); Builder << reinterpret_cast<const NamedDecl *>(Info.getRawArg(Index));
break; break;
case clang::DiagnosticsEngine::ak_nestednamespec: case clang::DiagnosticsEngine::ak_nestednamespec:
Builder << reinterpret_cast<NestedNameSpecifier *>(Info.getRawArg(Index)); Builder << NestedNameSpecifier::getFromVoidPointer(
reinterpret_cast<void *>(Info.getRawArg(Index)));
break; break;
case clang::DiagnosticsEngine::ak_declcontext: case clang::DiagnosticsEngine::ak_declcontext:
Builder << reinterpret_cast<DeclContext *>(Info.getRawArg(Index)); Builder << reinterpret_cast<DeclContext *>(Info.getRawArg(Index));

View File

@ -43,7 +43,8 @@ static bool isDerivedClassBefriended(const CXXRecordDecl *CRTP,
return false; return false;
} }
return FriendType->getType()->getAsCXXRecordDecl() == Derived; return declaresSameEntity(FriendType->getType()->getAsCXXRecordDecl(),
Derived);
}); });
} }
@ -55,7 +56,8 @@ getDerivedParameter(const ClassTemplateSpecializationDecl *CRTP,
CRTP->getTemplateArgs().asArray(), [&](const TemplateArgument &Arg) { CRTP->getTemplateArgs().asArray(), [&](const TemplateArgument &Arg) {
++Idx; ++Idx;
return Arg.getKind() == TemplateArgument::Type && return Arg.getKind() == TemplateArgument::Type &&
Arg.getAsType()->getAsCXXRecordDecl() == Derived; declaresSameEntity(Arg.getAsType()->getAsCXXRecordDecl(),
Derived);
}); });
return AnyOf ? CRTP->getSpecializedTemplate() return AnyOf ? CRTP->getSpecializedTemplate()

View File

@ -577,7 +577,7 @@ approximateImplicitConversion(const TheCheck &Check, QualType LType,
ImplicitConversionModellingMode ImplicitMode); ImplicitConversionModellingMode ImplicitMode);
static inline bool isUselessSugar(const Type *T) { static inline bool isUselessSugar(const Type *T) {
return isa<AttributedType, DecayedType, ElaboratedType, ParenType>(T); return isa<AttributedType, DecayedType, ParenType>(T);
} }
namespace { namespace {
@ -1040,7 +1040,9 @@ approximateStandardConversionSequence(const TheCheck &Check, QualType From,
const auto *ToRecord = To->getAsCXXRecordDecl(); const auto *ToRecord = To->getAsCXXRecordDecl();
if (isDerivedToBase(FromRecord, ToRecord)) { if (isDerivedToBase(FromRecord, ToRecord)) {
LLVM_DEBUG(llvm::dbgs() << "--- approximateStdConv. Derived To Base.\n"); LLVM_DEBUG(llvm::dbgs() << "--- approximateStdConv. Derived To Base.\n");
WorkType = QualType{ToRecord->getTypeForDecl(), FastQualifiersToApply}; WorkType = QualType{
ToRecord->getASTContext().getCanonicalTagType(ToRecord)->getTypePtr(),
FastQualifiersToApply};
} }
if (Ctx.getLangOpts().CPlusPlus17 && FromPtr && ToPtr) { if (Ctx.getLangOpts().CPlusPlus17 && FromPtr && ToPtr) {
@ -1072,9 +1074,9 @@ approximateStandardConversionSequence(const TheCheck &Check, QualType From,
WorkType = To; WorkType = To;
} }
if (WorkType == To) { if (Ctx.hasSameType(WorkType, To)) {
LLVM_DEBUG(llvm::dbgs() << "<<< approximateStdConv. Reached 'To' type.\n"); LLVM_DEBUG(llvm::dbgs() << "<<< approximateStdConv. Reached 'To' type.\n");
return {WorkType}; return {Ctx.getCommonSugaredType(WorkType, To)};
} }
LLVM_DEBUG(llvm::dbgs() << "<<< approximateStdConv. Did not reach 'To'.\n"); LLVM_DEBUG(llvm::dbgs() << "<<< approximateStdConv. Did not reach 'To'.\n");
@ -1219,7 +1221,7 @@ tryConversionOperators(const TheCheck &Check, const CXXRecordDecl *RD,
if (std::optional<UserDefinedConversionSelector::PreparedConversion> if (std::optional<UserDefinedConversionSelector::PreparedConversion>
SelectedConversion = ConversionSet()) { SelectedConversion = ConversionSet()) {
QualType RecordType{RD->getTypeForDecl(), 0}; CanQualType RecordType = RD->getASTContext().getCanonicalTagType(RD);
ConversionSequence Result{RecordType, ToType}; ConversionSequence Result{RecordType, ToType};
// The conversion from the operator call's return type to ToType was // The conversion from the operator call's return type to ToType was
@ -1270,7 +1272,7 @@ tryConvertingConstructors(const TheCheck &Check, QualType FromType,
if (std::optional<UserDefinedConversionSelector::PreparedConversion> if (std::optional<UserDefinedConversionSelector::PreparedConversion>
SelectedConversion = ConversionSet()) { SelectedConversion = ConversionSet()) {
QualType RecordType{RD->getTypeForDecl(), 0}; CanQualType RecordType = RD->getASTContext().getCanonicalTagType(RD);
ConversionSequence Result{FromType, RecordType}; ConversionSequence Result{FromType, RecordType};
Result.AfterFirstStandard = SelectedConversion->Seq.AfterFirstStandard; Result.AfterFirstStandard = SelectedConversion->Seq.AfterFirstStandard;

View File

@ -69,10 +69,9 @@ void ForwardDeclarationNamespaceCheck::check(
// struct B { friend A; }; // struct B { friend A; };
// \endcode // \endcode
// `A` will not be marked as "referenced" in the AST. // `A` will not be marked as "referenced" in the AST.
if (const TypeSourceInfo *Tsi = Decl->getFriendType()) { if (const TypeSourceInfo *Tsi = Decl->getFriendType())
QualType Desugared = Tsi->getType().getDesugaredType(*Result.Context); FriendTypes.insert(
FriendTypes.insert(Desugared.getTypePtr()); Tsi->getType()->getCanonicalTypeUnqualified().getTypePtr());
}
} }
} }
@ -119,7 +118,9 @@ void ForwardDeclarationNamespaceCheck::onEndOfTranslationUnit() {
if (CurDecl->hasDefinition() || CurDecl->isReferenced()) { if (CurDecl->hasDefinition() || CurDecl->isReferenced()) {
continue; // Skip forward declarations that are used/referenced. continue; // Skip forward declarations that are used/referenced.
} }
if (FriendTypes.contains(CurDecl->getTypeForDecl())) { if (FriendTypes.contains(CurDecl->getASTContext()
.getCanonicalTagType(CurDecl)
->getTypePtr())) {
continue; // Skip forward declarations referenced as friend. continue; // Skip forward declarations referenced as friend.
} }
if (CurDecl->getLocation().isMacroID() || if (CurDecl->getLocation().isMacroID() ||

View File

@ -33,21 +33,17 @@ AST_MATCHER(QualType, isEnableIf) {
BaseType = BaseType->getPointeeType().getTypePtr(); BaseType = BaseType->getPointeeType().getTypePtr();
} }
// Case: type parameter dependent (enable_if<is_integral<T>>). // Case: type parameter dependent (enable_if<is_integral<T>>).
if (const auto *Dependent = BaseType->getAs<DependentNameType>()) { if (const auto *Dependent = BaseType->getAs<DependentNameType>())
BaseType = Dependent->getQualifier()->getAsType(); BaseType = Dependent->getQualifier().getAsType();
}
if (!BaseType) if (!BaseType)
return false; return false;
if (CheckTemplate(BaseType->getAs<TemplateSpecializationType>())) if (CheckTemplate(BaseType->getAs<TemplateSpecializationType>()))
return true; // Case: enable_if_t< >. return true; // Case: enable_if_t< >.
if (const auto *Elaborated = BaseType->getAs<ElaboratedType>()) { if (const auto *TT = BaseType->getAs<TypedefType>())
if (const auto *Q = Elaborated->getQualifier()) if (NestedNameSpecifier Q = TT->getQualifier();
if (const auto *Qualifier = Q->getAsType()) { Q.getKind() == NestedNameSpecifier::Kind::Type)
if (CheckTemplate(Qualifier->getAs<TemplateSpecializationType>())) { if (CheckTemplate(Q.getAsType()->getAs<TemplateSpecializationType>()))
return true; // Case: enable_if< >::type. return true; // Case: enable_if< >::type.
}
}
}
return false; return false;
} }
AST_MATCHER_P(TemplateTypeParmDecl, hasDefaultArgument, AST_MATCHER_P(TemplateTypeParmDecl, hasDefaultArgument,

View File

@ -32,13 +32,10 @@ AST_MATCHER_P(TemplateTypeParmDecl, hasUnnamedDefaultArgument,
void IncorrectEnableIfCheck::registerMatchers(MatchFinder *Finder) { void IncorrectEnableIfCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher( Finder->addMatcher(
templateTypeParmDecl( templateTypeParmDecl(
hasUnnamedDefaultArgument( hasUnnamedDefaultArgument(templateSpecializationTypeLoc(
elaboratedTypeLoc( loc(qualType(hasDeclaration(namedDecl(
hasNamedTypeLoc(templateSpecializationTypeLoc( hasName("::std::enable_if"))))))
loc(qualType(hasDeclaration(namedDecl( .bind("enable_if_specialization")))
hasName("::std::enable_if"))))))
.bind("enable_if_specialization")))
.bind("elaborated")))
.bind("enable_if"), .bind("enable_if"),
this); this);
} }
@ -46,13 +43,11 @@ void IncorrectEnableIfCheck::registerMatchers(MatchFinder *Finder) {
void IncorrectEnableIfCheck::check(const MatchFinder::MatchResult &Result) { void IncorrectEnableIfCheck::check(const MatchFinder::MatchResult &Result) {
const auto *EnableIf = const auto *EnableIf =
Result.Nodes.getNodeAs<TemplateTypeParmDecl>("enable_if"); Result.Nodes.getNodeAs<TemplateTypeParmDecl>("enable_if");
const auto *ElaboratedLoc =
Result.Nodes.getNodeAs<ElaboratedTypeLoc>("elaborated");
const auto *EnableIfSpecializationLoc = const auto *EnableIfSpecializationLoc =
Result.Nodes.getNodeAs<TemplateSpecializationTypeLoc>( Result.Nodes.getNodeAs<TemplateSpecializationTypeLoc>(
"enable_if_specialization"); "enable_if_specialization");
if (!EnableIf || !ElaboratedLoc || !EnableIfSpecializationLoc) if (!EnableIf || !EnableIfSpecializationLoc)
return; return;
const SourceManager &SM = *Result.SourceManager; const SourceManager &SM = *Result.SourceManager;
@ -62,8 +57,10 @@ void IncorrectEnableIfCheck::check(const MatchFinder::MatchResult &Result) {
auto Diag = diag(EnableIf->getBeginLoc(), auto Diag = diag(EnableIf->getBeginLoc(),
"incorrect std::enable_if usage detected; use " "incorrect std::enable_if usage detected; use "
"'typename std::enable_if<...>::type'"); "'typename std::enable_if<...>::type'");
// FIXME: This should handle the enable_if specialization already having an
// elaborated keyword.
if (!getLangOpts().CPlusPlus20) { if (!getLangOpts().CPlusPlus20) {
Diag << FixItHint::CreateInsertion(ElaboratedLoc->getBeginLoc(), Diag << FixItHint::CreateInsertion(EnableIfSpecializationLoc->getBeginLoc(),
"typename "); "typename ");
} }
Diag << FixItHint::CreateInsertion(RAngleLoc.getLocWithOffset(1), "::type"); Diag << FixItHint::CreateInsertion(RAngleLoc.getLocWithOffset(1), "::type");

View File

@ -67,15 +67,15 @@ public:
return Visit(T->getElementType().getTypePtr()); return Visit(T->getElementType().getTypePtr());
} }
bool VisitEnumType(const EnumType *T) { bool VisitEnumType(const EnumType *T) {
if (isCompleteAndHasNoZeroValue(T->getDecl())) { if (isCompleteAndHasNoZeroValue(T->getOriginalDecl())) {
FoundEnum = T; FoundEnum = T;
return true; return true;
} }
return false; return false;
} }
bool VisitRecordType(const RecordType *T) { bool VisitRecordType(const RecordType *T) {
const RecordDecl *RD = T->getDecl(); const RecordDecl *RD = T->getOriginalDecl()->getDefinition();
if (RD->isUnion()) if (!RD || RD->isUnion())
return false; return false;
auto VisitField = [this](const FieldDecl *F) { auto VisitField = [this](const FieldDecl *F) {
return Visit(F->getType().getTypePtr()); return Visit(F->getType().getTypePtr());
@ -125,7 +125,7 @@ void InvalidEnumDefaultInitializationCheck::check(
if (!Finder.Visit(InitList->getArrayFiller()->getType().getTypePtr())) if (!Finder.Visit(InitList->getArrayFiller()->getType().getTypePtr()))
return; return;
InitExpr = InitList; InitExpr = InitList;
Enum = Finder.FoundEnum->getDecl(); Enum = Finder.FoundEnum->getOriginalDecl();
} }
if (!InitExpr || !Enum) if (!InitExpr || !Enum)

View File

@ -39,24 +39,31 @@ static void replaceMoveWithForward(const UnresolvedLookupExpr *Callee,
// std::move(). This will hopefully prevent erroneous replacements if the // std::move(). This will hopefully prevent erroneous replacements if the
// code does unusual things (e.g. create an alias for std::move() in // code does unusual things (e.g. create an alias for std::move() in
// another namespace). // another namespace).
NestedNameSpecifier *NNS = Callee->getQualifier(); NestedNameSpecifier NNS = Callee->getQualifier();
if (!NNS) { switch (NNS.getKind()) {
case NestedNameSpecifier::Kind::Null:
// Called as "move" (i.e. presumably the code had a "using std::move;"). // Called as "move" (i.e. presumably the code had a "using std::move;").
// We still conservatively put a "std::" in front of the forward because // We still conservatively put a "std::" in front of the forward because
// we don't know whether the code also had a "using std::forward;". // we don't know whether the code also had a "using std::forward;".
Diag << FixItHint::CreateReplacement(CallRange, "std::" + ForwardName); Diag << FixItHint::CreateReplacement(CallRange, "std::" + ForwardName);
} else if (const NamespaceBaseDecl *Namespace = NNS->getAsNamespace()) { break;
case NestedNameSpecifier::Kind::Namespace: {
auto [Namespace, Prefix] = NNS.getAsNamespaceAndPrefix();
if (Namespace->getName() == "std") { if (Namespace->getName() == "std") {
if (!NNS->getPrefix()) { if (!Prefix) {
// Called as "std::move". // Called as "std::move".
Diag << FixItHint::CreateReplacement(CallRange, Diag << FixItHint::CreateReplacement(CallRange,
"std::" + ForwardName); "std::" + ForwardName);
} else if (NNS->getPrefix()->getKind() == NestedNameSpecifier::Global) { } else if (Prefix.getKind() == NestedNameSpecifier::Kind::Global) {
// Called as "::std::move". // Called as "::std::move".
Diag << FixItHint::CreateReplacement(CallRange, Diag << FixItHint::CreateReplacement(CallRange,
"::std::" + ForwardName); "::std::" + ForwardName);
} }
} }
break;
}
default:
return;
} }
} }
} }

View File

@ -425,7 +425,7 @@ void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) {
"suspicious usage of 'sizeof(array)/sizeof(...)';" "suspicious usage of 'sizeof(array)/sizeof(...)';"
" denominator differs from the size of array elements") " denominator differs from the size of array elements")
<< E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange();
} else if (NumTy && DenomTy && NumTy == DenomTy && } else if (NumTy && DenomTy && Ctx.hasSameType(NumTy, DenomTy) &&
!NumTy->isDependentType()) { !NumTy->isDependentType()) {
// Dependent type should not be compared. // Dependent type should not be compared.
diag(E->getOperatorLoc(), diag(E->getOperatorLoc(),
@ -434,7 +434,7 @@ void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) {
<< E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange();
} else if (!WarnOnSizeOfPointer) { } else if (!WarnOnSizeOfPointer) {
// When 'WarnOnSizeOfPointer' is enabled, these messages become redundant: // When 'WarnOnSizeOfPointer' is enabled, these messages become redundant:
if (PointedTy && DenomTy && PointedTy == DenomTy) { if (PointedTy && DenomTy && Ctx.hasSameType(PointedTy, DenomTy)) {
diag(E->getOperatorLoc(), diag(E->getOperatorLoc(),
"suspicious usage of 'sizeof(...)/sizeof(...)'; size of pointer " "suspicious usage of 'sizeof(...)/sizeof(...)'; size of pointer "
"is divided by size of pointed type") "is divided by size of pointed type")
@ -463,7 +463,8 @@ void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) {
const auto *SizeOfExpr = const auto *SizeOfExpr =
Result.Nodes.getNodeAs<UnaryExprOrTypeTraitExpr>("sizeof-ptr-mul-expr"); Result.Nodes.getNodeAs<UnaryExprOrTypeTraitExpr>("sizeof-ptr-mul-expr");
if ((LPtrTy == RPtrTy) && (LPtrTy == SizeofArgTy)) { if (Ctx.hasSameType(LPtrTy, RPtrTy) &&
Ctx.hasSameType(LPtrTy, SizeofArgTy)) {
diag(SizeOfExpr->getBeginLoc(), "suspicious usage of 'sizeof(...)' in " diag(SizeOfExpr->getBeginLoc(), "suspicious usage of 'sizeof(...)' in "
"pointer arithmetic") "pointer arithmetic")
<< SizeOfExpr->getSourceRange() << E->getOperatorLoc() << SizeOfExpr->getSourceRange() << E->getOperatorLoc()
@ -477,7 +478,8 @@ void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) {
const auto *SizeOfExpr = const auto *SizeOfExpr =
Result.Nodes.getNodeAs<UnaryExprOrTypeTraitExpr>("sizeof-ptr-div-expr"); Result.Nodes.getNodeAs<UnaryExprOrTypeTraitExpr>("sizeof-ptr-div-expr");
if ((LPtrTy == RPtrTy) && (LPtrTy == SizeofArgTy)) { if (Ctx.hasSameType(LPtrTy, RPtrTy) &&
Ctx.hasSameType(LPtrTy, SizeofArgTy)) {
diag(SizeOfExpr->getBeginLoc(), "suspicious usage of 'sizeof(...)' in " diag(SizeOfExpr->getBeginLoc(), "suspicious usage of 'sizeof(...)' in "
"pointer arithmetic") "pointer arithmetic")
<< SizeOfExpr->getSourceRange() << E->getOperatorLoc() << SizeOfExpr->getSourceRange() << E->getOperatorLoc()

View File

@ -23,9 +23,9 @@ void NoSuspendWithLockCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
} }
void NoSuspendWithLockCheck::registerMatchers(MatchFinder *Finder) { void NoSuspendWithLockCheck::registerMatchers(MatchFinder *Finder) {
auto LockType = elaboratedType(namesType(templateSpecializationType( auto LockType = templateSpecializationType(
hasDeclaration(namedDecl(matchers::matchesAnyListedName( hasDeclaration(namedDecl(matchers::matchesAnyListedName(
utils::options::parseStringList(LockGuards))))))); utils::options::parseStringList(LockGuards)))));
StatementMatcher Lock = StatementMatcher Lock =
declStmt(has(varDecl(hasType(LockType)).bind("lock-decl"))) declStmt(has(varDecl(hasType(LockType)).bind("lock-decl")))

View File

@ -190,7 +190,7 @@ struct InitializerInsertion {
// Convenience utility to get a RecordDecl from a QualType. // Convenience utility to get a RecordDecl from a QualType.
const RecordDecl *getCanonicalRecordDecl(const QualType &Type) { const RecordDecl *getCanonicalRecordDecl(const QualType &Type) {
if (const auto *RT = Type.getCanonicalType()->getAs<RecordType>()) if (const auto *RT = Type.getCanonicalType()->getAs<RecordType>())
return RT->getDecl(); return RT->getOriginalDecl();
return nullptr; return nullptr;
} }

View File

@ -92,7 +92,7 @@ void SlicingCheck::diagnoseSlicedOverriddenMethods(
for (const auto &Base : DerivedDecl.bases()) { for (const auto &Base : DerivedDecl.bases()) {
if (const auto *BaseRecordType = Base.getType()->getAs<RecordType>()) { if (const auto *BaseRecordType = Base.getType()->getAs<RecordType>()) {
if (const auto *BaseRecord = cast_or_null<CXXRecordDecl>( if (const auto *BaseRecord = cast_or_null<CXXRecordDecl>(
BaseRecordType->getDecl()->getDefinition())) BaseRecordType->getOriginalDecl()->getDefinition()))
diagnoseSlicedOverriddenMethods(Call, *BaseRecord, BaseDecl); diagnoseSlicedOverriddenMethods(Call, *BaseRecord, BaseDecl);
} }
} }

View File

@ -74,7 +74,7 @@ bool MultipleInheritanceCheck::isInterface(const CXXRecordDecl *Node) {
const auto *Ty = I.getType()->getAs<RecordType>(); const auto *Ty = I.getType()->getAs<RecordType>();
if (!Ty) if (!Ty)
continue; continue;
const RecordDecl *D = Ty->getDecl()->getDefinition(); const RecordDecl *D = Ty->getOriginalDecl()->getDefinition();
if (!D) if (!D)
continue; continue;
const auto *Base = cast<CXXRecordDecl>(D); const auto *Base = cast<CXXRecordDecl>(D);
@ -106,7 +106,8 @@ void MultipleInheritanceCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Ty = I.getType()->getAs<RecordType>(); const auto *Ty = I.getType()->getAs<RecordType>();
if (!Ty) if (!Ty)
continue; continue;
const auto *Base = cast<CXXRecordDecl>(Ty->getDecl()->getDefinition()); const auto *Base =
cast<CXXRecordDecl>(Ty->getOriginalDecl()->getDefinition());
if (!isInterface(Base)) if (!isInterface(Base))
NumConcrete++; NumConcrete++;
} }
@ -117,7 +118,8 @@ void MultipleInheritanceCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Ty = V.getType()->getAs<RecordType>(); const auto *Ty = V.getType()->getAs<RecordType>();
if (!Ty) if (!Ty)
continue; continue;
const auto *Base = cast<CXXRecordDecl>(Ty->getDecl()->getDefinition()); const auto *Base =
cast<CXXRecordDecl>(Ty->getOriginalDecl()->getDefinition());
if (!isInterface(Base)) if (!isInterface(Base))
NumConcrete++; NumConcrete++;
} }

View File

@ -89,6 +89,30 @@ static StringRef getDestTypeString(const SourceManager &SM,
SM, LangOpts); SM, LangOpts);
} }
static bool sameTypeAsWritten(QualType X, QualType Y) {
if (X.getCanonicalType() != Y.getCanonicalType())
return false;
auto TC = X->getTypeClass();
if (TC != Y->getTypeClass())
return false;
switch (TC) {
case Type::Typedef:
return declaresSameEntity(cast<TypedefType>(X)->getDecl(),
cast<TypedefType>(Y)->getDecl());
case Type::Pointer:
return sameTypeAsWritten(cast<PointerType>(X)->getPointeeType(),
cast<PointerType>(Y)->getPointeeType());
case Type::RValueReference:
case Type::LValueReference:
return sameTypeAsWritten(cast<ReferenceType>(X)->getPointeeType(),
cast<ReferenceType>(Y)->getPointeeType());
default:
return true;
}
}
void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) { void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) {
const auto *CastExpr = Result.Nodes.getNodeAs<ExplicitCastExpr>("cast"); const auto *CastExpr = Result.Nodes.getNodeAs<ExplicitCastExpr>("cast");
@ -128,12 +152,7 @@ void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) {
// case of overloaded functions, so detection of redundant casts is trickier // case of overloaded functions, so detection of redundant casts is trickier
// in this case. Don't emit "redundant cast" warnings for function // in this case. Don't emit "redundant cast" warnings for function
// pointer/reference types. // pointer/reference types.
QualType Src = SourceTypeAsWritten, Dst = DestTypeAsWritten; if (sameTypeAsWritten(SourceTypeAsWritten, DestTypeAsWritten)) {
if (const auto *ElTy = dyn_cast<ElaboratedType>(Src))
Src = ElTy->getNamedType();
if (const auto *ElTy = dyn_cast<ElaboratedType>(Dst))
Dst = ElTy->getNamedType();
if (Src == Dst) {
diag(CastExpr->getBeginLoc(), "redundant cast to the same type") diag(CastExpr->getBeginLoc(), "redundant cast to the same type")
<< FixItHint::CreateRemoval(ReplaceRange); << FixItHint::CreateRemoval(ReplaceRange);
return; return;

View File

@ -72,7 +72,7 @@ static bool isStdInitializerList(QualType Type) {
} }
if (const auto *RT = Type->getAs<RecordType>()) { if (const auto *RT = Type->getAs<RecordType>()) {
if (const auto *Specialization = if (const auto *Specialization =
dyn_cast<ClassTemplateSpecializationDecl>(RT->getDecl())) dyn_cast<ClassTemplateSpecializationDecl>(RT->getOriginalDecl()))
return declIsStdInitializerList(Specialization->getSpecializedTemplate()); return declIsStdInitializerList(Specialization->getSpecializedTemplate());
} }
return false; return false;

View File

@ -257,8 +257,13 @@ getAliasNameRange(const MatchFinder::MatchResult &Result) {
return CharSourceRange::getTokenRange( return CharSourceRange::getTokenRange(
Using->getNameInfo().getSourceRange()); Using->getNameInfo().getSourceRange());
} }
return CharSourceRange::getTokenRange( TypeLoc TL = *Result.Nodes.getNodeAs<TypeLoc>("typeloc");
Result.Nodes.getNodeAs<TypeLoc>("typeloc")->getSourceRange()); if (auto QTL = TL.getAs<QualifiedTypeLoc>())
TL = QTL.getUnqualifiedLoc();
if (auto TTL = TL.getAs<TypedefTypeLoc>())
return CharSourceRange::getTokenRange(TTL.getNameLoc());
return CharSourceRange::getTokenRange(TL.castAs<UsingTypeLoc>().getNameLoc());
} }
void UpgradeGoogletestCaseCheck::check(const MatchFinder::MatchResult &Result) { void UpgradeGoogletestCaseCheck::check(const MatchFinder::MatchResult &Result) {

View File

@ -98,11 +98,12 @@ void ConstCorrectnessCheck::registerMatchers(MatchFinder *Finder) {
hasType(referenceType(pointee(hasCanonicalType(templateTypeParmType())))), hasType(referenceType(pointee(hasCanonicalType(templateTypeParmType())))),
hasType(referenceType(pointee(substTemplateTypeParmType())))); hasType(referenceType(pointee(substTemplateTypeParmType()))));
const auto AllowedType = hasType(qualType(anyOf( auto AllowedTypeDecl = namedDecl(
hasDeclaration(namedDecl(matchers::matchesAnyListedName(AllowedTypes))), anyOf(matchers::matchesAnyListedName(AllowedTypes), usingShadowDecl()));
references(namedDecl(matchers::matchesAnyListedName(AllowedTypes))),
pointerType(pointee(hasDeclaration( const auto AllowedType = hasType(qualType(
namedDecl(matchers::matchesAnyListedName(AllowedTypes)))))))); anyOf(hasDeclaration(AllowedTypeDecl), references(AllowedTypeDecl),
pointerType(pointee(hasDeclaration(AllowedTypeDecl))))));
const auto AutoTemplateType = varDecl( const auto AutoTemplateType = varDecl(
anyOf(hasType(autoType()), hasType(referenceType(pointee(autoType()))), anyOf(hasType(autoType()), hasType(referenceType(pointee(autoType()))),

View File

@ -19,13 +19,13 @@ void MisplacedConstCheck::registerMatchers(MatchFinder *Finder) {
pointee(anyOf(isConstQualified(), ignoringParens(functionType())))))); pointee(anyOf(isConstQualified(), ignoringParens(functionType()))))));
Finder->addMatcher( Finder->addMatcher(
valueDecl(hasType(qualType( valueDecl(
isConstQualified(), hasType(qualType(isConstQualified(),
elaboratedType(namesType(typedefType(hasDeclaration( typedefType(hasDeclaration(anyOf(
anyOf(typedefDecl(NonConstAndNonFunctionPointerType) typedefDecl(NonConstAndNonFunctionPointerType)
.bind("typedef"), .bind("typedef"),
typeAliasDecl(NonConstAndNonFunctionPointerType) typeAliasDecl(NonConstAndNonFunctionPointerType)
.bind("typeAlias"))))))))) .bind("typeAlias")))))))
.bind("decl"), .bind("decl"),
this); this);
} }

View File

@ -45,14 +45,6 @@ static bool incrementWithoutOverflow(const APSInt &Value, APSInt &Result) {
return Value < Result; return Value < Result;
} }
static bool areEquivalentNameSpecifier(const NestedNameSpecifier *Left,
const NestedNameSpecifier *Right) {
llvm::FoldingSetNodeID LeftID, RightID;
Left->Profile(LeftID);
Right->Profile(RightID);
return LeftID == RightID;
}
static bool areEquivalentExpr(const Expr *Left, const Expr *Right) { static bool areEquivalentExpr(const Expr *Left, const Expr *Right) {
if (!Left || !Right) if (!Left || !Right)
return !Left && !Right; return !Left && !Right;
@ -104,9 +96,8 @@ static bool areEquivalentExpr(const Expr *Left, const Expr *Right) {
if (cast<DependentScopeDeclRefExpr>(Left)->getDeclName() != if (cast<DependentScopeDeclRefExpr>(Left)->getDeclName() !=
cast<DependentScopeDeclRefExpr>(Right)->getDeclName()) cast<DependentScopeDeclRefExpr>(Right)->getDeclName())
return false; return false;
return areEquivalentNameSpecifier( return cast<DependentScopeDeclRefExpr>(Left)->getQualifier() ==
cast<DependentScopeDeclRefExpr>(Left)->getQualifier(), cast<DependentScopeDeclRefExpr>(Right)->getQualifier();
cast<DependentScopeDeclRefExpr>(Right)->getQualifier());
case Stmt::DeclRefExprClass: case Stmt::DeclRefExprClass:
return cast<DeclRefExpr>(Left)->getDecl() == return cast<DeclRefExpr>(Left)->getDecl() ==
cast<DeclRefExpr>(Right)->getDecl(); cast<DeclRefExpr>(Right)->getDecl();

View File

@ -35,12 +35,12 @@ void UnusedAliasDeclsCheck::check(const MatchFinder::MatchResult &Result) {
} }
if (const auto *NestedName = if (const auto *NestedName =
Result.Nodes.getNodeAs<NestedNameSpecifier>("nns")) { Result.Nodes.getNodeAs<NestedNameSpecifier>("nns");
if (const auto *AliasDecl = dyn_cast_if_present<NamespaceAliasDecl>( NestedName &&
NestedName->getAsNamespace())) { NestedName->getKind() == NestedNameSpecifier::Kind::Namespace)
if (const auto *AliasDecl = dyn_cast<NamespaceAliasDecl>(
NestedName->getAsNamespaceAndPrefix().Namespace))
FoundDecls[AliasDecl] = CharSourceRange(); FoundDecls[AliasDecl] = CharSourceRange();
}
}
} }
void UnusedAliasDeclsCheck::onEndOfTranslationUnit() { void UnusedAliasDeclsCheck::onEndOfTranslationUnit() {

View File

@ -71,11 +71,7 @@ void UnusedUsingDeclsCheck::registerMatchers(MatchFinder *Finder) {
templateArgument().bind("used")))), templateArgument().bind("used")))),
this); this);
Finder->addMatcher(userDefinedLiteral().bind("used"), this); Finder->addMatcher(userDefinedLiteral().bind("used"), this);
Finder->addMatcher( Finder->addMatcher(loc(asTagDecl(tagDecl().bind("used"))), this);
loc(elaboratedType(unless(hasQualifier(nestedNameSpecifier())),
hasUnqualifiedDesugaredType(
type(asTagDecl(tagDecl().bind("used")))))),
this);
// Cases where we can identify the UsingShadowDecl directly, rather than // Cases where we can identify the UsingShadowDecl directly, rather than
// just its target. // just its target.
// FIXME: cover more cases in this way, as the AST supports it. // FIXME: cover more cases in this way, as the AST supports it.
@ -136,7 +132,7 @@ void UnusedUsingDeclsCheck::check(const MatchFinder::MatchResult &Result) {
} }
if (const auto *ECD = dyn_cast<EnumConstantDecl>(Used)) { if (const auto *ECD = dyn_cast<EnumConstantDecl>(Used)) {
if (const auto *ET = ECD->getType()->getAs<EnumType>()) if (const auto *ET = ECD->getType()->getAs<EnumType>())
removeFromFoundDecls(ET->getDecl()); removeFromFoundDecls(ET->getOriginalDecl());
} }
}; };
// We rely on the fact that the clang AST is walked in order, usages are only // We rely on the fact that the clang AST is walked in order, usages are only

View File

@ -29,8 +29,7 @@ static std::optional<const char *> getReplacementType(StringRef Type) {
void DeprecatedIosBaseAliasesCheck::registerMatchers(MatchFinder *Finder) { void DeprecatedIosBaseAliasesCheck::registerMatchers(MatchFinder *Finder) {
auto IoStateDecl = typedefDecl(hasAnyName(DeprecatedTypes)).bind("TypeDecl"); auto IoStateDecl = typedefDecl(hasAnyName(DeprecatedTypes)).bind("TypeDecl");
auto IoStateType = auto IoStateType = typedefType(hasDeclaration(IoStateDecl));
qualType(hasDeclaration(IoStateDecl), unless(elaboratedType()));
Finder->addMatcher(typeLoc(loc(IoStateType)).bind("TypeLoc"), this); Finder->addMatcher(typeLoc(loc(IoStateType)).bind("TypeLoc"), this);
} }
@ -43,12 +42,14 @@ void DeprecatedIosBaseAliasesCheck::check(
StringRef TypeName = Typedef->getName(); StringRef TypeName = Typedef->getName();
auto Replacement = getReplacementType(TypeName); auto Replacement = getReplacementType(TypeName);
const auto *TL = Result.Nodes.getNodeAs<TypeLoc>("TypeLoc"); TypeLoc TL = *Result.Nodes.getNodeAs<TypeLoc>("TypeLoc");
SourceLocation IoStateLoc = TL->getBeginLoc(); if (auto QTL = TL.getAs<QualifiedTypeLoc>())
TL = QTL.getUnqualifiedLoc();
SourceLocation IoStateLoc = TL.castAs<TypedefTypeLoc>().getNameLoc();
// Do not generate fixits for matches depending on template arguments and // Do not generate fixits for matches depending on template arguments and
// macro expansions. // macro expansions.
bool Fix = Replacement && !TL->getType()->isDependentType(); bool Fix = Replacement && !TL.getType()->isDependentType();
if (IoStateLoc.isMacroID()) { if (IoStateLoc.isMacroID()) {
IoStateLoc = SM.getSpellingLoc(IoStateLoc); IoStateLoc = SM.getSpellingLoc(IoStateLoc);
Fix = false; Fix = false;

View File

@ -77,8 +77,7 @@ AST_MATCHER_P(CXXRecordDecl, isMoveConstructibleInBoundCXXRecordDecl, StringRef,
static TypeMatcher notTemplateSpecConstRefType() { static TypeMatcher notTemplateSpecConstRefType() {
return lValueReferenceType( return lValueReferenceType(
pointee(unless(elaboratedType(namesType(templateSpecializationType()))), pointee(unless(templateSpecializationType()), isConstQualified()));
isConstQualified()));
} }
static TypeMatcher nonConstValueType() { static TypeMatcher nonConstValueType() {

View File

@ -48,7 +48,7 @@ void ReplaceAutoPtrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
void ReplaceAutoPtrCheck::registerMatchers(MatchFinder *Finder) { void ReplaceAutoPtrCheck::registerMatchers(MatchFinder *Finder) {
auto AutoPtrDecl = recordDecl(hasName("auto_ptr"), isInStdNamespace()); auto AutoPtrDecl = recordDecl(hasName("auto_ptr"), isInStdNamespace());
auto AutoPtrType = qualType(hasDeclaration(AutoPtrDecl)); auto AutoPtrType = hasCanonicalType(recordType(hasDeclaration(AutoPtrDecl)));
// std::auto_ptr<int> a; // std::auto_ptr<int> a;
// ^~~~~~~~~~~~~ // ^~~~~~~~~~~~~
@ -58,11 +58,7 @@ void ReplaceAutoPtrCheck::registerMatchers(MatchFinder *Finder) {
// //
// std::auto_ptr<int> fn(std::auto_ptr<int>); // std::auto_ptr<int> fn(std::auto_ptr<int>);
// ^~~~~~~~~~~~~ ^~~~~~~~~~~~~ // ^~~~~~~~~~~~~ ^~~~~~~~~~~~~
Finder->addMatcher(typeLoc(loc(qualType(AutoPtrType, Finder->addMatcher(typeLoc(loc(qualType(AutoPtrType))).bind(AutoPtrTokenId),
// Skip elaboratedType() as the named
// type will match soon thereafter.
unless(elaboratedType()))))
.bind(AutoPtrTokenId),
this); this);
// using std::auto_ptr; // using std::auto_ptr;
@ -118,10 +114,13 @@ void ReplaceAutoPtrCheck::check(const MatchFinder::MatchResult &Result) {
} }
SourceLocation AutoPtrLoc; SourceLocation AutoPtrLoc;
if (const auto *TL = Result.Nodes.getNodeAs<TypeLoc>(AutoPtrTokenId)) { if (const auto *PTL = Result.Nodes.getNodeAs<TypeLoc>(AutoPtrTokenId)) {
auto TL = *PTL;
if (auto QTL = TL.getAs<QualifiedTypeLoc>())
TL = QTL.getUnqualifiedLoc();
// std::auto_ptr<int> i; // std::auto_ptr<int> i;
// ^ // ^
if (auto Loc = TL->getAs<TemplateSpecializationTypeLoc>()) if (auto Loc = TL.getAs<TemplateSpecializationTypeLoc>())
AutoPtrLoc = Loc.getTemplateNameLoc(); AutoPtrLoc = Loc.getTemplateNameLoc();
} else if (const auto *D = } else if (const auto *D =
Result.Nodes.getNodeAs<UsingDecl>(AutoPtrTokenId)) { Result.Nodes.getNodeAs<UsingDecl>(AutoPtrTokenId)) {

View File

@ -168,19 +168,6 @@ static DeclarationName getName(const DeclRefExpr &D) {
return D.getDecl()->getDeclName(); return D.getDecl()->getDeclName();
} }
static bool isNamedType(const ElaboratedTypeLoc &ETL) {
if (const auto *TFT =
ETL.getNamedTypeLoc().getTypePtr()->getAs<TypedefType>()) {
const TypedefNameDecl *Decl = TFT->getDecl();
return Decl->getDeclName().isIdentifier() && Decl->getName() == "type";
}
return false;
}
static bool isNamedType(const DependentNameTypeLoc &DTL) {
return DTL.getTypePtr()->getIdentifier()->getName() == "type";
}
namespace { namespace {
AST_POLYMORPHIC_MATCHER(isValue, AST_POLYMORPHIC_SUPPORTED_TYPES( AST_POLYMORPHIC_MATCHER(isValue, AST_POLYMORPHIC_SUPPORTED_TYPES(
DeclRefExpr, DependentScopeDeclRefExpr)) { DeclRefExpr, DependentScopeDeclRefExpr)) {
@ -188,10 +175,14 @@ AST_POLYMORPHIC_MATCHER(isValue, AST_POLYMORPHIC_SUPPORTED_TYPES(
return Ident && Ident->isStr("value"); return Ident && Ident->isStr("value");
} }
AST_POLYMORPHIC_MATCHER(isType, AST_MATCHER(TypeLoc, isType) {
AST_POLYMORPHIC_SUPPORTED_TYPES(ElaboratedTypeLoc, if (auto TL = Node.getAs<TypedefTypeLoc>()) {
DependentNameTypeLoc)) { const auto *TD = TL.getDecl();
return Node.getBeginLoc().isValid() && isNamedType(Node); return TD->getDeclName().isIdentifier() && TD->getName() == "type";
}
if (auto TL = Node.getAs<DependentNameTypeLoc>())
return TL.getTypePtr()->getIdentifier()->getName() == "type";
return false;
} }
} // namespace } // namespace
@ -214,10 +205,7 @@ void TypeTraitsCheck::registerMatchers(MatchFinder *Finder) {
.bind(Bind), .bind(Bind),
this); this);
} }
Finder->addMatcher(mapAnyOf(elaboratedTypeLoc, dependentNameTypeLoc) Finder->addMatcher(typeLoc(isType()).bind(Bind), this);
.with(isType())
.bind(Bind),
this);
} }
static bool isNamedDeclInStdTraitsSet(const NamedDecl *ND, static bool isNamedDeclInStdTraitsSet(const NamedDecl *ND,
@ -226,14 +214,11 @@ static bool isNamedDeclInStdTraitsSet(const NamedDecl *ND,
Set.contains(ND->getName()); Set.contains(ND->getName());
} }
static bool checkTemplatedDecl(const NestedNameSpecifier *NNS, static bool checkTemplatedDecl(NestedNameSpecifier NNS,
const llvm::StringSet<> &Set) { const llvm::StringSet<> &Set) {
if (!NNS) if (NNS.getKind() != NestedNameSpecifier::Kind::Type)
return false; return false;
const Type *NNST = NNS->getAsType(); const auto *TST = NNS.getAsType()->getAs<TemplateSpecializationType>();
if (!NNST)
return false;
const auto *TST = NNST->getAs<TemplateSpecializationType>();
if (!TST) if (!TST)
return false; return false;
if (const TemplateDecl *TD = TST->getTemplateName().getAsTemplateDecl()) { if (const TemplateDecl *TD = TST->getTemplateName().getAsTemplateDecl()) {
@ -250,8 +235,8 @@ void TypeTraitsCheck::check(const MatchFinder::MatchResult &Result) {
auto EmitValueWarning = [this, &Result](const NestedNameSpecifierLoc &QualLoc, auto EmitValueWarning = [this, &Result](const NestedNameSpecifierLoc &QualLoc,
SourceLocation EndLoc) { SourceLocation EndLoc) {
SourceLocation TemplateNameEndLoc; SourceLocation TemplateNameEndLoc;
if (auto TSTL = QualLoc.getTypeLoc().getAs<TemplateSpecializationTypeLoc>(); if (auto TSTL =
!TSTL.isNull()) QualLoc.getAsTypeLoc().getAs<TemplateSpecializationTypeLoc>())
TemplateNameEndLoc = Lexer::getLocForEndOfToken( TemplateNameEndLoc = Lexer::getLocForEndOfToken(
TSTL.getTemplateNameLoc(), 0, *Result.SourceManager, TSTL.getTemplateNameLoc(), 0, *Result.SourceManager,
Result.Context->getLangOpts()); Result.Context->getLangOpts());
@ -274,8 +259,8 @@ void TypeTraitsCheck::check(const MatchFinder::MatchResult &Result) {
SourceLocation EndLoc, SourceLocation EndLoc,
SourceLocation TypenameLoc) { SourceLocation TypenameLoc) {
SourceLocation TemplateNameEndLoc; SourceLocation TemplateNameEndLoc;
if (auto TSTL = QualLoc.getTypeLoc().getAs<TemplateSpecializationTypeLoc>(); if (auto TSTL =
!TSTL.isNull()) QualLoc.getAsTypeLoc().getAs<TemplateSpecializationTypeLoc>())
TemplateNameEndLoc = Lexer::getLocForEndOfToken( TemplateNameEndLoc = Lexer::getLocForEndOfToken(
TSTL.getTemplateNameLoc(), 0, *Result.SourceManager, TSTL.getTemplateNameLoc(), 0, *Result.SourceManager,
Result.Context->getLangOpts()); Result.Context->getLangOpts());
@ -301,23 +286,21 @@ void TypeTraitsCheck::check(const MatchFinder::MatchResult &Result) {
if (!DRE->hasQualifier()) if (!DRE->hasQualifier())
return; return;
if (const auto *CTSD = dyn_cast_if_present<ClassTemplateSpecializationDecl>( if (const auto *CTSD = dyn_cast_if_present<ClassTemplateSpecializationDecl>(
DRE->getQualifier()->getAsRecordDecl())) { DRE->getQualifier().getAsRecordDecl())) {
if (isNamedDeclInStdTraitsSet(CTSD, ValueTraits)) if (isNamedDeclInStdTraitsSet(CTSD, ValueTraits))
EmitValueWarning(DRE->getQualifierLoc(), DRE->getEndLoc()); EmitValueWarning(DRE->getQualifierLoc(), DRE->getEndLoc());
} }
return; return;
} }
if (const auto *ETL = Result.Nodes.getNodeAs<ElaboratedTypeLoc>(Bind)) { if (const auto *TL = Result.Nodes.getNodeAs<TypedefTypeLoc>(Bind)) {
const NestedNameSpecifierLoc QualLoc = ETL->getQualifierLoc(); const NestedNameSpecifierLoc QualLoc = TL->getQualifierLoc();
const auto *NNS = QualLoc.getNestedNameSpecifier(); NestedNameSpecifier NNS = QualLoc.getNestedNameSpecifier();
if (!NNS)
return;
if (const auto *CTSD = dyn_cast_if_present<ClassTemplateSpecializationDecl>( if (const auto *CTSD = dyn_cast_if_present<ClassTemplateSpecializationDecl>(
NNS->getAsRecordDecl())) { NNS.getAsRecordDecl())) {
if (isNamedDeclInStdTraitsSet(CTSD, TypeTraits)) if (isNamedDeclInStdTraitsSet(CTSD, TypeTraits))
EmitTypeWarning(ETL->getQualifierLoc(), ETL->getEndLoc(), EmitTypeWarning(TL->getQualifierLoc(), TL->getEndLoc(),
ETL->getElaboratedKeywordLoc()); TL->getElaboratedKeywordLoc());
} }
return; return;
} }

View File

@ -186,16 +186,14 @@ TypeMatcher nestedIterator() {
/// declarations and which name standard iterators for standard containers. /// declarations and which name standard iterators for standard containers.
TypeMatcher iteratorFromUsingDeclaration() { TypeMatcher iteratorFromUsingDeclaration() {
auto HasIteratorDecl = hasDeclaration(namedDecl(hasStdIteratorName())); auto HasIteratorDecl = hasDeclaration(namedDecl(hasStdIteratorName()));
// Types resulting from using declarations are represented by elaboratedType. // Unwrap the nested name specifier to test for one of the standard
return elaboratedType( // containers.
// Unwrap the nested name specifier to test for one of the standard auto Qualifier = hasQualifier(specifiesType(templateSpecializationType(
// containers. hasDeclaration(namedDecl(hasStdContainerName(), isInStdNamespace())))));
hasQualifier(specifiesType(templateSpecializationType(hasDeclaration( // the named type is what comes after the final '::' in the type. It should
namedDecl(hasStdContainerName(), isInStdNamespace()))))), // name one of the standard iterator names.
// the named type is what comes after the final '::' in the type. It return anyOf(typedefType(HasIteratorDecl, Qualifier),
// should name one of the standard iterator names. recordType(HasIteratorDecl, Qualifier));
namesType(
anyOf(typedefType(HasIteratorDecl), recordType(HasIteratorDecl))));
} }
/// This matcher returns declaration statements that contain variable /// This matcher returns declaration statements that contain variable

View File

@ -60,9 +60,11 @@ matchEnableIfSpecializationImplTypename(TypeLoc TheType) {
Keyword != ElaboratedTypeKeyword::None)) { Keyword != ElaboratedTypeKeyword::None)) {
return std::nullopt; return std::nullopt;
} }
TheType = Dep.getQualifierLoc().getTypeLoc(); TheType = Dep.getQualifierLoc().getAsTypeLoc();
if (TheType.isNull()) if (TheType.isNull())
return std::nullopt; return std::nullopt;
} else {
return std::nullopt;
} }
if (const auto SpecializationLoc = if (const auto SpecializationLoc =
@ -89,9 +91,6 @@ matchEnableIfSpecializationImplTypename(TypeLoc TheType) {
static std::optional<TemplateSpecializationTypeLoc> static std::optional<TemplateSpecializationTypeLoc>
matchEnableIfSpecializationImplTrait(TypeLoc TheType) { matchEnableIfSpecializationImplTrait(TypeLoc TheType) {
if (const auto Elaborated = TheType.getAs<ElaboratedTypeLoc>())
TheType = Elaborated.getNamedTypeLoc();
if (const auto SpecializationLoc = if (const auto SpecializationLoc =
TheType.getAs<TemplateSpecializationTypeLoc>()) { TheType.getAs<TemplateSpecializationTypeLoc>()) {

View File

@ -164,10 +164,10 @@ void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) {
auto CallEmplacy = cxxMemberCallExpr( auto CallEmplacy = cxxMemberCallExpr(
hasDeclaration( hasDeclaration(
functionDecl(hasAnyNameIgnoringTemplates(EmplacyFunctions))), functionDecl(hasAnyNameIgnoringTemplates(EmplacyFunctions))),
on(hasTypeOrPointeeType(hasCanonicalType(hasDeclaration( on(hasTypeOrPointeeType(
has(typedefNameDecl(hasName("value_type"), hasCanonicalType(hasDeclaration(has(typedefNameDecl(
hasType(type(hasUnqualifiedDesugaredType( hasName("value_type"),
recordType().bind("value_type"))))))))))); hasType(hasCanonicalType(recordType().bind("value_type"))))))))));
// We can't replace push_backs of smart pointer because // We can't replace push_backs of smart pointer because
// if emplacement fails (f.e. bad_alloc in vector) we will have leak of // if emplacement fails (f.e. bad_alloc in vector) we will have leak of
@ -241,17 +241,16 @@ void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) {
auto HasConstructExprWithValueTypeType = auto HasConstructExprWithValueTypeType =
has(ignoringImplicit(cxxConstructExpr( has(ignoringImplicit(cxxConstructExpr(
SoughtConstructExpr, hasType(type(hasUnqualifiedDesugaredType( SoughtConstructExpr,
type(equalsBoundNode("value_type")))))))); hasType(hasCanonicalType(type(equalsBoundNode("value_type")))))));
auto HasBracedInitListWithValueTypeType = auto HasBracedInitListWithValueTypeType = anyOf(
anyOf(allOf(HasConstructInitListExpr, allOf(HasConstructInitListExpr,
has(initListExpr(hasType(type(hasUnqualifiedDesugaredType( has(initListExpr(hasType(
type(equalsBoundNode("value_type")))))))), hasCanonicalType(type(equalsBoundNode("value_type"))))))),
has(cxxBindTemporaryExpr( has(cxxBindTemporaryExpr(HasConstructInitListExpr,
HasConstructInitListExpr, has(initListExpr(hasType(hasCanonicalType(
has(initListExpr(hasType(type(hasUnqualifiedDesugaredType( type(equalsBoundNode("value_type")))))))));
type(equalsBoundNode("value_type"))))))))));
auto HasConstructExprWithValueTypeTypeAsLastArgument = hasLastArgument( auto HasConstructExprWithValueTypeTypeAsLastArgument = hasLastArgument(
materializeTemporaryExpr( materializeTemporaryExpr(
@ -289,19 +288,17 @@ void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) {
this); this);
Finder->addMatcher( Finder->addMatcher(
traverse( traverse(TK_AsIs,
TK_AsIs, cxxMemberCallExpr(
cxxMemberCallExpr( CallEmplacy,
CallEmplacy, on(hasType(cxxRecordDecl(has(typedefNameDecl(
on(hasType(cxxRecordDecl(has(typedefNameDecl( hasName("value_type"),
hasName("value_type"), hasType(hasCanonicalType(recordType(hasDeclaration(
hasType(type( cxxRecordDecl(hasAnyName(SmallVector<StringRef, 2>(
hasUnqualifiedDesugaredType(recordType(hasDeclaration( TupleTypes.begin(), TupleTypes.end())))))))))))),
cxxRecordDecl(hasAnyName(SmallVector<StringRef, 2>( has(MakeTuple), hasSameNumArgsAsDeclNumParams(),
TupleTypes.begin(), TupleTypes.end()))))))))))))), unless(isInTemplateInstantiation()))
has(MakeTuple), hasSameNumArgsAsDeclNumParams(), .bind("emplacy_call")),
unless(isInTemplateInstantiation()))
.bind("emplacy_call")),
this); this);
} }

View File

@ -29,7 +29,7 @@ static bool isLockGuardDecl(const NamedDecl *Decl) {
static bool isLockGuard(const QualType &Type) { static bool isLockGuard(const QualType &Type) {
if (const auto *Record = Type->getAs<RecordType>()) if (const auto *Record = Type->getAs<RecordType>())
if (const RecordDecl *Decl = Record->getDecl()) if (const RecordDecl *Decl = Record->getOriginalDecl())
return isLockGuardDecl(Decl); return isLockGuardDecl(Decl);
if (const auto *TemplateSpecType = Type->getAs<TemplateSpecializationType>()) if (const auto *TemplateSpecType = Type->getAs<TemplateSpecializationType>())
@ -89,17 +89,6 @@ findLocksInCompoundStmt(const CompoundStmt *Block,
return LockGuardGroups; return LockGuardGroups;
} }
static TemplateSpecializationTypeLoc
getTemplateLockGuardTypeLoc(const TypeSourceInfo *SourceInfo) {
const TypeLoc Loc = SourceInfo->getTypeLoc();
const auto ElaboratedLoc = Loc.getAs<ElaboratedTypeLoc>();
if (!ElaboratedLoc)
return {};
return ElaboratedLoc.getNamedTypeLoc().getAs<TemplateSpecializationTypeLoc>();
}
// Find the exact source range of the 'lock_guard' token // Find the exact source range of the 'lock_guard' token
static SourceRange getLockGuardRange(const TypeSourceInfo *SourceInfo) { static SourceRange getLockGuardRange(const TypeSourceInfo *SourceInfo) {
const TypeLoc LockGuardTypeLoc = SourceInfo->getTypeLoc(); const TypeLoc LockGuardTypeLoc = SourceInfo->getTypeLoc();
@ -110,7 +99,7 @@ static SourceRange getLockGuardRange(const TypeSourceInfo *SourceInfo) {
// Find the exact source range of the 'lock_guard' name token // Find the exact source range of the 'lock_guard' name token
static SourceRange getLockGuardNameRange(const TypeSourceInfo *SourceInfo) { static SourceRange getLockGuardNameRange(const TypeSourceInfo *SourceInfo) {
const TemplateSpecializationTypeLoc TemplateLoc = const TemplateSpecializationTypeLoc TemplateLoc =
getTemplateLockGuardTypeLoc(SourceInfo); SourceInfo->getTypeLoc().getAs<TemplateSpecializationTypeLoc>();
if (!TemplateLoc) if (!TemplateLoc)
return {}; return {};
@ -136,11 +125,11 @@ void UseScopedLockCheck::registerMatchers(MatchFinder *Finder) {
const auto LockGuardClassDecl = const auto LockGuardClassDecl =
namedDecl(hasName("lock_guard"), isInStdNamespace()); namedDecl(hasName("lock_guard"), isInStdNamespace());
const auto LockGuardType = qualType(anyOf( const auto LockGuardType =
hasUnqualifiedDesugaredType( qualType(anyOf(hasUnqualifiedDesugaredType(
recordType(hasDeclaration(LockGuardClassDecl))), recordType(hasDeclaration(LockGuardClassDecl))),
elaboratedType(namesType(hasUnqualifiedDesugaredType( hasUnqualifiedDesugaredType(templateSpecializationType(
templateSpecializationType(hasDeclaration(LockGuardClassDecl))))))); hasDeclaration(LockGuardClassDecl)))));
const auto LockVarDecl = varDecl(hasType(LockGuardType)); const auto LockVarDecl = varDecl(hasType(LockGuardType));
@ -165,18 +154,16 @@ void UseScopedLockCheck::registerMatchers(MatchFinder *Finder) {
if (WarnOnUsingAndTypedef) { if (WarnOnUsingAndTypedef) {
// Match 'typedef std::lock_guard<std::mutex> Lock' // Match 'typedef std::lock_guard<std::mutex> Lock'
Finder->addMatcher(typedefDecl(unless(isExpansionInSystemHeader()), Finder->addMatcher(typedefDecl(unless(isExpansionInSystemHeader()),
hasUnderlyingType(LockGuardType)) hasType(hasUnderlyingType(LockGuardType)))
.bind("lock-guard-typedef"), .bind("lock-guard-typedef"),
this); this);
// Match 'using Lock = std::lock_guard<std::mutex>' // Match 'using Lock = std::lock_guard<std::mutex>'
Finder->addMatcher( Finder->addMatcher(typeAliasDecl(unless(isExpansionInSystemHeader()),
typeAliasDecl( hasType(templateSpecializationType(
unless(isExpansionInSystemHeader()), hasDeclaration(LockGuardClassDecl))))
hasType(elaboratedType(namesType(templateSpecializationType( .bind("lock-guard-using-alias"),
hasDeclaration(LockGuardClassDecl)))))) this);
.bind("lock-guard-using-alias"),
this);
// Match 'using std::lock_guard' // Match 'using std::lock_guard'
Finder->addMatcher( Finder->addMatcher(
@ -288,8 +275,8 @@ void UseScopedLockCheck::diagOnSourceInfo(
const ast_matchers::MatchFinder::MatchResult &Result) { const ast_matchers::MatchFinder::MatchResult &Result) {
const TypeLoc TL = LockGuardSourceInfo->getTypeLoc(); const TypeLoc TL = LockGuardSourceInfo->getTypeLoc();
if (const auto ElaboratedTL = TL.getAs<ElaboratedTypeLoc>()) { if (const auto TTL = TL.getAs<TemplateSpecializationTypeLoc>()) {
auto Diag = diag(ElaboratedTL.getBeginLoc(), UseScopedLockMessage); auto Diag = diag(TTL.getBeginLoc(), UseScopedLockMessage);
const SourceRange LockGuardRange = const SourceRange LockGuardRange =
getLockGuardNameRange(LockGuardSourceInfo); getLockGuardNameRange(LockGuardSourceInfo);

View File

@ -64,66 +64,65 @@ public:
return false; return false;
} }
bool TraverseTypeLoc(TypeLoc TL, bool Elaborated = false) { bool TraverseTypeLoc(TypeLoc TL, bool TraverseQualifier = true) {
if (TL.isNull()) if (TL.isNull())
return true; return true;
if (!Elaborated) { switch (TL.getTypeLocClass()) {
switch (TL.getTypeLocClass()) { case TypeLoc::InjectedClassName:
case TypeLoc::Record: case TypeLoc::Record:
if (visitUnqualName( case TypeLoc::Enum: {
TL.getAs<RecordTypeLoc>().getTypePtr()->getDecl()->getName())) auto TTL = TL.getAs<TagTypeLoc>();
return false; const auto *T = TTL.getTypePtr();
if (T->getKeyword() != ElaboratedTypeKeyword::None ||
TTL.getQualifierLoc())
break; break;
case TypeLoc::Enum: if (visitUnqualName(T->getOriginalDecl()->getName()))
if (visitUnqualName( return false;
TL.getAs<EnumTypeLoc>().getTypePtr()->getDecl()->getName())) break;
return false; }
case TypeLoc::TemplateSpecialization: {
auto TTL = TL.getAs<TemplateSpecializationTypeLoc>();
const auto *T = TTL.getTypePtr();
if (T->getKeyword() != ElaboratedTypeKeyword::None ||
TTL.getQualifierLoc())
break; break;
case TypeLoc::TemplateSpecialization: if (visitUnqualName(T->getTemplateName().getAsTemplateDecl()->getName()))
if (visitUnqualName(TL.getAs<TemplateSpecializationTypeLoc>() return false;
.getTypePtr() break;
->getTemplateName() }
.getAsTemplateDecl() case TypeLoc::Typedef: {
->getName())) auto TTL = TL.getAs<TypedefTypeLoc>();
return false; const auto *T = TTL.getTypePtr();
if (T->getKeyword() != ElaboratedTypeKeyword::None ||
TTL.getQualifierLoc())
break; break;
case TypeLoc::Typedef: if (visitUnqualName(T->getDecl()->getName()))
if (visitUnqualName( return false;
TL.getAs<TypedefTypeLoc>().getTypePtr()->getDecl()->getName())) break;
return false; }
case TypeLoc::Using: {
auto TTL = TL.getAs<UsingTypeLoc>();
const auto *T = TTL.getTypePtr();
if (T->getKeyword() != ElaboratedTypeKeyword::None ||
TTL.getQualifierLoc())
break; break;
case TypeLoc::Using: if (visitUnqualName(T->getDecl()->getName()))
if (visitUnqualName(TL.getAs<UsingTypeLoc>() return false;
.getTypePtr() break;
->getFoundDecl() }
->getName())) default:
return false; break;
break;
default:
break;
}
} }
return RecursiveASTVisitor<UnqualNameVisitor>::TraverseTypeLoc(TL); return RecursiveASTVisitor<UnqualNameVisitor>::TraverseTypeLoc(
TL, TraverseQualifier);
} }
// Replace the base method in order to call our own // Replace the base method in order to call our own
// TraverseTypeLoc(). // TraverseTypeLoc().
bool TraverseQualifiedTypeLoc(QualifiedTypeLoc TL) { bool TraverseQualifiedTypeLoc(QualifiedTypeLoc TL, bool TraverseQualifier) {
return TraverseTypeLoc(TL.getUnqualifiedLoc()); return TraverseTypeLoc(TL.getUnqualifiedLoc(), TraverseQualifier);
}
// Replace the base version to inform TraverseTypeLoc that the type is
// elaborated.
bool TraverseElaboratedTypeLoc(ElaboratedTypeLoc TL) {
if (TL.getQualifierLoc() &&
!TraverseNestedNameSpecifierLoc(TL.getQualifierLoc()))
return false;
const auto *T = TL.getTypePtr();
return TraverseTypeLoc(TL.getNamedTypeLoc(),
T->getKeyword() != ElaboratedTypeKeyword::None ||
T->getQualifier());
} }
bool VisitDeclRefExpr(DeclRefExpr *S) { bool VisitDeclRefExpr(DeclRefExpr *S) {

View File

@ -37,15 +37,13 @@ void UseTransparentFunctorsCheck::registerMatchers(MatchFinder *Finder) {
// Non-transparent functor mentioned as a template parameter. FIXIT. // Non-transparent functor mentioned as a template parameter. FIXIT.
Finder->addMatcher( Finder->addMatcher(
loc(qualType( loc(qualType(hasDeclaration(classTemplateSpecializationDecl(
unless(elaboratedType()), unless(hasAnyTemplateArgument(templateArgument(refersToType(
hasDeclaration(classTemplateSpecializationDecl( qualType(pointsTo(qualType(isAnyCharacter()))))))),
unless(hasAnyTemplateArgument(templateArgument(refersToType( hasAnyTemplateArgument(
qualType(pointsTo(qualType(isAnyCharacter()))))))), templateArgument(refersToType(qualType(
hasAnyTemplateArgument( hasDeclaration(TransparentFunctors))))
templateArgument(refersToType(qualType(hasDeclaration( .bind("Functor"))))))
TransparentFunctors))))
.bind("Functor"))))))
.bind("FunctorParentLoc"), .bind("FunctorParentLoc"),
this); this);

View File

@ -42,11 +42,11 @@ void NoAutomaticMoveCheck::registerMatchers(MatchFinder *Finder) {
// A matcher for a `DstT::DstT(const Src&)` where DstT also has a // A matcher for a `DstT::DstT(const Src&)` where DstT also has a
// `DstT::DstT(Src&&)`. // `DstT::DstT(Src&&)`.
const auto LValueRefCtor = cxxConstructorDecl( const auto LValueRefCtor = cxxConstructorDecl(
hasParameter(0, hasParameter(0, hasType(hasCanonicalType(
hasType(lValueReferenceType(pointee(type().bind("SrcT"))))), lValueReferenceType(pointee(type().bind("SrcT")))))),
ofClass(cxxRecordDecl(hasMethod(cxxConstructorDecl( ofClass(cxxRecordDecl(hasMethod(cxxConstructorDecl(
hasParameter(0, hasType(rValueReferenceType( hasParameter(0, hasType(hasCanonicalType(rValueReferenceType(
pointee(type(equalsBoundNode("SrcT"))))))))))); pointee(type(equalsBoundNode("SrcT"))))))))))));
// A matcher for `DstT::DstT(const Src&&)`, which typically comes from an // A matcher for `DstT::DstT(const Src&&)`, which typically comes from an
// instantiation of `template <typename U> DstT::DstT(U&&)`. // instantiation of `template <typename U> DstT::DstT(U&&)`.

View File

@ -15,10 +15,11 @@ namespace clang::tidy::portability {
void StdAllocatorConstCheck::registerMatchers(MatchFinder *Finder) { void StdAllocatorConstCheck::registerMatchers(MatchFinder *Finder) {
// Match std::allocator<const T>. // Match std::allocator<const T>.
auto AllocatorConst = auto AllocatorConst = qualType(hasCanonicalType(
recordType(hasDeclaration(classTemplateSpecializationDecl( recordType(hasDeclaration(classTemplateSpecializationDecl(
hasName("::std::allocator"), hasName("::std::allocator"),
hasTemplateArgument(0, refersToType(qualType(isConstQualified())))))); hasTemplateArgument(0,
refersToType(qualType(isConstQualified()))))))));
auto HasContainerName = auto HasContainerName =
hasAnyName("::std::vector", "::std::deque", "::std::list", hasAnyName("::std::vector", "::std::deque", "::std::list",
@ -31,8 +32,10 @@ void StdAllocatorConstCheck::registerMatchers(MatchFinder *Finder) {
// aren't caught. // aren't caught.
Finder->addMatcher( Finder->addMatcher(
typeLoc( typeLoc(
templateSpecializationTypeLoc(), anyOf(templateSpecializationTypeLoc(),
loc(hasUnqualifiedDesugaredType(anyOf( qualifiedTypeLoc(
hasUnqualifiedLoc(templateSpecializationTypeLoc()))),
loc(qualType(anyOf(
recordType(hasDeclaration(classTemplateSpecializationDecl( recordType(hasDeclaration(classTemplateSpecializationDecl(
HasContainerName, HasContainerName,
anyOf( anyOf(

View File

@ -19,19 +19,25 @@ namespace {
AST_MATCHER(CXXMethodDecl, isStatic) { return Node.isStatic(); } AST_MATCHER(CXXMethodDecl, isStatic) { return Node.isStatic(); }
} // namespace } // namespace
static unsigned getNameSpecifierNestingLevel(const QualType &QType) { static unsigned getNameSpecifierNestingLevel(QualType QType) {
if (const auto *ElType = QType->getAs<ElaboratedType>()) { unsigned NameSpecifierNestingLevel = 1;
if (const NestedNameSpecifier *NestedSpecifiers = ElType->getQualifier()) { for (NestedNameSpecifier Qualifier = QType->getPrefix(); /**/;
unsigned NameSpecifierNestingLevel = 1; ++NameSpecifierNestingLevel) {
do { switch (Qualifier.getKind()) {
NameSpecifierNestingLevel++; case NestedNameSpecifier::Kind::Null:
NestedSpecifiers = NestedSpecifiers->getPrefix();
} while (NestedSpecifiers);
return NameSpecifierNestingLevel; return NameSpecifierNestingLevel;
case NestedNameSpecifier::Kind::Global:
case NestedNameSpecifier::Kind::MicrosoftSuper:
return NameSpecifierNestingLevel + 1;
case NestedNameSpecifier::Kind::Namespace:
Qualifier = Qualifier.getAsNamespaceAndPrefix().Prefix;
continue;
case NestedNameSpecifier::Kind::Type:
Qualifier = Qualifier.getAsType()->getPrefix();
continue;
} }
llvm_unreachable("unhandled nested name specifier kind");
} }
return 0;
} }
void StaticAccessedThroughInstanceCheck::storeOptions( void StaticAccessedThroughInstanceCheck::storeOptions(

View File

@ -414,9 +414,9 @@ static bool areTypesCompatible(QualType ArgType, QualType ParamType,
// Arithmetic types are interconvertible, except scoped enums. // Arithmetic types are interconvertible, except scoped enums.
if (ParamType->isArithmeticType() && ArgType->isArithmeticType()) { if (ParamType->isArithmeticType() && ArgType->isArithmeticType()) {
if ((ParamType->isEnumeralType() && if ((ParamType->isEnumeralType() &&
ParamType->castAs<EnumType>()->getDecl()->isScoped()) || ParamType->castAs<EnumType>()->getOriginalDecl()->isScoped()) ||
(ArgType->isEnumeralType() && (ArgType->isEnumeralType() &&
ArgType->castAs<EnumType>()->getDecl()->isScoped())) ArgType->castAs<EnumType>()->getOriginalDecl()->isScoped()))
return false; return false;
return true; return true;

View File

@ -67,11 +67,7 @@ static QualType getNonTemplateAlias(QualType QT) {
if (!TT->getDecl()->getDescribedTemplate() && if (!TT->getDecl()->getDescribedTemplate() &&
!TT->getDecl()->getDeclContext()->isDependentContext()) !TT->getDecl()->getDeclContext()->isDependentContext())
return QT; return QT;
QT = TT->getDecl()->getUnderlyingType(); QT = TT->desugar();
}
// cast to elaborated type
else if (const ElaboratedType *ET = dyn_cast<ElaboratedType>(QT)) {
QT = ET->getNamedType();
} else { } else {
break; break;
} }

View File

@ -66,7 +66,8 @@ ExceptionSpecAnalyzer::analyzeBase(const CXXBaseSpecifier &Base,
if (!RecType) if (!RecType)
return State::Unknown; return State::Unknown;
const auto *BaseClass = cast<CXXRecordDecl>(RecType->getDecl()); const auto *BaseClass =
cast<CXXRecordDecl>(RecType->getOriginalDecl())->getDefinitionOrSelf();
return analyzeRecord(BaseClass, Kind); return analyzeRecord(BaseClass, Kind);
} }

View File

@ -461,8 +461,9 @@ bool FormatStringConverter::emitIntegerArgument(
// the signedness based on the format string, so we need to do the // the signedness based on the format string, so we need to do the
// same. // same.
if (const auto *ET = ArgType->getAs<EnumType>()) { if (const auto *ET = ArgType->getAs<EnumType>()) {
if (const std::optional<std::string> MaybeCastType = if (const std::optional<std::string> MaybeCastType = castTypeForArgument(
castTypeForArgument(ArgKind, ET->getDecl()->getIntegerType())) ArgKind,
ET->getOriginalDecl()->getDefinitionOrSelf()->getIntegerType()))
ArgFixes.emplace_back( ArgFixes.emplace_back(
ArgIndex, (Twine("static_cast<") + *MaybeCastType + ">(").str()); ArgIndex, (Twine("static_cast<") + *MaybeCastType + ">(").str());
else else

View File

@ -34,7 +34,7 @@ bool MatchesAnyListedTypeNameMatcher::matches(
PrintingPolicy PrintingPolicyWithSuppressedTag( PrintingPolicy PrintingPolicyWithSuppressedTag(
Finder->getASTContext().getLangOpts()); Finder->getASTContext().getLangOpts());
PrintingPolicyWithSuppressedTag.PrintAsCanonical = CanonicalTypes; PrintingPolicyWithSuppressedTag.PrintAsCanonical = CanonicalTypes;
PrintingPolicyWithSuppressedTag.SuppressElaboration = true; PrintingPolicyWithSuppressedTag.FullyQualifiedName = true;
PrintingPolicyWithSuppressedTag.SuppressScope = false; PrintingPolicyWithSuppressedTag.SuppressScope = false;
PrintingPolicyWithSuppressedTag.SuppressTagKeyword = true; PrintingPolicyWithSuppressedTag.SuppressTagKeyword = true;
PrintingPolicyWithSuppressedTag.SuppressUnwrittenScope = true; PrintingPolicyWithSuppressedTag.SuppressUnwrittenScope = true;

View File

@ -281,9 +281,10 @@ public:
} }
bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc Loc) { bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc Loc) {
if (const NestedNameSpecifier *Spec = Loc.getNestedNameSpecifier()) { if (NestedNameSpecifier Spec = Loc.getNestedNameSpecifier();
Spec.getKind() == NestedNameSpecifier::Kind::Namespace) {
if (const auto *Decl = if (const auto *Decl =
dyn_cast_if_present<NamespaceDecl>(Spec->getAsNamespace())) dyn_cast<NamespaceDecl>(Spec.getAsNamespaceAndPrefix().Namespace))
Check->addUsage(Decl, Loc.getLocalSourceRange(), SM); Check->addUsage(Decl, Loc.getLocalSourceRange(), SM);
} }
@ -323,48 +324,34 @@ public:
} }
bool VisitTypedefTypeLoc(const TypedefTypeLoc &Loc) { bool VisitTypedefTypeLoc(const TypedefTypeLoc &Loc) {
Check->addUsage(Loc.getTypedefNameDecl(), Loc.getSourceRange(), SM); Check->addUsage(Loc.getDecl(), Loc.getNameLoc(), SM);
return true; return true;
} }
bool VisitTagTypeLoc(const TagTypeLoc &Loc) { bool VisitTagTypeLoc(const TagTypeLoc &Loc) {
Check->addUsage(Loc.getDecl(), Loc.getSourceRange(), SM); Check->addUsage(Loc.getOriginalDecl(), Loc.getNameLoc(), SM);
return true;
}
bool VisitInjectedClassNameTypeLoc(const InjectedClassNameTypeLoc &Loc) {
Check->addUsage(Loc.getDecl(), Loc.getSourceRange(), SM);
return true; return true;
} }
bool VisitUnresolvedUsingTypeLoc(const UnresolvedUsingTypeLoc &Loc) { bool VisitUnresolvedUsingTypeLoc(const UnresolvedUsingTypeLoc &Loc) {
Check->addUsage(Loc.getDecl(), Loc.getSourceRange(), SM); Check->addUsage(Loc.getDecl(), Loc.getNameLoc(), SM);
return true; return true;
} }
bool VisitTemplateTypeParmTypeLoc(const TemplateTypeParmTypeLoc &Loc) { bool VisitTemplateTypeParmTypeLoc(const TemplateTypeParmTypeLoc &Loc) {
Check->addUsage(Loc.getDecl(), Loc.getSourceRange(), SM); Check->addUsage(Loc.getDecl(), Loc.getNameLoc(), SM);
return true; return true;
} }
bool bool
VisitTemplateSpecializationTypeLoc(const TemplateSpecializationTypeLoc &Loc) { VisitTemplateSpecializationTypeLoc(const TemplateSpecializationTypeLoc &Loc) {
const TemplateDecl *Decl = const TemplateDecl *Decl =
Loc.getTypePtr()->getTemplateName().getAsTemplateDecl(); Loc.getTypePtr()->getTemplateName().getAsTemplateDecl(
/*IgnoreDeduced=*/true);
SourceRange Range(Loc.getTemplateNameLoc(), Loc.getTemplateNameLoc()); if (const auto *ClassDecl = dyn_cast<TemplateDecl>(Decl))
if (const auto *ClassDecl = dyn_cast<TemplateDecl>(Decl)) {
if (const NamedDecl *TemplDecl = ClassDecl->getTemplatedDecl()) if (const NamedDecl *TemplDecl = ClassDecl->getTemplatedDecl())
Check->addUsage(TemplDecl, Range, SM); Check->addUsage(TemplDecl, Loc.getTemplateNameLoc(), SM);
}
return true;
}
bool VisitDependentTemplateSpecializationTypeLoc(
const DependentTemplateSpecializationTypeLoc &Loc) {
if (const TagDecl *Decl = Loc.getTypePtr()->getAsTagDecl())
Check->addUsage(Decl, Loc.getSourceRange(), SM);
return true; return true;
} }

View File

@ -124,7 +124,8 @@ bool isTriviallyDefaultConstructible(QualType Type, const ASTContext &Context) {
return true; return true;
if (const auto *RT = CanonicalType->getAs<RecordType>()) { if (const auto *RT = CanonicalType->getAs<RecordType>()) {
return recordIsTriviallyDefaultConstructible(*RT->getDecl(), Context); return recordIsTriviallyDefaultConstructible(
*RT->getOriginalDecl()->getDefinitionOrSelf(), Context);
} }
// No other types can match. // No other types can match.

View File

@ -102,54 +102,78 @@ getUsingNamespaceDirectives(const DeclContext *DestContext,
// ancestor is redundant, therefore we stop at lowest common ancestor. // ancestor is redundant, therefore we stop at lowest common ancestor.
// In addition to that stops early whenever IsVisible returns true. This can be // In addition to that stops early whenever IsVisible returns true. This can be
// used to implement support for "using namespace" decls. // used to implement support for "using namespace" decls.
std::string std::string getQualification(ASTContext &Context,
getQualification(ASTContext &Context, const DeclContext *DestContext, const DeclContext *DestContext,
const DeclContext *SourceContext, const DeclContext *SourceContext,
llvm::function_ref<bool(NestedNameSpecifier *)> IsVisible) { llvm::function_ref<bool(const Decl *)> IsVisible) {
std::vector<const NestedNameSpecifier *> Parents; std::vector<const Decl *> Parents;
bool ReachedNS = false; [[maybe_unused]] bool ReachedNS = false;
for (const DeclContext *CurContext = SourceContext; CurContext; for (const DeclContext *CurContext = SourceContext; CurContext;
CurContext = CurContext->getLookupParent()) { CurContext = CurContext->getLookupParent()) {
// Stop once we reach a common ancestor. // Stop once we reach a common ancestor.
if (CurContext->Encloses(DestContext)) if (CurContext->Encloses(DestContext))
break; break;
NestedNameSpecifier *NNS = nullptr; const Decl *CurD;
if (auto *TD = llvm::dyn_cast<TagDecl>(CurContext)) { if (auto *TD = llvm::dyn_cast<TagDecl>(CurContext)) {
// There can't be any more tag parents after hitting a namespace. // There can't be any more tag parents after hitting a namespace.
assert(!ReachedNS); assert(!ReachedNS);
(void)ReachedNS; CurD = TD;
NNS = NestedNameSpecifier::Create(Context, nullptr, TD->getTypeForDecl());
} else if (auto *NSD = llvm::dyn_cast<NamespaceDecl>(CurContext)) { } else if (auto *NSD = llvm::dyn_cast<NamespaceDecl>(CurContext)) {
ReachedNS = true; ReachedNS = true;
NNS = NestedNameSpecifier::Create(Context, nullptr, NSD);
// Anonymous and inline namespace names are not spelled while qualifying // Anonymous and inline namespace names are not spelled while qualifying
// a name, so skip those. // a name, so skip those.
if (NSD->isAnonymousNamespace() || NSD->isInlineNamespace()) if (NSD->isAnonymousNamespace() || NSD->isInlineNamespace())
continue; continue;
CurD = NSD;
} else { } else {
// Other types of contexts cannot be spelled in code, just skip over // Other types of contexts cannot be spelled in code, just skip over
// them. // them.
continue; continue;
} }
// Stop if this namespace is already visible at DestContext. // Stop if this namespace is already visible at DestContext.
if (IsVisible(NNS)) if (IsVisible(CurD))
break; break;
Parents.push_back(NNS); Parents.push_back(CurD);
} }
// Go over name-specifiers in reverse order to create necessary qualification, // Go over the declarations in reverse order, since we stored inner-most
// since we stored inner-most parent first. // parent first.
NestedNameSpecifier Qualifier = std::nullopt;
bool IsFirst = true;
for (const auto *CurD : llvm::reverse(Parents)) {
if (auto *TD = llvm::dyn_cast<TagDecl>(CurD)) {
QualType T;
if (const auto *RD = dyn_cast<CXXRecordDecl>(TD);
ClassTemplateDecl *CTD = RD->getDescribedClassTemplate()) {
ArrayRef<TemplateArgument> Args;
if (const auto *SD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
Args = SD->getTemplateArgs().asArray();
else
Args = CTD->getTemplateParameters()->getInjectedTemplateArgs(Context);
T = Context.getTemplateSpecializationType(
ElaboratedTypeKeyword::None,
Context.getQualifiedTemplateName(
Qualifier, /*TemplateKeyword=*/!IsFirst, TemplateName(CTD)),
Args, /*CanonicalArgs=*/{}, Context.getCanonicalTagType(RD));
} else {
T = Context.getTagType(ElaboratedTypeKeyword::None, Qualifier, TD,
/*OwnsTag=*/false);
}
Qualifier = NestedNameSpecifier(T.getTypePtr());
} else {
Qualifier =
NestedNameSpecifier(Context, cast<NamespaceDecl>(CurD), Qualifier);
}
IsFirst = false;
}
if (!Qualifier)
return "";
std::string Result; std::string Result;
llvm::raw_string_ostream OS(Result); llvm::raw_string_ostream OS(Result);
for (const auto *Parent : llvm::reverse(Parents)) { Qualifier.print(OS, Context.getPrintingPolicy());
if (Parent != *Parents.rbegin() && Parent->isDependent() &&
Parent->getAsRecordDecl() &&
Parent->getAsRecordDecl()->getDescribedClassTemplate())
OS << "template ";
Parent->print(OS, Context.getPrintingPolicy());
}
return OS.str(); return OS.str();
} }
@ -187,6 +211,7 @@ std::string printQualifiedName(const NamedDecl &ND) {
// include them, but at query time it's hard to find all the inline // include them, but at query time it's hard to find all the inline
// namespaces to query: the preamble doesn't have a dedicated list. // namespaces to query: the preamble doesn't have a dedicated list.
Policy.SuppressUnwrittenScope = true; Policy.SuppressUnwrittenScope = true;
Policy.SuppressScope = true;
// (unnamed struct), not (unnamed struct at /path/to/foo.cc:42:1). // (unnamed struct), not (unnamed struct at /path/to/foo.cc:42:1).
// In clangd, context is usually available and paths are mostly noise. // In clangd, context is usually available and paths are mostly noise.
Policy.AnonymousTagLocations = false; Policy.AnonymousTagLocations = false;
@ -213,8 +238,7 @@ std::string printUsingNamespaceName(const ASTContext &Ctx,
std::string Name; std::string Name;
llvm::raw_string_ostream Out(Name); llvm::raw_string_ostream Out(Name);
if (auto *Qual = D.getQualifier()) D.getQualifier().print(Out, PP);
Qual->print(Out, PP);
D.getNominatedNamespaceAsWritten()->printName(Out); D.getNominatedNamespaceAsWritten()->printName(Out);
return Out.str(); return Out.str();
} }
@ -229,8 +253,7 @@ std::string printName(const ASTContext &Ctx, const NamedDecl &ND) {
// Handle 'using namespace'. They all have the same name - <using-directive>. // Handle 'using namespace'. They all have the same name - <using-directive>.
if (auto *UD = llvm::dyn_cast<UsingDirectiveDecl>(&ND)) { if (auto *UD = llvm::dyn_cast<UsingDirectiveDecl>(&ND)) {
Out << "using namespace "; Out << "using namespace ";
if (auto *Qual = UD->getQualifier()) UD->getQualifier().print(Out, PP);
Qual->print(Out, PP);
UD->getNominatedNamespaceAsWritten()->printName(Out); UD->getNominatedNamespaceAsWritten()->printName(Out);
return Out.str(); return Out.str();
} }
@ -250,8 +273,7 @@ std::string printName(const ASTContext &Ctx, const NamedDecl &ND) {
} }
// Print nested name qualifier if it was written in the source code. // Print nested name qualifier if it was written in the source code.
if (auto *Qualifier = getQualifierLoc(ND).getNestedNameSpecifier()) getQualifierLoc(ND).getNestedNameSpecifier().print(Out, PP);
Qualifier->print(Out, PP);
// Print the name itself. // Print the name itself.
ND.getDeclName().print(Out, PP); ND.getDeclName().print(Out, PP);
// Print template arguments. // Print template arguments.
@ -391,12 +413,13 @@ preferredIncludeDirective(llvm::StringRef FileName, const LangOptions &LangOpts,
} }
std::string printType(const QualType QT, const DeclContext &CurContext, std::string printType(const QualType QT, const DeclContext &CurContext,
const llvm::StringRef Placeholder) { const llvm::StringRef Placeholder, bool FullyQualify) {
std::string Result; std::string Result;
llvm::raw_string_ostream OS(Result); llvm::raw_string_ostream OS(Result);
PrintingPolicy PP(CurContext.getParentASTContext().getPrintingPolicy()); PrintingPolicy PP(CurContext.getParentASTContext().getPrintingPolicy());
PP.SuppressTagKeyword = true; PP.SuppressTagKeyword = true;
PP.SuppressUnwrittenScope = true; PP.SuppressUnwrittenScope = true;
PP.FullyQualifiedName = FullyQualify;
class PrintCB : public PrintingCallbacks { class PrintCB : public PrintingCallbacks {
public: public:
@ -439,6 +462,7 @@ QualType declaredType(const TypeDecl *D) {
if (const auto *CTSD = llvm::dyn_cast<ClassTemplateSpecializationDecl>(D)) if (const auto *CTSD = llvm::dyn_cast<ClassTemplateSpecializationDecl>(D))
if (const auto *Args = CTSD->getTemplateArgsAsWritten()) if (const auto *Args = CTSD->getTemplateArgsAsWritten())
return Context.getTemplateSpecializationType( return Context.getTemplateSpecializationType(
ElaboratedTypeKeyword::None,
TemplateName(CTSD->getSpecializedTemplate()), Args->arguments(), TemplateName(CTSD->getSpecializedTemplate()), Args->arguments(),
/*CanonicalArgs=*/{}); /*CanonicalArgs=*/{});
return Context.getTypeDeclType(D); return Context.getTypeDeclType(D);
@ -664,13 +688,10 @@ std::string getQualification(ASTContext &Context,
auto VisibleNamespaceDecls = auto VisibleNamespaceDecls =
getUsingNamespaceDirectives(DestContext, InsertionPoint); getUsingNamespaceDirectives(DestContext, InsertionPoint);
return getQualification( return getQualification(
Context, DestContext, ND->getDeclContext(), Context, DestContext, ND->getDeclContext(), [&](const Decl *D) {
[&](NestedNameSpecifier *NNS) { if (D->getKind() != Decl::Namespace)
const NamespaceDecl *NS =
dyn_cast_if_present<NamespaceDecl>(NNS->getAsNamespace());
if (!NS)
return false; return false;
NS = NS->getCanonicalDecl(); const auto *NS = cast<NamespaceDecl>(D)->getCanonicalDecl();
return llvm::any_of(VisibleNamespaceDecls, return llvm::any_of(VisibleNamespaceDecls,
[NS](const NamespaceDecl *NSD) { [NS](const NamespaceDecl *NSD) {
return NSD->getCanonicalDecl() == NS; return NSD->getCanonicalDecl() == NS;
@ -687,12 +708,11 @@ std::string getQualification(ASTContext &Context,
(void)NS; (void)NS;
} }
return getQualification( return getQualification(
Context, DestContext, ND->getDeclContext(), Context, DestContext, ND->getDeclContext(), [&](const Decl *D) {
[&](NestedNameSpecifier *NNS) {
return llvm::any_of(VisibleNamespaces, [&](llvm::StringRef Namespace) { return llvm::any_of(VisibleNamespaces, [&](llvm::StringRef Namespace) {
std::string NS; std::string NS;
llvm::raw_string_ostream OS(NS); llvm::raw_string_ostream OS(NS);
NNS->print(OS, Context.getPrintingPolicy()); D->print(OS, Context.getPrintingPolicy());
return OS.str() == Namespace; return OS.str() == Namespace;
}); });
}); });

View File

@ -135,7 +135,8 @@ preferredIncludeDirective(llvm::StringRef FileName, const LangOptions &LangOpts,
/// Returns a QualType as string. The result doesn't contain unwritten scopes /// Returns a QualType as string. The result doesn't contain unwritten scopes
/// like anonymous/inline namespace. /// like anonymous/inline namespace.
std::string printType(const QualType QT, const DeclContext &CurContext, std::string printType(const QualType QT, const DeclContext &CurContext,
llvm::StringRef Placeholder = ""); llvm::StringRef Placeholder = "",
bool FullyQualify = false);
/// Indicates if \p D is a template instantiation implicitly generated by the /// Indicates if \p D is a template instantiation implicitly generated by the
/// compiler, e.g. /// compiler, e.g.

View File

@ -1466,19 +1466,15 @@ bool allowIndex(CodeCompletionContext &CC) {
auto Scope = CC.getCXXScopeSpecifier(); auto Scope = CC.getCXXScopeSpecifier();
if (!Scope) if (!Scope)
return true; return true;
NestedNameSpecifier *NameSpec = (*Scope)->getScopeRep();
if (!NameSpec)
return true;
// We only query the index when qualifier is a namespace. // We only query the index when qualifier is a namespace.
// If it's a class, we rely solely on sema completions. // If it's a class, we rely solely on sema completions.
switch (NameSpec->getKind()) { switch ((*Scope)->getScopeRep().getKind()) {
case NestedNameSpecifier::Global: case NestedNameSpecifier::Kind::Null:
case NestedNameSpecifier::Namespace: case NestedNameSpecifier::Kind::Global:
case NestedNameSpecifier::Kind::Namespace:
return true; return true;
case NestedNameSpecifier::Super: case NestedNameSpecifier::Kind::MicrosoftSuper:
case NestedNameSpecifier::TypeSpec: case NestedNameSpecifier::Kind::Type:
// Unresolved inside a template.
case NestedNameSpecifier::Identifier:
return false; return false;
} }
llvm_unreachable("invalid NestedNameSpecifier kind"); llvm_unreachable("invalid NestedNameSpecifier kind");

View File

@ -147,17 +147,17 @@ class DumpVisitor : public RecursiveASTVisitor<DumpVisitor> {
} }
llvm_unreachable("Unhandled ArgKind enum"); llvm_unreachable("Unhandled ArgKind enum");
} }
std::string getKind(const NestedNameSpecifierLoc &NNSL) { std::string getKind(NestedNameSpecifierLoc NNSL) {
assert(NNSL.getNestedNameSpecifier()); switch (NNSL.getNestedNameSpecifier().getKind()) {
switch (NNSL.getNestedNameSpecifier()->getKind()) { case NestedNameSpecifier::Kind::Null:
llvm_unreachable("unexpected null nested name specifier");
#define NNS_KIND(X) \ #define NNS_KIND(X) \
case NestedNameSpecifier::X: \ case NestedNameSpecifier::Kind::X: \
return #X return #X
NNS_KIND(Identifier);
NNS_KIND(Namespace); NNS_KIND(Namespace);
NNS_KIND(TypeSpec); NNS_KIND(Type);
NNS_KIND(Global); NNS_KIND(Global);
NNS_KIND(Super); NNS_KIND(MicrosoftSuper);
#undef NNS_KIND #undef NNS_KIND
} }
llvm_unreachable("Unhandled SpecifierKind enum"); llvm_unreachable("Unhandled SpecifierKind enum");
@ -261,7 +261,7 @@ class DumpVisitor : public RecursiveASTVisitor<DumpVisitor> {
return TL.getType().getLocalQualifiers().getAsString( return TL.getType().getLocalQualifiers().getAsString(
Ctx.getPrintingPolicy()); Ctx.getPrintingPolicy());
if (const auto *TT = dyn_cast<TagType>(TL.getTypePtr())) if (const auto *TT = dyn_cast<TagType>(TL.getTypePtr()))
return getDetail(TT->getDecl()); return getDetail(TT->getOriginalDecl());
if (const auto *DT = dyn_cast<DeducedType>(TL.getTypePtr())) if (const auto *DT = dyn_cast<DeducedType>(TL.getTypePtr()))
if (DT->isDeduced()) if (DT->isDeduced())
return DT->getDeducedType().getAsString(Ctx.getPrintingPolicy()); return DT->getDeducedType().getAsString(Ctx.getPrintingPolicy());
@ -273,16 +273,11 @@ class DumpVisitor : public RecursiveASTVisitor<DumpVisitor> {
return getDetail(TT->getDecl()); return getDetail(TT->getDecl());
return ""; return "";
} }
std::string getDetail(const NestedNameSpecifierLoc &NNSL) { std::string getDetail(NestedNameSpecifierLoc NNSL) {
const auto &NNS = *NNSL.getNestedNameSpecifier(); NestedNameSpecifier NNS = NNSL.getNestedNameSpecifier();
switch (NNS.getKind()) { if (NNS.getKind() != NestedNameSpecifier::Kind::Namespace)
case NestedNameSpecifier::Identifier:
return NNS.getAsIdentifier()->getName().str() + "::";
case NestedNameSpecifier::Namespace:
return NNS.getAsNamespace()->getNameAsString() + "::";
default:
return ""; return "";
} return NNS.getAsNamespaceAndPrefix().Namespace->getNameAsString() + "::";
} }
std::string getDetail(const CXXCtorInitializer *CCI) { std::string getDetail(const CXXCtorInitializer *CCI) {
if (FieldDecl *FD = CCI->getAnyMember()) if (FieldDecl *FD = CCI->getAnyMember())
@ -346,8 +341,10 @@ public:
return !D || isInjectedClassName(D) || return !D || isInjectedClassName(D) ||
traverseNode("declaration", D, [&] { Base::TraverseDecl(D); }); traverseNode("declaration", D, [&] { Base::TraverseDecl(D); });
} }
bool TraverseTypeLoc(TypeLoc TL) { bool TraverseTypeLoc(TypeLoc TL, bool TraverseQualifier = true) {
return !TL || traverseNode("type", TL, [&] { Base::TraverseTypeLoc(TL); }); return !TL || traverseNode("type", TL, [&] {
Base::TraverseTypeLoc(TL, TraverseQualifier);
});
} }
bool TraverseTemplateName(const TemplateName &TN) { bool TraverseTemplateName(const TemplateName &TN) {
return traverseNode("template name", TN, return traverseNode("template name", TN,
@ -389,11 +386,11 @@ public:
// This means we'd never see 'int' in 'const int'! Work around that here. // This means we'd never see 'int' in 'const int'! Work around that here.
// (The reason for the behavior is to avoid traversing the nested Type twice, // (The reason for the behavior is to avoid traversing the nested Type twice,
// but we ignore TraverseType anyway). // but we ignore TraverseType anyway).
bool TraverseQualifiedTypeLoc(QualifiedTypeLoc QTL) { bool TraverseQualifiedTypeLoc(QualifiedTypeLoc QTL, bool TraverseQualifier) {
return TraverseTypeLoc(QTL.getUnqualifiedLoc()); return TraverseTypeLoc(QTL.getUnqualifiedLoc());
} }
// Uninteresting parts of the AST that don't have locations within them. // Uninteresting parts of the AST that don't have locations within them.
bool TraverseNestedNameSpecifier(NestedNameSpecifier *) { return true; } bool TraverseNestedNameSpecifier(NestedNameSpecifier) { return true; }
bool TraverseType(QualType) { return true; } bool TraverseType(QualType) { return true; }
// OpaqueValueExpr blocks traversal, we must explicitly traverse it. // OpaqueValueExpr blocks traversal, we must explicitly traverse it.
@ -420,7 +417,7 @@ ASTNode dumpAST(const DynTypedNode &N, const syntax::TokenBuffer &Tokens,
V.TraverseNestedNameSpecifierLoc( V.TraverseNestedNameSpecifierLoc(
*const_cast<NestedNameSpecifierLoc *>(NNSL)); *const_cast<NestedNameSpecifierLoc *>(NNSL));
else if (const auto *NNS = N.get<NestedNameSpecifier>()) else if (const auto *NNS = N.get<NestedNameSpecifier>())
V.TraverseNestedNameSpecifier(const_cast<NestedNameSpecifier *>(NNS)); V.TraverseNestedNameSpecifier(*NNS);
else if (const auto *TL = N.get<TypeLoc>()) else if (const auto *TL = N.get<TypeLoc>())
V.TraverseTypeLoc(*const_cast<TypeLoc *>(TL)); V.TraverseTypeLoc(*const_cast<TypeLoc *>(TL));
else if (const auto *QT = N.get<QualType>()) else if (const auto *QT = N.get<QualType>())

View File

@ -366,19 +366,11 @@ public:
Visitor(TargetFinder &Outer, RelSet Flags) : Outer(Outer), Flags(Flags) {} Visitor(TargetFinder &Outer, RelSet Flags) : Outer(Outer), Flags(Flags) {}
void VisitTagType(const TagType *TT) { void VisitTagType(const TagType *TT) {
Outer.add(TT->getAsTagDecl(), Flags); Outer.add(cast<TagType>(TT)->getOriginalDecl(), Flags);
}
void VisitElaboratedType(const ElaboratedType *ET) {
Outer.add(ET->desugar(), Flags);
} }
void VisitUsingType(const UsingType *ET) { void VisitUsingType(const UsingType *ET) {
Outer.add(ET->getFoundDecl(), Flags); Outer.add(ET->getDecl(), Flags);
}
void VisitInjectedClassNameType(const InjectedClassNameType *ICNT) {
Outer.add(ICNT->getDecl(), Flags);
} }
void VisitDecltypeType(const DecltypeType *DTT) { void VisitDecltypeType(const DecltypeType *DTT) {
@ -483,30 +475,27 @@ public:
Visitor(*this, Flags).Visit(T.getTypePtr()); Visitor(*this, Flags).Visit(T.getTypePtr());
} }
void add(const NestedNameSpecifier *NNS, RelSet Flags) { void add(NestedNameSpecifier NNS, RelSet Flags) {
if (!NNS) if (!NNS)
return; return;
debug(*NNS, Flags); debug(NNS, Flags);
switch (NNS->getKind()) { switch (NNS.getKind()) {
case NestedNameSpecifier::Namespace: case NestedNameSpecifier::Kind::Namespace:
add(NNS->getAsNamespace(), Flags); add(NNS.getAsNamespaceAndPrefix().Namespace, Flags);
return; return;
case NestedNameSpecifier::Identifier: case NestedNameSpecifier::Kind::Type:
if (Resolver) { add(QualType(NNS.getAsType(), 0), Flags);
add(Resolver->resolveNestedNameSpecifierToType(NNS), Flags);
}
return; return;
case NestedNameSpecifier::TypeSpec: case NestedNameSpecifier::Kind::Global:
add(QualType(NNS->getAsType(), 0), Flags);
return;
case NestedNameSpecifier::Global:
// This should be TUDecl, but we can't get a pointer to it! // This should be TUDecl, but we can't get a pointer to it!
return; return;
case NestedNameSpecifier::Super: case NestedNameSpecifier::Kind::MicrosoftSuper:
add(NNS->getAsRecordDecl(), Flags); add(NNS.getAsMicrosoftSuper(), Flags);
return; return;
case NestedNameSpecifier::Kind::Null:
llvm_unreachable("unexpected null nested name specifier");
} }
llvm_unreachable("unhandled NestedNameSpecifier::SpecifierKind"); llvm_unreachable("unhandled NestedNameSpecifier::Kind");
} }
void add(const CXXCtorInitializer *CCI, RelSet Flags) { void add(const CXXCtorInitializer *CCI, RelSet Flags) {
@ -555,7 +544,7 @@ allTargetDecls(const DynTypedNode &N, const HeuristicResolver *Resolver) {
else if (const NestedNameSpecifierLoc *NNSL = N.get<NestedNameSpecifierLoc>()) else if (const NestedNameSpecifierLoc *NNSL = N.get<NestedNameSpecifierLoc>())
Finder.add(NNSL->getNestedNameSpecifier(), Flags); Finder.add(NNSL->getNestedNameSpecifier(), Flags);
else if (const NestedNameSpecifier *NNS = N.get<NestedNameSpecifier>()) else if (const NestedNameSpecifier *NNS = N.get<NestedNameSpecifier>())
Finder.add(NNS, Flags); Finder.add(*NNS, Flags);
else if (const TypeLoc *TL = N.get<TypeLoc>()) else if (const TypeLoc *TL = N.get<TypeLoc>())
Finder.add(TL->getType(), Flags); Finder.add(TL->getType(), Flags);
else if (const QualType *QT = N.get<QualType>()) else if (const QualType *QT = N.get<QualType>())
@ -861,32 +850,25 @@ refInTypeLoc(TypeLoc L, const HeuristicResolver *Resolver) {
const HeuristicResolver *Resolver; const HeuristicResolver *Resolver;
llvm::SmallVector<ReferenceLoc> Refs; llvm::SmallVector<ReferenceLoc> Refs;
void VisitElaboratedTypeLoc(ElaboratedTypeLoc L) { void VisitUnresolvedUsingTypeLoc(UnresolvedUsingTypeLoc L) {
// We only know about qualifier, rest if filled by inner locations. Refs.push_back(ReferenceLoc{L.getQualifierLoc(),
size_t InitialSize = Refs.size(); L.getLocalSourceRange().getBegin(),
Visit(L.getNamedTypeLoc().getUnqualifiedLoc()); /*IsDecl=*/false,
size_t NewSize = Refs.size(); {L.getDecl()}});
// Add qualifier for the newly-added refs.
for (unsigned I = InitialSize; I < NewSize; ++I) {
ReferenceLoc *Ref = &Refs[I];
// Fill in the qualifier.
assert(!Ref->Qualifier.hasQualifier() && "qualifier already set");
Ref->Qualifier = L.getQualifierLoc();
}
} }
void VisitUsingTypeLoc(UsingTypeLoc L) { void VisitUsingTypeLoc(UsingTypeLoc L) {
Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(), Refs.push_back(ReferenceLoc{L.getQualifierLoc(),
L.getLocalSourceRange().getBegin(), L.getLocalSourceRange().getBegin(),
/*IsDecl=*/false, /*IsDecl=*/false,
{L.getFoundDecl()}}); {L.getDecl()}});
} }
void VisitTagTypeLoc(TagTypeLoc L) { void VisitTagTypeLoc(TagTypeLoc L) {
Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(), Refs.push_back(ReferenceLoc{L.getQualifierLoc(),
L.getNameLoc(), L.getNameLoc(),
/*IsDecl=*/false, /*IsDecl=*/false,
{L.getDecl()}}); {L.getOriginalDecl()}});
} }
void VisitTemplateTypeParmTypeLoc(TemplateTypeParmTypeLoc L) { void VisitTemplateTypeParmTypeLoc(TemplateTypeParmTypeLoc L) {
@ -906,25 +888,18 @@ refInTypeLoc(TypeLoc L, const HeuristicResolver *Resolver) {
// 2. 'vector<int>' with mask 'Underlying'. // 2. 'vector<int>' with mask 'Underlying'.
// we want to return only #1 in this case. // we want to return only #1 in this case.
Refs.push_back(ReferenceLoc{ Refs.push_back(ReferenceLoc{
NestedNameSpecifierLoc(), L.getTemplateNameLoc(), /*IsDecl=*/false, L.getQualifierLoc(), L.getTemplateNameLoc(), /*IsDecl=*/false,
explicitReferenceTargets(DynTypedNode::create(L.getType()), explicitReferenceTargets(DynTypedNode::create(L.getType()),
DeclRelation::Alias, Resolver)}); DeclRelation::Alias, Resolver)});
} }
void VisitDeducedTemplateSpecializationTypeLoc( void VisitDeducedTemplateSpecializationTypeLoc(
DeducedTemplateSpecializationTypeLoc L) { DeducedTemplateSpecializationTypeLoc L) {
Refs.push_back(ReferenceLoc{ Refs.push_back(ReferenceLoc{
NestedNameSpecifierLoc(), L.getNameLoc(), /*IsDecl=*/false, L.getQualifierLoc(), L.getNameLoc(), /*IsDecl=*/false,
explicitReferenceTargets(DynTypedNode::create(L.getType()), explicitReferenceTargets(DynTypedNode::create(L.getType()),
DeclRelation::Alias, Resolver)}); DeclRelation::Alias, Resolver)});
} }
void VisitInjectedClassNameTypeLoc(InjectedClassNameTypeLoc TL) {
Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(),
TL.getNameLoc(),
/*IsDecl=*/false,
{TL.getDecl()}});
}
void VisitDependentTemplateSpecializationTypeLoc( void VisitDependentTemplateSpecializationTypeLoc(
DependentTemplateSpecializationTypeLoc L) { DependentTemplateSpecializationTypeLoc L) {
Refs.push_back( Refs.push_back(
@ -943,12 +918,12 @@ refInTypeLoc(TypeLoc L, const HeuristicResolver *Resolver) {
} }
void VisitTypedefTypeLoc(TypedefTypeLoc L) { void VisitTypedefTypeLoc(TypedefTypeLoc L) {
if (shouldSkipTypedef(L.getTypedefNameDecl())) if (shouldSkipTypedef(L.getDecl()))
return; return;
Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(), Refs.push_back(ReferenceLoc{L.getQualifierLoc(),
L.getNameLoc(), L.getNameLoc(),
/*IsDecl=*/false, /*IsDecl=*/false,
{L.getTypedefNameDecl()}}); {L.getDecl()}});
} }
void VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc L) { void VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc L) {
@ -980,17 +955,6 @@ public:
return true; return true;
} }
bool TraverseElaboratedTypeLoc(ElaboratedTypeLoc L) {
// ElaboratedTypeLoc will reports information for its inner type loc.
// Otherwise we loose information about inner types loc's qualifier.
TypeLoc Inner = L.getNamedTypeLoc().getUnqualifiedLoc();
if (L.getBeginLoc() == Inner.getBeginLoc())
return RecursiveASTVisitor::TraverseTypeLoc(Inner);
else
TypeLocsToSkip.insert(Inner.getBeginLoc());
return RecursiveASTVisitor::TraverseElaboratedTypeLoc(L);
}
bool VisitStmt(Stmt *S) { bool VisitStmt(Stmt *S) {
visitNode(DynTypedNode::create(*S)); visitNode(DynTypedNode::create(*S));
return true; return true;
@ -1051,7 +1015,7 @@ public:
return true; return true;
visitNode(DynTypedNode::create(L)); visitNode(DynTypedNode::create(L));
// Inner type is missing information about its qualifier, skip it. // Inner type is missing information about its qualifier, skip it.
if (auto TL = L.getTypeLoc()) if (auto TL = L.getAsTypeLoc())
TypeLocsToSkip.insert(TL.getBeginLoc()); TypeLocsToSkip.insert(TL.getBeginLoc());
return RecursiveASTVisitor::TraverseNestedNameSpecifierLoc(L); return RecursiveASTVisitor::TraverseNestedNameSpecifierLoc(L);
} }
@ -1092,12 +1056,21 @@ private:
if (auto *S = N.get<Stmt>()) if (auto *S = N.get<Stmt>())
return refInStmt(S, Resolver); return refInStmt(S, Resolver);
if (auto *NNSL = N.get<NestedNameSpecifierLoc>()) { if (auto *NNSL = N.get<NestedNameSpecifierLoc>()) {
// (!) 'DeclRelation::Alias' ensures we do not loose namespace aliases. // (!) 'DeclRelation::Alias' ensures we do not lose namespace aliases.
return {ReferenceLoc{ NestedNameSpecifierLoc Qualifier;
NNSL->getPrefix(), NNSL->getLocalBeginLoc(), false, SourceLocation NameLoc;
explicitReferenceTargets( if (auto TL = NNSL->getAsTypeLoc()) {
DynTypedNode::create(*NNSL->getNestedNameSpecifier()), Qualifier = TL.getPrefix();
DeclRelation::Alias, Resolver)}}; NameLoc = TL.getNonPrefixBeginLoc();
} else {
Qualifier = NNSL->getAsNamespaceAndPrefix().Prefix;
NameLoc = NNSL->getLocalBeginLoc();
}
return {
ReferenceLoc{Qualifier, NameLoc, false,
explicitReferenceTargets(
DynTypedNode::create(NNSL->getNestedNameSpecifier()),
DeclRelation::Alias, Resolver)}};
} }
if (const TypeLoc *TL = N.get<TypeLoc>()) if (const TypeLoc *TL = N.get<TypeLoc>())
return refInTypeLoc(*TL, Resolver); return refInTypeLoc(*TL, Resolver);
@ -1210,8 +1183,8 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ReferenceLoc R) {
OS << "}"; OS << "}";
if (R.Qualifier) { if (R.Qualifier) {
OS << ", qualifier = '"; OS << ", qualifier = '";
R.Qualifier.getNestedNameSpecifier()->print(OS, R.Qualifier.getNestedNameSpecifier().print(OS,
PrintingPolicy(LangOptions())); PrintingPolicy(LangOptions()));
OS << "'"; OS << "'";
} }
if (R.IsDecl) if (R.IsDecl)

View File

@ -172,13 +172,14 @@ HoverInfo::PrintedType printType(QualType QT, ASTContext &ASTCtx,
QT = QT->castAs<DecltypeType>()->getUnderlyingType(); QT = QT->castAs<DecltypeType>()->getUnderlyingType();
HoverInfo::PrintedType Result; HoverInfo::PrintedType Result;
llvm::raw_string_ostream OS(Result.Type); llvm::raw_string_ostream OS(Result.Type);
// Special case: if the outer type is a tag type without qualifiers, then // Special case: if the outer type is a canonical tag type, then include the
// include the tag for extra clarity. // tag for extra clarity. This isn't very idiomatic, so don't attempt it for
// This isn't very idiomatic, so don't attempt it for complex cases, including // complex cases, including pointers/references, template specializations,
// pointers/references, template specializations, etc. // etc.
if (!QT.isNull() && !QT.hasQualifiers() && PP.SuppressTagKeyword) { if (!QT.isNull() && !QT.hasQualifiers() && PP.SuppressTagKeyword) {
if (auto *TT = llvm::dyn_cast<TagType>(QT.getTypePtr())) if (auto *TT = llvm::dyn_cast<TagType>(QT.getTypePtr());
OS << TT->getDecl()->getKindName() << " "; TT && TT->isCanonicalUnqualified())
OS << TT->getOriginalDecl()->getKindName() << " ";
} }
QT.print(OS, PP); QT.print(OS, PP);
@ -454,7 +455,7 @@ std::optional<std::string> printExprValue(const Expr *E,
// Compare to int64_t to avoid bit-width match requirements. // Compare to int64_t to avoid bit-width match requirements.
int64_t Val = Constant.Val.getInt().getExtValue(); int64_t Val = Constant.Val.getInt().getExtValue();
for (const EnumConstantDecl *ECD : for (const EnumConstantDecl *ECD :
T->castAs<EnumType>()->getDecl()->enumerators()) T->castAs<EnumType>()->getOriginalDecl()->enumerators())
if (ECD->getInitVal() == Val) if (ECD->getInitVal() == Val)
return llvm::formatv("{0} ({1})", ECD->getNameAsString(), return llvm::formatv("{0} ({1})", ECD->getNameAsString(),
printHex(Constant.Val.getInt())) printHex(Constant.Val.getInt()))
@ -972,10 +973,11 @@ void addLayoutInfo(const NamedDecl &ND, HoverInfo &HI) {
const auto &Ctx = ND.getASTContext(); const auto &Ctx = ND.getASTContext();
if (auto *RD = llvm::dyn_cast<RecordDecl>(&ND)) { if (auto *RD = llvm::dyn_cast<RecordDecl>(&ND)) {
if (auto Size = Ctx.getTypeSizeInCharsIfKnown(RD->getTypeForDecl())) CanQualType RT = Ctx.getCanonicalTagType(RD);
if (auto Size = Ctx.getTypeSizeInCharsIfKnown(RT))
HI.Size = Size->getQuantity() * 8; HI.Size = Size->getQuantity() * 8;
if (!RD->isDependentType() && RD->isCompleteDefinition()) if (!RD->isDependentType() && RD->isCompleteDefinition())
HI.Align = Ctx.getTypeAlign(RD->getTypeForDecl()); HI.Align = Ctx.getTypeAlign(RT);
return; return;
} }

View File

@ -173,7 +173,7 @@ std::vector<Fix> IncludeFixer::fix(DiagnosticsEngine::Level DiagLevel,
// `enum x : int;' is not formally an incomplete type. // `enum x : int;' is not formally an incomplete type.
// We may need a full definition anyway. // We may need a full definition anyway.
if (auto * ET = llvm::dyn_cast<EnumType>(T)) if (auto * ET = llvm::dyn_cast<EnumType>(T))
if (!ET->getDecl()->getDefinition()) if (!ET->getOriginalDecl()->getDefinition())
return fixIncompleteType(*T); return fixIncompleteType(*T);
} }
} }
@ -400,35 +400,35 @@ std::optional<CheapUnresolvedName> extractUnresolvedNameCheaply(
CheapUnresolvedName Result; CheapUnresolvedName Result;
Result.Name = Unresolved.getAsString(); Result.Name = Unresolved.getAsString();
if (SS && SS->isNotEmpty()) { // "::" or "ns::" if (SS && SS->isNotEmpty()) { // "::" or "ns::"
if (auto *Nested = SS->getScopeRep()) { NestedNameSpecifier Nested = SS->getScopeRep();
if (Nested->getKind() == NestedNameSpecifier::Global) { if (Nested.getKind() == NestedNameSpecifier::Kind::Global) {
Result.ResolvedScope = ""; Result.ResolvedScope = "";
} else if (const NamespaceBaseDecl *NSB = Nested->getAsNamespace()) { } else if (Nested.getKind() == NestedNameSpecifier::Kind::Namespace) {
if (const auto *NS = dyn_cast<NamespaceDecl>(NSB)) { const NamespaceBaseDecl *NSB = Nested.getAsNamespaceAndPrefix().Namespace;
std::string SpecifiedNS = printNamespaceScope(*NS); if (const auto *NS = dyn_cast<NamespaceDecl>(NSB)) {
std::optional<std::string> Spelling = getSpelledSpecifier(*SS, SM); std::string SpecifiedNS = printNamespaceScope(*NS);
std::optional<std::string> Spelling = getSpelledSpecifier(*SS, SM);
// Check the specifier spelled in the source. // Check the specifier spelled in the source.
// If the resolved scope doesn't end with the spelled scope, the // If the resolved scope doesn't end with the spelled scope, the
// resolved scope may come from a sema typo correction. For example, // resolved scope may come from a sema typo correction. For example,
// sema assumes that "clangd::" is a typo of "clang::" and uses // sema assumes that "clangd::" is a typo of "clang::" and uses
// "clang::" as the specified scope in: // "clang::" as the specified scope in:
// namespace clang { clangd::X; } // namespace clang { clangd::X; }
// In this case, we use the "typo" specifier as extra scope instead // In this case, we use the "typo" specifier as extra scope instead
// of using the scope assumed by sema. // of using the scope assumed by sema.
if (!Spelling || llvm::StringRef(SpecifiedNS).ends_with(*Spelling)) { if (!Spelling || llvm::StringRef(SpecifiedNS).ends_with(*Spelling)) {
Result.ResolvedScope = std::move(SpecifiedNS); Result.ResolvedScope = std::move(SpecifiedNS);
} else {
Result.UnresolvedScope = std::move(*Spelling);
}
} else { } else {
Result.ResolvedScope = printNamespaceScope(*cast<NamespaceAliasDecl>(NSB)->getNamespace()); Result.UnresolvedScope = std::move(*Spelling);
} }
} else { } else {
// We don't fix symbols in scopes that are not top-level e.g. class Result.ResolvedScope = printNamespaceScope(*cast<NamespaceAliasDecl>(NSB)->getNamespace());
// members, as we don't collect includes for them.
return std::nullopt;
} }
} else {
// We don't fix symbols in scopes that are not top-level e.g. class
// members, as we don't collect includes for them.
return std::nullopt;
} }
} }

View File

@ -55,18 +55,24 @@ void stripLeadingUnderscores(StringRef &Name) { Name = Name.ltrim('_'); }
// getDeclForType() returns the decl responsible for Type's spelling. // getDeclForType() returns the decl responsible for Type's spelling.
// This is the inverse of ASTContext::getTypeDeclType(). // This is the inverse of ASTContext::getTypeDeclType().
template <typename Ty, typename = decltype(((Ty *)nullptr)->getDecl())>
const NamedDecl *getDeclForTypeImpl(const Ty *T) {
return T->getDecl();
}
const NamedDecl *getDeclForTypeImpl(const void *T) { return nullptr; }
const NamedDecl *getDeclForType(const Type *T) { const NamedDecl *getDeclForType(const Type *T) {
switch (T->getTypeClass()) { switch (T->getTypeClass()) {
#define ABSTRACT_TYPE(TY, BASE) case Type::Enum:
#define TYPE(TY, BASE) \ case Type::Record:
case Type::TY: \ case Type::InjectedClassName:
return getDeclForTypeImpl(llvm::cast<TY##Type>(T)); return cast<TagType>(T)->getOriginalDecl();
#include "clang/AST/TypeNodes.inc" case Type::TemplateSpecialization:
return cast<TemplateSpecializationType>(T)
->getTemplateName()
.getAsTemplateDecl(/*IgnoreDeduced=*/true);
case Type::Typedef:
return cast<TypedefType>(T)->getDecl();
case Type::UnresolvedUsing:
return cast<UnresolvedUsingType>(T)->getDecl();
case Type::Using:
return cast<UsingType>(T)->getDecl();
default:
return nullptr;
} }
llvm_unreachable("Unknown TypeClass enum"); llvm_unreachable("Unknown TypeClass enum");
} }
@ -81,8 +87,6 @@ llvm::StringRef getSimpleName(const NamedDecl &D) {
return getSimpleName(D.getDeclName()); return getSimpleName(D.getDeclName());
} }
llvm::StringRef getSimpleName(QualType T) { llvm::StringRef getSimpleName(QualType T) {
if (const auto *ET = llvm::dyn_cast<ElaboratedType>(T))
return getSimpleName(ET->getNamedType());
if (const auto *BT = llvm::dyn_cast<BuiltinType>(T)) { if (const auto *BT = llvm::dyn_cast<BuiltinType>(T)) {
PrintingPolicy PP(LangOptions{}); PrintingPolicy PP(LangOptions{});
PP.adjustForCPlusPlus(); PP.adjustForCPlusPlus();

View File

@ -62,7 +62,8 @@ void recordMetrics(const SelectionTree &S, const LangOptions &Lang) {
} }
// Return the range covering a node and all its children. // Return the range covering a node and all its children.
SourceRange getSourceRange(const DynTypedNode &N) { SourceRange getSourceRange(const DynTypedNode &N,
bool IncludeQualifier = false) {
// MemberExprs to implicitly access anonymous fields should not claim any // MemberExprs to implicitly access anonymous fields should not claim any
// tokens for themselves. Given: // tokens for themselves. Given:
// struct A { struct { int b; }; }; // struct A { struct { int b; }; };
@ -80,7 +81,7 @@ SourceRange getSourceRange(const DynTypedNode &N) {
? getSourceRange(DynTypedNode::create(*ME->getBase())) ? getSourceRange(DynTypedNode::create(*ME->getBase()))
: SourceRange(); : SourceRange();
} }
return N.getSourceRange(); return N.getSourceRange(IncludeQualifier);
} }
// An IntervalSet maintains a set of disjoint subranges of an array. // An IntervalSet maintains a set of disjoint subranges of an array.
@ -643,8 +644,9 @@ public:
} }
return traverseNode(X, [&] { return Base::TraverseDecl(X); }); return traverseNode(X, [&] { return Base::TraverseDecl(X); });
} }
bool TraverseTypeLoc(TypeLoc X) { bool TraverseTypeLoc(TypeLoc X, bool TraverseQualifier = true) {
return traverseNode(&X, [&] { return Base::TraverseTypeLoc(X); }); return traverseNode(
&X, [&] { return Base::TraverseTypeLoc(X, TraverseQualifier); });
} }
bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &X) { bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &X) {
return traverseNode(&X, return traverseNode(&X,
@ -690,7 +692,8 @@ public:
// This means we'd never see 'int' in 'const int'! Work around that here. // This means we'd never see 'int' in 'const int'! Work around that here.
// (The reason for the behavior is to avoid traversing the nested Type twice, // (The reason for the behavior is to avoid traversing the nested Type twice,
// but we ignore TraverseType anyway). // but we ignore TraverseType anyway).
bool TraverseQualifiedTypeLoc(QualifiedTypeLoc QX) { bool TraverseQualifiedTypeLoc(QualifiedTypeLoc QX,
bool TraverseQualifier = true) {
return traverseNode<TypeLoc>( return traverseNode<TypeLoc>(
&QX, [&] { return TraverseTypeLoc(QX.getUnqualifiedLoc()); }); &QX, [&] { return TraverseTypeLoc(QX.getUnqualifiedLoc()); });
} }
@ -698,7 +701,7 @@ public:
return traverseNode(&PL, [&] { return Base::TraverseObjCProtocolLoc(PL); }); return traverseNode(&PL, [&] { return Base::TraverseObjCProtocolLoc(PL); });
} }
// Uninteresting parts of the AST that don't have locations within them. // Uninteresting parts of the AST that don't have locations within them.
bool TraverseNestedNameSpecifier(NestedNameSpecifier *) { return true; } bool TraverseNestedNameSpecifier(NestedNameSpecifier) { return true; }
bool TraverseType(QualType) { return true; } bool TraverseType(QualType) { return true; }
// The DeclStmt for the loop variable claims to cover the whole range // The DeclStmt for the loop variable claims to cover the whole range
@ -798,7 +801,7 @@ private:
// An optimization for a common case: nodes outside macro expansions that // An optimization for a common case: nodes outside macro expansions that
// don't intersect the selection may be recursively skipped. // don't intersect the selection may be recursively skipped.
bool canSafelySkipNode(const DynTypedNode &N) { bool canSafelySkipNode(const DynTypedNode &N) {
SourceRange S = getSourceRange(N); SourceRange S = getSourceRange(N, /*IncludeQualifier=*/true);
if (auto *TL = N.get<TypeLoc>()) { if (auto *TL = N.get<TypeLoc>()) {
// FIXME: TypeLoc::getBeginLoc()/getEndLoc() are pretty fragile // FIXME: TypeLoc::getBeginLoc()/getEndLoc() are pretty fragile
// heuristics. We should consider only pruning critical TypeLoc nodes, to // heuristics. We should consider only pruning critical TypeLoc nodes, to

View File

@ -1127,21 +1127,6 @@ public:
return RecursiveASTVisitor::TraverseTemplateArgumentLoc(L); return RecursiveASTVisitor::TraverseTemplateArgumentLoc(L);
} }
// findExplicitReferences will walk nested-name-specifiers and
// find anything that can be resolved to a Decl. However, non-leaf
// components of nested-name-specifiers which are dependent names
// (kind "Identifier") cannot be resolved to a decl, so we visit
// them here.
bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc Q) {
if (NestedNameSpecifier *NNS = Q.getNestedNameSpecifier()) {
if (NNS->getKind() == NestedNameSpecifier::Identifier)
H.addToken(Q.getLocalBeginLoc(), HighlightingKind::Type)
.addModifier(HighlightingModifier::DependentName)
.addModifier(HighlightingModifier::ClassScope);
}
return RecursiveASTVisitor::TraverseNestedNameSpecifierLoc(Q);
}
private: private:
HighlightingsBuilder &H; HighlightingsBuilder &H;
}; };

View File

@ -1965,7 +1965,8 @@ std::vector<const CXXRecordDecl *> findRecordTypeAt(ParsedAST &AST,
// Return the type most associated with an AST node. // Return the type most associated with an AST node.
// This isn't precisely defined: we want "go to type" to do something useful. // This isn't precisely defined: we want "go to type" to do something useful.
static QualType typeForNode(const SelectionTree::Node *N) { static QualType typeForNode(const ASTContext &Ctx,
const SelectionTree::Node *N) {
// If we're looking at a namespace qualifier, walk up to what it's qualifying. // If we're looking at a namespace qualifier, walk up to what it's qualifying.
// (If we're pointing at a *class* inside a NNS, N will be a TypeLoc). // (If we're pointing at a *class* inside a NNS, N will be a TypeLoc).
while (N && N->ASTNode.get<NestedNameSpecifierLoc>()) while (N && N->ASTNode.get<NestedNameSpecifierLoc>())
@ -1999,10 +2000,13 @@ static QualType typeForNode(const SelectionTree::Node *N) {
if (const Decl *D = N->ASTNode.get<Decl>()) { if (const Decl *D = N->ASTNode.get<Decl>()) {
struct Visitor : ConstDeclVisitor<Visitor, QualType> { struct Visitor : ConstDeclVisitor<Visitor, QualType> {
const ASTContext &Ctx;
Visitor(const ASTContext &Ctx) : Ctx(Ctx) {}
QualType VisitValueDecl(const ValueDecl *D) { return D->getType(); } QualType VisitValueDecl(const ValueDecl *D) { return D->getType(); }
// Declaration of a type => that type. // Declaration of a type => that type.
QualType VisitTypeDecl(const TypeDecl *D) { QualType VisitTypeDecl(const TypeDecl *D) {
return QualType(D->getTypeForDecl(), 0); return Ctx.getTypeDeclType(D);
} }
// Exception: alias declaration => the underlying type, not the alias. // Exception: alias declaration => the underlying type, not the alias.
QualType VisitTypedefNameDecl(const TypedefNameDecl *D) { QualType VisitTypedefNameDecl(const TypedefNameDecl *D) {
@ -2012,7 +2016,7 @@ static QualType typeForNode(const SelectionTree::Node *N) {
QualType VisitTemplateDecl(const TemplateDecl *D) { QualType VisitTemplateDecl(const TemplateDecl *D) {
return Visit(D->getTemplatedDecl()); return Visit(D->getTemplatedDecl());
} }
} V; } V(Ctx);
return V.Visit(D); return V.Visit(D);
} }
@ -2156,7 +2160,8 @@ std::vector<LocatedSymbol> findType(ParsedAST &AST, Position Pos,
// unique_ptr<unique_ptr<T>>. Let's *not* remove them, because it gives you some // unique_ptr<unique_ptr<T>>. Let's *not* remove them, because it gives you some
// information about the type you may have not known before // information about the type you may have not known before
// (since unique_ptr<unique_ptr<T>> != unique_ptr<T>). // (since unique_ptr<unique_ptr<T>> != unique_ptr<T>).
for (const QualType& Type : unwrapFindType(typeForNode(N), AST.getHeuristicResolver())) for (const QualType &Type : unwrapFindType(
typeForNode(AST.getASTContext(), N), AST.getHeuristicResolver()))
llvm::copy(locateSymbolForType(AST, Type, Index), llvm::copy(locateSymbolForType(AST, Type, Index),
std::back_inserter(LocatedSymbols)); std::back_inserter(LocatedSymbols));

View File

@ -115,13 +115,6 @@ private:
const SourceManager &SM; const SourceManager &SM;
}; };
bool isFullyQualified(const NestedNameSpecifier *NNS) {
if (!NNS)
return false;
return NNS->getKind() == NestedNameSpecifier::Global ||
isFullyQualified(NNS->getPrefix());
}
struct InsertionPointData { struct InsertionPointData {
// Location to insert the "using" statement. If invalid then the statement // Location to insert the "using" statement. If invalid then the statement
// should not be inserted at all (it already exists). // should not be inserted at all (it already exists).
@ -167,18 +160,20 @@ findInsertionPoint(const Tweak::Selection &Inputs,
for (auto &U : Usings) { for (auto &U : Usings) {
// Only "upgrade" to fully qualified is all relevant using decls are fully // Only "upgrade" to fully qualified is all relevant using decls are fully
// qualified. Otherwise trust what the user typed. // qualified. Otherwise trust what the user typed.
if (!isFullyQualified(U->getQualifier())) if (!U->getQualifier().isFullyQualified())
AlwaysFullyQualify = false; AlwaysFullyQualify = false;
if (SM.isBeforeInTranslationUnit(Inputs.Cursor, U->getUsingLoc())) if (SM.isBeforeInTranslationUnit(Inputs.Cursor, U->getUsingLoc()))
// "Usings" is sorted, so we're done. // "Usings" is sorted, so we're done.
break; break;
if (const auto *Namespace = dyn_cast_if_present<NamespaceDecl>( if (NestedNameSpecifier Qualifier = U->getQualifier();
U->getQualifier()->getAsNamespace())) { Qualifier.getKind() == NestedNameSpecifier::Kind::Namespace) {
const auto *Namespace =
U->getQualifier().getAsNamespaceAndPrefix().Namespace;
if (Namespace->getCanonicalDecl() == if (Namespace->getCanonicalDecl() ==
QualifierToRemove.getNestedNameSpecifier() QualifierToRemove.getNestedNameSpecifier()
->getAsNamespace() .getAsNamespaceAndPrefix()
->getCanonicalDecl() && .Namespace->getCanonicalDecl() &&
U->getName() == Name) { U->getName() == Name) {
return InsertionPointData(); return InsertionPointData();
} }
@ -232,8 +227,9 @@ findInsertionPoint(const Tweak::Selection &Inputs,
} }
bool isNamespaceForbidden(const Tweak::Selection &Inputs, bool isNamespaceForbidden(const Tweak::Selection &Inputs,
const NestedNameSpecifier &Namespace) { NestedNameSpecifier Namespace) {
const auto *NS = dyn_cast<NamespaceDecl>(Namespace.getAsNamespace()); const auto *NS =
dyn_cast<NamespaceDecl>(Namespace.getAsNamespaceAndPrefix().Namespace);
if (!NS) if (!NS)
return true; return true;
std::string NamespaceStr = printNamespaceScope(*NS); std::string NamespaceStr = printNamespaceScope(*NS);
@ -247,11 +243,11 @@ bool isNamespaceForbidden(const Tweak::Selection &Inputs,
return false; return false;
} }
std::string getNNSLAsString(NestedNameSpecifierLoc &NNSL, std::string getNNSLAsString(NestedNameSpecifierLoc NNSL,
const PrintingPolicy &Policy) { const PrintingPolicy &Policy) {
std::string Out; std::string Out;
llvm::raw_string_ostream OutStream(Out); llvm::raw_string_ostream OutStream(Out);
NNSL.getNestedNameSpecifier()->print(OutStream, Policy); NNSL.getNestedNameSpecifier().print(OutStream, Policy);
return OutStream.str(); return OutStream.str();
} }
@ -276,16 +272,15 @@ bool AddUsing::prepare(const Selection &Inputs) {
continue; continue;
} }
if (auto *T = Node->ASTNode.get<TypeLoc>()) { if (auto *T = Node->ASTNode.get<TypeLoc>()) {
if (T->getAs<ElaboratedTypeLoc>()) { // Find the outermost TypeLoc.
break; if (Node->Parent->ASTNode.get<NestedNameSpecifierLoc>())
} continue;
if (Node->Parent->ASTNode.get<TypeLoc>() || if (isa<TagType, TemplateSpecializationType, TypedefType, UsingType,
Node->Parent->ASTNode.get<NestedNameSpecifierLoc>()) { UnresolvedUsingType>(T->getTypePtr()))
// Node is TypeLoc, but it's parent is either TypeLoc or break;
// NestedNameSpecifier. In both cases, we want to go up, to find // Find the outermost TypeLoc.
// the outermost TypeLoc. if (Node->Parent->ASTNode.get<TypeLoc>())
continue; continue;
}
} }
break; break;
} }
@ -307,32 +302,70 @@ bool AddUsing::prepare(const Selection &Inputs) {
MustInsertAfterLoc = D->getDecl()->getBeginLoc(); MustInsertAfterLoc = D->getDecl()->getBeginLoc();
} }
} else if (auto *T = Node->ASTNode.get<TypeLoc>()) { } else if (auto *T = Node->ASTNode.get<TypeLoc>()) {
if (auto E = T->getAs<ElaboratedTypeLoc>()) { switch (T->getTypeLocClass()) {
QualifierToRemove = E.getQualifierLoc(); case TypeLoc::TemplateSpecialization: {
auto TL = T->castAs<TemplateSpecializationTypeLoc>();
SpelledNameRange = E.getSourceRange(); QualifierToRemove = TL.getQualifierLoc();
if (auto T = E.getNamedTypeLoc().getAs<TemplateSpecializationTypeLoc>()) { if (!QualifierToRemove)
// Remove the template arguments from the name. break;
SpelledNameRange.setEnd(T.getLAngleLoc().getLocWithOffset(-1)); SpelledNameRange = TL.getTemplateNameLoc();
} if (auto *TD = TL.getTypePtr()->getTemplateName().getAsTemplateDecl(
/*IgnoreDeduced=*/true))
if (const auto *ET = E.getTypePtr()) { MustInsertAfterLoc = TD->getBeginLoc();
if (const auto *TDT = break;
dyn_cast<TypedefType>(ET->getNamedType().getTypePtr())) {
MustInsertAfterLoc = TDT->getDecl()->getBeginLoc();
} else if (auto *TD = ET->getAsTagDecl()) {
MustInsertAfterLoc = TD->getBeginLoc();
}
}
} }
case TypeLoc::Enum:
case TypeLoc::Record:
case TypeLoc::InjectedClassName: {
auto TL = T->castAs<TagTypeLoc>();
QualifierToRemove = TL.getQualifierLoc();
if (!QualifierToRemove)
break;
SpelledNameRange = TL.getNameLoc();
MustInsertAfterLoc = TL.getOriginalDecl()->getBeginLoc();
break;
}
case TypeLoc::Typedef: {
auto TL = T->castAs<TypedefTypeLoc>();
QualifierToRemove = TL.getQualifierLoc();
if (!QualifierToRemove)
break;
SpelledNameRange = TL.getNameLoc();
MustInsertAfterLoc = TL.getDecl()->getBeginLoc();
break;
}
case TypeLoc::UnresolvedUsing: {
auto TL = T->castAs<UnresolvedUsingTypeLoc>();
QualifierToRemove = TL.getQualifierLoc();
if (!QualifierToRemove)
break;
SpelledNameRange = TL.getNameLoc();
MustInsertAfterLoc = TL.getDecl()->getBeginLoc();
break;
}
case TypeLoc::Using: {
auto TL = T->castAs<UsingTypeLoc>();
QualifierToRemove = TL.getQualifierLoc();
if (!QualifierToRemove)
break;
SpelledNameRange = TL.getNameLoc();
MustInsertAfterLoc = TL.getDecl()->getBeginLoc();
break;
}
default:
break;
}
if (QualifierToRemove)
SpelledNameRange.setBegin(QualifierToRemove.getBeginLoc());
} }
if (!QualifierToRemove || if (!QualifierToRemove ||
// FIXME: This only supports removing qualifiers that are made up of just // FIXME: This only supports removing qualifiers that are made up of just
// namespace names. If qualifier contains a type, we could take the // namespace names. If qualifier contains a type, we could take the
// longest namespace prefix and remove that. // longest namespace prefix and remove that.
!QualifierToRemove.getNestedNameSpecifier()->getAsNamespace() || QualifierToRemove.getNestedNameSpecifier().getKind() !=
NestedNameSpecifier::Kind::Namespace ||
// Respect user config. // Respect user config.
isNamespaceForbidden(Inputs, *QualifierToRemove.getNestedNameSpecifier())) isNamespaceForbidden(Inputs, QualifierToRemove.getNestedNameSpecifier()))
return false; return false;
// Macros are difficult. We only want to offer code action when what's spelled // Macros are difficult. We only want to offer code action when what's spelled
// under the cursor is a namespace qualifier. If it's a macro that expands to // under the cursor is a namespace qualifier. If it's a macro that expands to
@ -384,7 +417,7 @@ Expected<Tweak::Effect> AddUsing::apply(const Selection &Inputs) {
llvm::raw_string_ostream UsingTextStream(UsingText); llvm::raw_string_ostream UsingTextStream(UsingText);
UsingTextStream << "using "; UsingTextStream << "using ";
if (InsertionPoint->AlwaysFullyQualify && if (InsertionPoint->AlwaysFullyQualify &&
!isFullyQualified(QualifierToRemove.getNestedNameSpecifier())) !QualifierToRemove.getNestedNameSpecifier().isFullyQualified())
UsingTextStream << "::"; UsingTextStream << "::";
UsingTextStream << QualifierToSpell << SpelledName << ";" UsingTextStream << QualifierToSpell << SpelledName << ";"
<< InsertionPoint->Suffix; << InsertionPoint->Suffix;

View File

@ -362,7 +362,7 @@ struct NewFunction {
SourceLocation DefinitionPoint; SourceLocation DefinitionPoint;
std::optional<SourceLocation> ForwardDeclarationPoint; std::optional<SourceLocation> ForwardDeclarationPoint;
const CXXRecordDecl *EnclosingClass = nullptr; const CXXRecordDecl *EnclosingClass = nullptr;
const NestedNameSpecifier *DefinitionQualifier = nullptr; NestedNameSpecifier DefinitionQualifier = std::nullopt;
const DeclContext *SemanticDC = nullptr; const DeclContext *SemanticDC = nullptr;
const DeclContext *SyntacticDC = nullptr; const DeclContext *SyntacticDC = nullptr;
const DeclContext *ForwardDeclarationSyntacticDC = nullptr; const DeclContext *ForwardDeclarationSyntacticDC = nullptr;
@ -455,13 +455,12 @@ std::string NewFunction::renderQualifiers() const {
} }
std::string NewFunction::renderDeclarationName(FunctionDeclKind K) const { std::string NewFunction::renderDeclarationName(FunctionDeclKind K) const {
if (DefinitionQualifier == nullptr || K != OutOfLineDefinition) { if (!DefinitionQualifier || K != OutOfLineDefinition)
return Name; return Name;
}
std::string QualifierName; std::string QualifierName;
llvm::raw_string_ostream Oss(QualifierName); llvm::raw_string_ostream Oss(QualifierName);
DefinitionQualifier->print(Oss, *LangOpts); DefinitionQualifier.print(Oss, *LangOpts);
return llvm::formatv("{0}{1}", QualifierName, Name); return llvm::formatv("{0}{1}", QualifierName, Name);
} }

View File

@ -116,7 +116,7 @@ bool PopulateSwitch::prepare(const Selection &Sel) {
EnumT = Cond->getType().getCanonicalType()->getAsAdjusted<EnumType>(); EnumT = Cond->getType().getCanonicalType()->getAsAdjusted<EnumType>();
if (!EnumT) if (!EnumT)
return false; return false;
EnumD = EnumT->getDecl(); EnumD = EnumT->getOriginalDecl();
if (!EnumD || EnumD->isDependentType()) if (!EnumD || EnumD->isDependentType())
return false; return false;

View File

@ -421,7 +421,7 @@ TEST(ClangdAST, GetQualification) {
{ {
R"cpp( R"cpp(
namespace ns1 { namespace ns2 { void Foo(); } } namespace ns1 { namespace ns2 { void Foo(); } }
void insert(); // ns2::Foo void insert(); // ns1::ns2::Foo
namespace ns1 { namespace ns1 {
void insert(); // ns2::Foo void insert(); // ns2::Foo
namespace ns2 { namespace ns2 {
@ -429,7 +429,7 @@ TEST(ClangdAST, GetQualification) {
} }
} }
)cpp", )cpp",
{"ns2::", "ns2::", ""}, {"ns1::ns2::", "ns2::", ""},
{"ns1::"}, {"ns1::"},
}, },
{ {
@ -531,7 +531,8 @@ TEST(ClangdAST, PrintType) {
ASSERT_EQ(InsertionPoints.size(), Case.Types.size()); ASSERT_EQ(InsertionPoints.size(), Case.Types.size());
for (size_t I = 0, E = InsertionPoints.size(); I != E; ++I) { for (size_t I = 0, E = InsertionPoints.size(); I != E; ++I) {
const auto *DC = InsertionPoints[I]; const auto *DC = InsertionPoints[I];
EXPECT_EQ(printType(AST.getASTContext().getTypeDeclType(TargetDecl), *DC), EXPECT_EQ(printType(AST.getASTContext().getTypeDeclType(TargetDecl), *DC,
/*Placeholder=*/"", /*FullyQualify=*/true),
Case.Types[I]); Case.Types[I]);
} }
} }

View File

@ -72,15 +72,14 @@ declaration: Namespace - root
expression: BinaryOperator - + expression: BinaryOperator - +
expression: ImplicitCast - LValueToRValue expression: ImplicitCast - LValueToRValue
expression: DeclRef - x expression: DeclRef - x
specifier: TypeSpec specifier: Type
type: Record - S type: Record - S
expression: ImplicitCast - LValueToRValue expression: ImplicitCast - LValueToRValue
expression: Member - x expression: Member - x
expression: CXXBindTemporary expression: CXXBindTemporary
expression: CXXTemporaryObject - S expression: CXXTemporaryObject - S
type: Elaborated type: Record - S
specifier: Namespace - root:: specifier: Namespace - root::
type: Record - S
)"}, )"},
{R"cpp( {R"cpp(
namespace root { namespace root {
@ -104,14 +103,13 @@ declaration: Namespace - root
expression: BinaryOperator - + expression: BinaryOperator - +
expression: ImplicitCast - LValueToRValue expression: ImplicitCast - LValueToRValue
expression: DeclRef - x expression: DeclRef - x
specifier: TypeSpec specifier: Type
type: Record - S type: Record - S
expression: ImplicitCast - LValueToRValue expression: ImplicitCast - LValueToRValue
expression: Member - x expression: Member - x
expression: CXXTemporaryObject - S expression: CXXTemporaryObject - S
type: Elaborated type: Record - S
specifier: Namespace - root:: specifier: Namespace - root::
type: Record - S
)"}, )"},
{R"cpp( {R"cpp(
namespace root { namespace root {
@ -138,7 +136,7 @@ declaration: Namespace - root
type: Builtin - unsigned int type: Builtin - unsigned int
statement: Return statement: Return
expression: DependentScopeDeclRef - value expression: DependentScopeDeclRef - value
specifier: TypeSpec specifier: Type
type: TemplateTypeParm - T type: TemplateTypeParm - T
)"}, )"},
{R"cpp( {R"cpp(
@ -154,8 +152,7 @@ declaration: Var - root
expression: DeclRef - operator+ expression: DeclRef - operator+
expression: MaterializeTemporary - lvalue expression: MaterializeTemporary - lvalue
expression: CXXTemporaryObject - Foo expression: CXXTemporaryObject - Foo
type: Elaborated type: Record - Foo
type: Record - Foo
expression: IntegerLiteral - 42 expression: IntegerLiteral - 42
)"}, )"},
{R"cpp( {R"cpp(

View File

@ -992,7 +992,7 @@ TEST_F(TargetDeclTest, DependentTypes) {
)cpp"; )cpp";
EXPECT_DECLS("DependentNameTypeLoc", "struct B"); EXPECT_DECLS("DependentNameTypeLoc", "struct B");
// Heuristic resolution of dependent type name which doesn't get a TypeLoc // Heuristic resolution of dependent type name within a NestedNameSpecifierLoc
Code = R"cpp( Code = R"cpp(
template <typename> template <typename>
struct A { struct B { struct C {}; }; }; struct A { struct B { struct C {}; }; };
@ -1000,7 +1000,7 @@ TEST_F(TargetDeclTest, DependentTypes) {
template <typename T> template <typename T>
void foo(typename A<T>::[[B]]::C); void foo(typename A<T>::[[B]]::C);
)cpp"; )cpp";
EXPECT_DECLS("NestedNameSpecifierLoc", "struct B"); EXPECT_DECLS("DependentNameTypeLoc", "struct B");
// Heuristic resolution of dependent type name whose qualifier is also // Heuristic resolution of dependent type name whose qualifier is also
// dependent // dependent

View File

@ -2894,7 +2894,7 @@ TEST(Hover, All) {
)cpp", )cpp",
[](HoverInfo &HI) { [](HoverInfo &HI) {
HI.Name = "this"; HI.Name = "this";
HI.Definition = "const Foo<T> *"; HI.Definition = "const ns::Foo<T> *";
}}, }},
{ {
R"cpp(// this expr for specialization class R"cpp(// this expr for specialization class
@ -2910,7 +2910,7 @@ TEST(Hover, All) {
)cpp", )cpp",
[](HoverInfo &HI) { [](HoverInfo &HI) {
HI.Name = "this"; HI.Name = "this";
HI.Definition = "Foo<int> *"; HI.Definition = "ns::Foo<int> *";
}}, }},
{ {
R"cpp(// this expr for partial specialization struct R"cpp(// this expr for partial specialization struct
@ -2926,7 +2926,7 @@ TEST(Hover, All) {
)cpp", )cpp",
[](HoverInfo &HI) { [](HoverInfo &HI) {
HI.Name = "this"; HI.Name = "this";
HI.Definition = "const Foo<int, F> *"; HI.Definition = "const ns::Foo<int, F> *";
}}, }},
{ {
R"cpp( R"cpp(
@ -3046,8 +3046,8 @@ TEST(Hover, All) {
HI.Kind = index::SymbolKind::Function; HI.Kind = index::SymbolKind::Function;
HI.NamespaceScope = ""; HI.NamespaceScope = "";
HI.Definition = "MyRect foobar()"; HI.Definition = "MyRect foobar()";
HI.Type = {"MyRect ()", "MyRect ()"}; HI.Type = {"MyRect ()", "struct MyRect ()"};
HI.ReturnType = {"MyRect", "MyRect"}; HI.ReturnType = {"MyRect", "struct MyRect"};
HI.Parameters.emplace(); HI.Parameters.emplace();
}}, }},
{R"cpp( {R"cpp(

View File

@ -1295,14 +1295,7 @@ TEST(TypeHints, NoQualifiers) {
} }
} }
)cpp", )cpp",
ExpectedHint{": S1", "x"}, ExpectedHint{": S1", "x"}, ExpectedHint{": Inner<int>", "y"});
// FIXME: We want to suppress scope specifiers
// here because we are into the whole
// brevity thing, but the ElaboratedType
// printer does not honor the SuppressScope
// flag by design, so we need to extend the
// PrintingPolicy to support this use case.
ExpectedHint{": S2::Inner<int>", "y"});
} }
TEST(TypeHints, Lambda) { TEST(TypeHints, Lambda) {

View File

@ -121,7 +121,9 @@ TEST(QualityTests, SymbolRelevanceSignalExtraction) {
SymbolRelevanceSignals Relevance; SymbolRelevanceSignals Relevance;
Relevance.merge(CodeCompletionResult(&findDecl(AST, "deprecated"), Relevance.merge(CodeCompletionResult(&findDecl(AST, "deprecated"),
/*Priority=*/42, nullptr, false, /*Priority=*/42,
/*Qualifier=*/std::nullopt,
/*QualifierIsInformative=*/false,
/*Accessible=*/false)); /*Accessible=*/false));
EXPECT_EQ(Relevance.NameMatch, SymbolRelevanceSignals().NameMatch); EXPECT_EQ(Relevance.NameMatch, SymbolRelevanceSignals().NameMatch);
EXPECT_TRUE(Relevance.Forbidden); EXPECT_TRUE(Relevance.Forbidden);
@ -487,13 +489,15 @@ TEST(QualityTests, ItemWithFixItsRankedDown) {
auto AST = Header.build(); auto AST = Header.build();
SymbolRelevanceSignals RelevanceWithFixIt; SymbolRelevanceSignals RelevanceWithFixIt;
RelevanceWithFixIt.merge(CodeCompletionResult(&findDecl(AST, "x"), 0, nullptr, RelevanceWithFixIt.merge(CodeCompletionResult(
false, true, {FixItHint{}})); &findDecl(AST, "x"), /*Priority=*/0, /*Qualifier=*/std::nullopt,
/*QualifierIsInformative=*/false, /*Accessible=*/true, {FixItHint{}}));
EXPECT_TRUE(RelevanceWithFixIt.NeedsFixIts); EXPECT_TRUE(RelevanceWithFixIt.NeedsFixIts);
SymbolRelevanceSignals RelevanceWithoutFixIt; SymbolRelevanceSignals RelevanceWithoutFixIt;
RelevanceWithoutFixIt.merge( RelevanceWithoutFixIt.merge(CodeCompletionResult(
CodeCompletionResult(&findDecl(AST, "x"), 0, nullptr, false, true, {})); &findDecl(AST, "x"), /*Priority=*/0, /*Qualifier=*/std::nullopt,
/*QualifierIsInformative=*/false, /*Accessible=*/true, {}));
EXPECT_FALSE(RelevanceWithoutFixIt.NeedsFixIts); EXPECT_FALSE(RelevanceWithoutFixIt.NeedsFixIts);
EXPECT_LT(RelevanceWithFixIt.evaluateHeuristics(), EXPECT_LT(RelevanceWithFixIt.evaluateHeuristics(),

View File

@ -104,9 +104,9 @@ TEST(SelectionTest, CommonAncestor) {
{ {
R"cpp( R"cpp(
template <typename T> template <typename T>
int x = [[T::^U::]]ccc(); int x = T::[[^U]]::ccc();
)cpp", )cpp",
"NestedNameSpecifierLoc", "DependentNameTypeLoc",
}, },
{ {
R"cpp( R"cpp(

View File

@ -60,14 +60,10 @@ class ASTWalker : public RecursiveASTVisitor<ASTWalker> {
NamedDecl *getMemberProvider(QualType Base) { NamedDecl *getMemberProvider(QualType Base) {
if (Base->isPointerType()) if (Base->isPointerType())
return getMemberProvider(Base->getPointeeType()); return getMemberProvider(Base->getPointeeType());
// Unwrap the sugar ElaboratedType.
if (const auto *ElTy = dyn_cast<ElaboratedType>(Base))
return getMemberProvider(ElTy->getNamedType());
if (const auto *TT = dyn_cast<TypedefType>(Base)) if (const auto *TT = dyn_cast<TypedefType>(Base))
return TT->getDecl(); return TT->getDecl();
if (const auto *UT = dyn_cast<UsingType>(Base)) if (const auto *UT = dyn_cast<UsingType>(Base))
return UT->getFoundDecl(); return UT->getDecl();
// A heuristic: to resolve a template type to **only** its template name. // A heuristic: to resolve a template type to **only** its template name.
// We're only using this method for the base type of MemberExpr, in general // We're only using this method for the base type of MemberExpr, in general
// the template provides the member, and the critical case `unique_ptr<Foo>` // the template provides the member, and the critical case `unique_ptr<Foo>`
@ -135,16 +131,14 @@ public:
} }
bool qualifierIsNamespaceOrNone(DeclRefExpr *DRE) { bool qualifierIsNamespaceOrNone(DeclRefExpr *DRE) {
const auto *Qual = DRE->getQualifier(); NestedNameSpecifier Qual = DRE->getQualifier();
if (!Qual) switch (Qual.getKind()) {
case NestedNameSpecifier::Kind::Null:
case NestedNameSpecifier::Kind::Namespace:
case NestedNameSpecifier::Kind::Global:
return true; return true;
switch (Qual->getKind()) { case NestedNameSpecifier::Kind::Type:
case NestedNameSpecifier::Namespace: case NestedNameSpecifier::Kind::MicrosoftSuper:
case NestedNameSpecifier::Global:
return true;
case NestedNameSpecifier::TypeSpec:
case NestedNameSpecifier::Super:
case NestedNameSpecifier::Identifier:
return false; return false;
} }
llvm_unreachable("Unknown value for NestedNameSpecifierKind"); llvm_unreachable("Unknown value for NestedNameSpecifierKind");
@ -341,17 +335,17 @@ public:
} }
bool VisitUsingTypeLoc(UsingTypeLoc TL) { bool VisitUsingTypeLoc(UsingTypeLoc TL) {
reportType(TL.getNameLoc(), TL.getFoundDecl()); reportType(TL.getNameLoc(), TL.getDecl());
return true; return true;
} }
bool VisitTagTypeLoc(TagTypeLoc TTL) { bool VisitTagTypeLoc(TagTypeLoc TTL) {
reportType(TTL.getNameLoc(), TTL.getDecl()); reportType(TTL.getNameLoc(), TTL.getOriginalDecl());
return true; return true;
} }
bool VisitTypedefTypeLoc(TypedefTypeLoc TTL) { bool VisitTypedefTypeLoc(TypedefTypeLoc TTL) {
reportType(TTL.getNameLoc(), TTL.getTypedefNameDecl()); reportType(TTL.getNameLoc(), TTL.getDecl());
return true; return true;
} }

View File

@ -165,7 +165,6 @@ FROMMACRO
class X15 : public CopyableAlias2 { class X15 : public CopyableAlias2 {
X15(const X15 &other) {} X15(const X15 &other) {}
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: calling a base constructor // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: calling a base constructor
// CHECK-FIXES: X15(const X15 &other) : Copyable5(other) {}
}; };
class X16 : public NonCopyable { class X16 : public NonCopyable {

View File

@ -69,14 +69,14 @@ template<typename T>
T qux(T Generic) { T qux(T Generic) {
async::Future<Units> PendingA = acquireUnits(); async::Future<Units> PendingA = acquireUnits();
auto PendingB = acquireUnits(); auto PendingB = acquireUnits();
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: unused local variable 'PendingB' of type 'a::Future<Units>' (aka 'Future<Units>') [bugprone-unused-local-non-trivial-variable] // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: unused local variable 'PendingB' of type 'a::Future<Units>' (aka 'async::Future<Units>') [bugprone-unused-local-non-trivial-variable]
async::Future<Units> MustBeUsed; async::Future<Units> MustBeUsed;
// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: unused local variable 'MustBeUsed' of type 'async::Future<Units>' [bugprone-unused-local-non-trivial-variable] // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: unused local variable 'MustBeUsed' of type 'async::Future<Units>' [bugprone-unused-local-non-trivial-variable]
PendingA.get(); PendingA.get();
async::Future<T> TemplateType; async::Future<T> TemplateType;
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: unused local variable 'TemplateType' of type 'async::Future<T>' [bugprone-unused-local-non-trivial-variable] // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: unused local variable 'TemplateType' of type 'async::Future<T>' [bugprone-unused-local-non-trivial-variable]
a::Future<T> AliasTemplateType; a::Future<T> AliasTemplateType;
// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: unused local variable 'AliasTemplateType' of type 'a::Future<T>' (aka 'Future<T>') [bugprone-unused-local-non-trivial-variable] // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: unused local variable 'AliasTemplateType' of type 'a::Future<T>' (aka 'async::Future<T>') [bugprone-unused-local-non-trivial-variable]
[[maybe_unused]] async::Future<Units> MaybeUnused; [[maybe_unused]] async::Future<Units> MaybeUnused;
return Generic; return Generic;
} }
@ -86,7 +86,7 @@ async::Future<int> Global;
int bar(int Num) { int bar(int Num) {
a::Future<Units> PendingA = acquireUnits(); a::Future<Units> PendingA = acquireUnits();
a::Future<Units> PendingB = acquireUnits(); // not used at all, unused variable not fired because of destructor side effect a::Future<Units> PendingB = acquireUnits(); // not used at all, unused variable not fired because of destructor side effect
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: unused local variable 'PendingB' of type 'a::Future<Units>' (aka 'Future<Units>') [bugprone-unused-local-non-trivial-variable] // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: unused local variable 'PendingB' of type 'a::Future<Units>' (aka 'async::Future<Units>') [bugprone-unused-local-non-trivial-variable]
auto Num2 = PendingA.get(); auto Num2 = PendingA.get();
auto Num3 = qux(Num); auto Num3 = qux(Num);
async::Ptr<a::Future<Units>> Shared = async::Ptr<a::Future<Units>>(acquireUnits()); async::Ptr<a::Future<Units>> Shared = async::Ptr<a::Future<Units>>(acquireUnits());

View File

@ -43,25 +43,25 @@ template <class T>
class allocator {}; class allocator {};
void simple(const std::vector<const char> &v, std::deque<const short> *d) { void simple(const std::vector<const char> &v, std::deque<const short> *d) {
// CHECK-MESSAGES: [[#@LINE-1]]:24: warning: container using std::allocator<const T> is a deprecated libc++ extension; remove const for compatibility with other standard libraries // CHECK-MESSAGES: [[#@LINE-1]]:19: warning: container using std::allocator<const T> is a deprecated libc++ extension; remove const for compatibility with other standard libraries
// CHECK-MESSAGES: [[#@LINE-2]]:52: warning: container // CHECK-MESSAGES: [[#@LINE-2]]:47: warning: container
std::list<const long> l; std::list<const long> l;
// CHECK-MESSAGES: [[#@LINE-1]]:8: warning: container // CHECK-MESSAGES: [[#@LINE-1]]:3: warning: container
std::multiset<int *const> ms; std::multiset<int *const> ms;
// CHECK-MESSAGES: [[#@LINE-1]]:8: warning: container // CHECK-MESSAGES: [[#@LINE-1]]:3: warning: container
std::set<const std::hash<int>> s; std::set<const std::hash<int>> s;
// CHECK-MESSAGES: [[#@LINE-1]]:8: warning: container // CHECK-MESSAGES: [[#@LINE-1]]:3: warning: container
std::unordered_multiset<int *const> ums; std::unordered_multiset<int *const> ums;
// CHECK-MESSAGES: [[#@LINE-1]]:8: warning: container // CHECK-MESSAGES: [[#@LINE-1]]:3: warning: container
std::unordered_set<const int> us; std::unordered_set<const int> us;
// CHECK-MESSAGES: [[#@LINE-1]]:8: warning: container // CHECK-MESSAGES: [[#@LINE-1]]:3: warning: container
absl::flat_hash_set<const int> fhs; absl::flat_hash_set<const int> fhs;
// CHECK-MESSAGES: [[#@LINE-1]]:9: warning: container // CHECK-MESSAGES: [[#@LINE-1]]:3: warning: container
using my_vector = std::vector<const int>; using my_vector = std::vector<const int>;
// CHECK-MESSAGES: [[#@LINE-1]]:26: warning: container // CHECK-MESSAGES: [[#@LINE-1]]:21: warning: container
my_vector v1; my_vector v1;
using my_vector2 = my_vector; using my_vector2 = my_vector;
@ -76,7 +76,7 @@ void simple(const std::vector<const char> &v, std::deque<const short> *d) {
template <class T> template <class T>
void temp1() { void temp1() {
std::vector<const T> v; std::vector<const T> v;
// CHECK-MESSAGES: [[#@LINE-1]]:8: warning: container // CHECK-MESSAGES: [[#@LINE-1]]:3: warning: container
std::vector<T> neg1; std::vector<T> neg1;
std::forward_list<const T> neg2; std::forward_list<const T> neg2;
@ -87,7 +87,7 @@ template <class T>
void temp2() { void temp2() {
// Match std::vector<const dependent> for the uninstantiated temp2. // Match std::vector<const dependent> for the uninstantiated temp2.
std::vector<const T> v; std::vector<const T> v;
// CHECK-MESSAGES: [[#@LINE-1]]:8: warning: container // CHECK-MESSAGES: [[#@LINE-1]]:3: warning: container
std::vector<T> neg1; std::vector<T> neg1;
std::forward_list<const T> neg2; std::forward_list<const T> neg2;

View File

@ -63,7 +63,7 @@ class TestType(unittest.TestCase):
self.assertIsNotNone(fields[1].translation_unit) self.assertIsNotNone(fields[1].translation_unit)
self.assertEqual(fields[1].spelling, "b") self.assertEqual(fields[1].spelling, "b")
self.assertFalse(fields[1].type.is_const_qualified()) self.assertFalse(fields[1].type.is_const_qualified())
self.assertEqual(fields[1].type.kind, TypeKind.ELABORATED) self.assertEqual(fields[1].type.kind, TypeKind.TYPEDEF)
self.assertEqual(fields[1].type.get_canonical().kind, TypeKind.INT) self.assertEqual(fields[1].type.get_canonical().kind, TypeKind.INT)
self.assertEqual(fields[1].type.get_declaration().spelling, "I") self.assertEqual(fields[1].type.get_declaration().spelling, "I")
self.assertEqual(fields[1].type.get_typedef_name(), "I") self.assertEqual(fields[1].type.get_typedef_name(), "I")

View File

@ -70,12 +70,16 @@ ABI Changes in This Version
AST Dumping Potentially Breaking Changes AST Dumping Potentially Breaking Changes
---------------------------------------- ----------------------------------------
- How nested name specifiers are dumped and printed changes, keeping track of clang AST changes.
Clang Frontend Potentially Breaking Changes Clang Frontend Potentially Breaking Changes
------------------------------------------- -------------------------------------------
Clang Python Bindings Potentially Breaking Changes Clang Python Bindings Potentially Breaking Changes
-------------------------------------------------- --------------------------------------------------
- TypeKind ``ELABORATED`` is not used anymore, per clang AST changes removing
ElaboratedTypes. The value becomes unused, and all the existing users should
expect the former underlying type to be reported instead.
What's New in Clang |release|? What's New in Clang |release|?
============================== ==============================
@ -187,6 +191,9 @@ Bug Fixes to C++ Support
Bug Fixes to AST Handling Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^
- Fix incorrect name qualifiers applied to alias CTAD. (#GH136624)
- Fixed ElaboratedTypes appearing within NestedNameSpecifier, which was not a
legal representation. This is fixed because ElaboratedTypes don't exist anymore. (#GH43179) (#GH68670) (#GH92757)
Miscellaneous Bug Fixes Miscellaneous Bug Fixes
^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
@ -256,6 +263,8 @@ Fixed Point Support in Clang
AST Matchers AST Matchers
------------ ------------
- Removed elaboratedType matchers, and related nested name specifier changes,
following the corresponding changes in the clang AST.
- Ensure ``hasBitWidth`` doesn't crash on bit widths that are dependent on template - Ensure ``hasBitWidth`` doesn't crash on bit widths that are dependent on template
parameters. parameters.

View File

@ -15,7 +15,7 @@
#define LLVM_CLANG_AST_ASTCONCEPT_H #define LLVM_CLANG_AST_ASTCONCEPT_H
#include "clang/AST/DeclarationName.h" #include "clang/AST/DeclarationName.h"
#include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/NestedNameSpecifierBase.h"
#include "clang/AST/TemplateBase.h" #include "clang/AST/TemplateBase.h"
#include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceLocation.h"
#include "clang/Basic/UnsignedOrNone.h" #include "clang/Basic/UnsignedOrNone.h"
@ -177,12 +177,7 @@ public:
SourceLocation getLocation() const { return getConceptNameLoc(); } SourceLocation getLocation() const { return getConceptNameLoc(); }
SourceLocation getBeginLoc() const LLVM_READONLY { SourceLocation getBeginLoc() const LLVM_READONLY;
// Note that if the qualifier is null the template KW must also be null.
if (auto QualifierLoc = getNestedNameSpecifierLoc())
return QualifierLoc.getBeginLoc();
return getConceptNameInfo().getBeginLoc();
}
SourceLocation getEndLoc() const LLVM_READONLY { SourceLocation getEndLoc() const LLVM_READONLY {
return getTemplateArgsAsWritten() && return getTemplateArgsAsWritten() &&

View File

@ -233,10 +233,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
mutable llvm::ContextualFoldingSet<TemplateSpecializationType, ASTContext&> mutable llvm::ContextualFoldingSet<TemplateSpecializationType, ASTContext&>
TemplateSpecializationTypes; TemplateSpecializationTypes;
mutable llvm::FoldingSet<ParenType> ParenTypes{GeneralTypesLog2InitSize}; mutable llvm::FoldingSet<ParenType> ParenTypes{GeneralTypesLog2InitSize};
mutable llvm::FoldingSet<TagTypeFoldingSetPlaceholder> TagTypes;
mutable llvm::FoldingSet<FoldingSetPlaceholder<UnresolvedUsingType>>
UnresolvedUsingTypes;
mutable llvm::FoldingSet<UsingType> UsingTypes; mutable llvm::FoldingSet<UsingType> UsingTypes;
mutable llvm::FoldingSet<TypedefType> TypedefTypes; mutable llvm::FoldingSet<FoldingSetPlaceholder<TypedefType>> TypedefTypes;
mutable llvm::FoldingSet<ElaboratedType> ElaboratedTypes{
GeneralTypesLog2InitSize};
mutable llvm::FoldingSet<DependentNameType> DependentNameTypes; mutable llvm::FoldingSet<DependentNameType> DependentNameTypes;
mutable llvm::DenseMap<llvm::FoldingSetNodeID, mutable llvm::DenseMap<llvm::FoldingSetNodeID,
DependentTemplateSpecializationType *> DependentTemplateSpecializationType *>
@ -282,11 +283,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
llvm::to_underlying(PredefinedSugarType::Kind::Last) + 1> llvm::to_underlying(PredefinedSugarType::Kind::Last) + 1>
PredefinedSugarTypes{}; PredefinedSugarTypes{};
/// The set of nested name specifiers. /// Internal storage for NestedNameSpecifiers.
/// ///
/// This set is managed by the NestedNameSpecifier class. /// This set is managed by the NestedNameSpecifier class.
mutable llvm::FoldingSet<NestedNameSpecifier> NestedNameSpecifiers; mutable llvm::FoldingSet<NamespaceAndPrefixStorage>
mutable NestedNameSpecifier *GlobalNestedNameSpecifier = nullptr; NamespaceAndPrefixStorages;
/// A cache mapping from RecordDecls to ASTRecordLayouts. /// A cache mapping from RecordDecls to ASTRecordLayouts.
/// ///
@ -1386,8 +1387,6 @@ private:
/// Return a type with extended qualifiers. /// Return a type with extended qualifiers.
QualType getExtQualType(const Type *Base, Qualifiers Quals) const; QualType getExtQualType(const Type *Base, Qualifiers Quals) const;
QualType getTypeDeclTypeSlow(const TypeDecl *Decl) const;
QualType getPipeType(QualType T, bool ReadOnly) const; QualType getPipeType(QualType T, bool ReadOnly) const;
public: public:
@ -1630,7 +1629,7 @@ public:
/// Return the uniqued reference to the type for a member pointer to /// Return the uniqued reference to the type for a member pointer to
/// the specified type in the specified nested name. /// the specified type in the specified nested name.
QualType getMemberPointerType(QualType T, NestedNameSpecifier *Qualifier, QualType getMemberPointerType(QualType T, NestedNameSpecifier Qualifier,
const CXXRecordDecl *Cls) const; const CXXRecordDecl *Cls) const;
/// Return a non-unique reference to the type for a variable array of /// Return a non-unique reference to the type for a variable array of
@ -1767,34 +1766,53 @@ private:
bool IsCanon = false) const; bool IsCanon = false) const;
public: public:
QualType getTypeDeclType(ElaboratedTypeKeyword Keyword,
NestedNameSpecifier Qualifier,
const TypeDecl *Decl) const;
/// Return the unique reference to the type for the specified type /// Return the unique reference to the type for the specified type
/// declaration. /// declaration.
QualType getTypeDeclType(const TypeDecl *Decl, QualType getTypeDeclType(const TypeDecl *Decl) const;
const TypeDecl *PrevDecl = nullptr) const {
assert(Decl && "Passed null for Decl param");
if (Decl->TypeForDecl) return QualType(Decl->TypeForDecl, 0);
if (PrevDecl) { /// Use the normal 'getFooBarType' constructors to obtain these types.
assert(PrevDecl->TypeForDecl && "previous decl has no TypeForDecl"); QualType getTypeDeclType(const TagDecl *) const = delete;
Decl->TypeForDecl = PrevDecl->TypeForDecl; QualType getTypeDeclType(const TypedefDecl *) const = delete;
return QualType(PrevDecl->TypeForDecl, 0); QualType getTypeDeclType(const TypeAliasDecl *) const = delete;
} QualType getTypeDeclType(const UnresolvedUsingTypenameDecl *) const = delete;
return getTypeDeclTypeSlow(Decl); CanQualType getCanonicalTypeDeclType(const TypeDecl *TD) const;
}
QualType getUsingType(const UsingShadowDecl *Found, QualType getUsingType(ElaboratedTypeKeyword Keyword,
QualType Underlying) const; NestedNameSpecifier Qualifier, const UsingShadowDecl *D,
QualType UnderlyingType = QualType()) const;
/// Return the unique reference to the type for the specified /// Return the unique reference to the type for the specified
/// typedef-name decl. /// typedef-name decl.
QualType getTypedefType(const TypedefNameDecl *Decl, /// FIXME: TypeMatchesDeclOrNone is a workaround for a serialization issue:
QualType Underlying = QualType()) const; /// The decl underlying type might still not be available.
QualType getTypedefType(
ElaboratedTypeKeyword Keyword, NestedNameSpecifier Qualifier,
const TypedefNameDecl *Decl, QualType UnderlyingType = QualType(),
std::optional<bool> TypeMatchesDeclOrNone = std::nullopt) const;
QualType getRecordType(const RecordDecl *Decl) const; CanQualType getCanonicalTagType(const TagDecl *TD) const;
QualType getTagType(ElaboratedTypeKeyword Keyword,
NestedNameSpecifier Qualifier, const TagDecl *TD,
bool OwnsTag) const;
QualType getEnumType(const EnumDecl *Decl) const; private:
UnresolvedUsingType *getUnresolvedUsingTypeInternal(
ElaboratedTypeKeyword Keyword, NestedNameSpecifier Qualifier,
const UnresolvedUsingTypenameDecl *D, void *InsertPos,
const Type *CanonicalType) const;
TagType *getTagTypeInternal(ElaboratedTypeKeyword Keyword,
NestedNameSpecifier Qualifier, const TagDecl *Tag,
bool OwnsTag, bool IsInjected,
const Type *CanonicalType,
bool WithFoldingSetNode) const;
public:
/// Compute BestType and BestPromotionType for an enum based on the highest /// Compute BestType and BestPromotionType for an enum based on the highest
/// number of negative and positive bits of its elements. /// number of negative and positive bits of its elements.
/// Returns true if enum width is too large. /// Returns true if enum width is too large.
@ -1843,10 +1861,11 @@ public:
return MembersRepresentableByInt; return MembersRepresentableByInt;
} }
QualType CanQualType
getUnresolvedUsingType(const UnresolvedUsingTypenameDecl *Decl) const; getCanonicalUnresolvedUsingType(const UnresolvedUsingTypenameDecl *D) const;
QualType getUnresolvedUsingType(ElaboratedTypeKeyword Keyword,
QualType getInjectedClassNameType(CXXRecordDecl *Decl, QualType TST) const; NestedNameSpecifier Qualifier,
const UnresolvedUsingTypenameDecl *D) const;
QualType getAttributedType(attr::Kind attrKind, QualType modifiedType, QualType getAttributedType(attr::Kind attrKind, QualType modifiedType,
QualType equivalentType, QualType equivalentType,
@ -1886,18 +1905,20 @@ public:
TemplateName T, ArrayRef<TemplateArgument> CanonicalArgs) const; TemplateName T, ArrayRef<TemplateArgument> CanonicalArgs) const;
QualType QualType
getTemplateSpecializationType(TemplateName T, getTemplateSpecializationType(ElaboratedTypeKeyword Keyword, TemplateName T,
ArrayRef<TemplateArgument> SpecifiedArgs, ArrayRef<TemplateArgument> SpecifiedArgs,
ArrayRef<TemplateArgument> CanonicalArgs, ArrayRef<TemplateArgument> CanonicalArgs,
QualType Underlying = QualType()) const; QualType Underlying = QualType()) const;
QualType QualType
getTemplateSpecializationType(TemplateName T, getTemplateSpecializationType(ElaboratedTypeKeyword Keyword, TemplateName T,
ArrayRef<TemplateArgumentLoc> SpecifiedArgs, ArrayRef<TemplateArgumentLoc> SpecifiedArgs,
ArrayRef<TemplateArgument> CanonicalArgs, ArrayRef<TemplateArgument> CanonicalArgs,
QualType Canon = QualType()) const; QualType Canon = QualType()) const;
TypeSourceInfo *getTemplateSpecializationTypeInfo( TypeSourceInfo *getTemplateSpecializationTypeInfo(
ElaboratedTypeKeyword Keyword, SourceLocation ElaboratedKeywordLoc,
NestedNameSpecifierLoc QualifierLoc, SourceLocation TemplateKeywordLoc,
TemplateName T, SourceLocation TLoc, TemplateName T, SourceLocation TLoc,
const TemplateArgumentListInfo &SpecifiedArgs, const TemplateArgumentListInfo &SpecifiedArgs,
ArrayRef<TemplateArgument> CanonicalArgs, ArrayRef<TemplateArgument> CanonicalArgs,
@ -1908,11 +1929,8 @@ public:
QualType getMacroQualifiedType(QualType UnderlyingTy, QualType getMacroQualifiedType(QualType UnderlyingTy,
const IdentifierInfo *MacroII) const; const IdentifierInfo *MacroII) const;
QualType getElaboratedType(ElaboratedTypeKeyword Keyword,
NestedNameSpecifier *NNS, QualType NamedType,
TagDecl *OwnedTagDecl = nullptr) const;
QualType getDependentNameType(ElaboratedTypeKeyword Keyword, QualType getDependentNameType(ElaboratedTypeKeyword Keyword,
NestedNameSpecifier *NNS, NestedNameSpecifier NNS,
const IdentifierInfo *Name) const; const IdentifierInfo *Name) const;
QualType getDependentTemplateSpecializationType( QualType getDependentTemplateSpecializationType(
@ -1999,21 +2017,17 @@ public:
QualType getUnconstrainedType(QualType T) const; QualType getUnconstrainedType(QualType T) const;
/// C++17 deduced class template specialization type. /// C++17 deduced class template specialization type.
QualType getDeducedTemplateSpecializationType(TemplateName Template, QualType getDeducedTemplateSpecializationType(ElaboratedTypeKeyword Keyword,
TemplateName Template,
QualType DeducedType, QualType DeducedType,
bool IsDependent) const; bool IsDependent) const;
private: private:
QualType getDeducedTemplateSpecializationTypeInternal(TemplateName Template, QualType getDeducedTemplateSpecializationTypeInternal(
QualType DeducedType, ElaboratedTypeKeyword Keyword, TemplateName Template,
bool IsDependent, QualType DeducedType, bool IsDependent, QualType Canon) const;
QualType Canon) const;
public: public:
/// Return the unique reference to the type for the specified TagDecl
/// (struct/union/class/enum) decl.
QualType getTagDeclType(const TagDecl *Decl) const;
/// Return the unique type for "size_t" (C99 7.17), defined in /// Return the unique type for "size_t" (C99 7.17), defined in
/// <stddef.h>. /// <stddef.h>.
/// ///
@ -2089,7 +2103,9 @@ public:
/// if it hasn't yet been built. /// if it hasn't yet been built.
QualType getRawCFConstantStringType() const { QualType getRawCFConstantStringType() const {
if (CFConstantStringTypeDecl) if (CFConstantStringTypeDecl)
return getTypedefType(CFConstantStringTypeDecl); return getTypedefType(ElaboratedTypeKeyword::None,
/*Qualifier=*/std::nullopt,
CFConstantStringTypeDecl);
return QualType(); return QualType();
} }
void setCFConstantStringType(QualType T); void setCFConstantStringType(QualType T);
@ -2186,10 +2202,11 @@ public:
} }
#include "clang/Basic/BuiltinTemplates.inc" #include "clang/Basic/BuiltinTemplates.inc"
/// Retrieve the Objective-C "instancetype" type, if already known; /// Retrieve the Objective-C "instancetype" type.
/// otherwise, returns a NULL type;
QualType getObjCInstanceType() { QualType getObjCInstanceType() {
return getTypeDeclType(getObjCInstanceTypeDecl()); return getTypedefType(ElaboratedTypeKeyword::None,
/*Qualifier=*/std::nullopt,
getObjCInstanceTypeDecl());
} }
/// Retrieve the typedef declaration corresponding to the Objective-C /// Retrieve the typedef declaration corresponding to the Objective-C
@ -2202,7 +2219,8 @@ public:
/// Retrieve the C FILE type. /// Retrieve the C FILE type.
QualType getFILEType() const { QualType getFILEType() const {
if (FILEDecl) if (FILEDecl)
return getTypeDeclType(FILEDecl); return getTypeDeclType(ElaboratedTypeKeyword::None,
/*Qualifier=*/std::nullopt, FILEDecl);
return QualType(); return QualType();
} }
@ -2214,7 +2232,8 @@ public:
/// Retrieve the C jmp_buf type. /// Retrieve the C jmp_buf type.
QualType getjmp_bufType() const { QualType getjmp_bufType() const {
if (jmp_bufDecl) if (jmp_bufDecl)
return getTypeDeclType(jmp_bufDecl); return getTypeDeclType(ElaboratedTypeKeyword::None,
/*Qualifier=*/std::nullopt, jmp_bufDecl);
return QualType(); return QualType();
} }
@ -2226,7 +2245,8 @@ public:
/// Retrieve the C sigjmp_buf type. /// Retrieve the C sigjmp_buf type.
QualType getsigjmp_bufType() const { QualType getsigjmp_bufType() const {
if (sigjmp_bufDecl) if (sigjmp_bufDecl)
return getTypeDeclType(sigjmp_bufDecl); return getTypeDeclType(ElaboratedTypeKeyword::None,
/*Qualifier=*/std::nullopt, sigjmp_bufDecl);
return QualType(); return QualType();
} }
@ -2238,12 +2258,13 @@ public:
/// Retrieve the C ucontext_t type. /// Retrieve the C ucontext_t type.
QualType getucontext_tType() const { QualType getucontext_tType() const {
if (ucontext_tDecl) if (ucontext_tDecl)
return getTypeDeclType(ucontext_tDecl); return getTypeDeclType(ElaboratedTypeKeyword::None,
/*Qualifier=*/std::nullopt, ucontext_tDecl);
return QualType(); return QualType();
} }
/// The result type of logical operations, '<', '>', '!=', etc. /// The result type of logical operations, '<', '>', '!=', etc.
QualType getLogicalOperationType() const { CanQualType getLogicalOperationType() const {
return getLangOpts().CPlusPlus ? BoolTy : IntTy; return getLangOpts().CPlusPlus ? BoolTy : IntTy;
} }
@ -2308,7 +2329,8 @@ public:
/// This is set up lazily, by Sema. \c id is always a (typedef for a) /// This is set up lazily, by Sema. \c id is always a (typedef for a)
/// pointer type, a pointer to a struct. /// pointer type, a pointer to a struct.
QualType getObjCIdType() const { QualType getObjCIdType() const {
return getTypeDeclType(getObjCIdDecl()); return getTypedefType(ElaboratedTypeKeyword::None,
/*Qualifier=*/std::nullopt, getObjCIdDecl());
} }
/// Retrieve the typedef corresponding to the predefined 'SEL' type /// Retrieve the typedef corresponding to the predefined 'SEL' type
@ -2318,7 +2340,8 @@ public:
/// Retrieve the type that corresponds to the predefined Objective-C /// Retrieve the type that corresponds to the predefined Objective-C
/// 'SEL' type. /// 'SEL' type.
QualType getObjCSelType() const { QualType getObjCSelType() const {
return getTypeDeclType(getObjCSelDecl()); return getTypedefType(ElaboratedTypeKeyword::None,
/*Qualifier=*/std::nullopt, getObjCSelDecl());
} }
PointerAuthQualifier getObjCMemberSelTypePtrAuth(); PointerAuthQualifier getObjCMemberSelTypePtrAuth();
@ -2332,7 +2355,8 @@ public:
/// This is set up lazily, by Sema. \c Class is always a (typedef for a) /// This is set up lazily, by Sema. \c Class is always a (typedef for a)
/// pointer type, a pointer to a struct. /// pointer type, a pointer to a struct.
QualType getObjCClassType() const { QualType getObjCClassType() const {
return getTypeDeclType(getObjCClassDecl()); return getTypedefType(ElaboratedTypeKeyword::None,
/*Qualifier=*/std::nullopt, getObjCClassDecl());
} }
/// Retrieve the Objective-C class declaration corresponding to /// Retrieve the Objective-C class declaration corresponding to
@ -2351,7 +2375,8 @@ public:
/// type of 'BOOL' type. /// type of 'BOOL' type.
QualType getBOOLType() const { QualType getBOOLType() const {
return getTypeDeclType(getBOOLDecl()); return getTypedefType(ElaboratedTypeKeyword::None,
/*Qualifier=*/std::nullopt, getBOOLDecl());
} }
/// Retrieve the type of the Objective-C \c Protocol class. /// Retrieve the type of the Objective-C \c Protocol class.
@ -2365,7 +2390,8 @@ public:
/// Retrieve the type of the \c __builtin_va_list type. /// Retrieve the type of the \c __builtin_va_list type.
QualType getBuiltinVaListType() const { QualType getBuiltinVaListType() const {
return getTypeDeclType(getBuiltinVaListDecl()); return getTypedefType(ElaboratedTypeKeyword::None,
/*Qualifier=*/std::nullopt, getBuiltinVaListDecl());
} }
/// Retrieve the C type declaration corresponding to the predefined /// Retrieve the C type declaration corresponding to the predefined
@ -2379,16 +2405,17 @@ public:
/// Retrieve the type of the \c __builtin_ms_va_list type. /// Retrieve the type of the \c __builtin_ms_va_list type.
QualType getBuiltinMSVaListType() const { QualType getBuiltinMSVaListType() const {
return getTypeDeclType(getBuiltinMSVaListDecl()); return getTypedefType(ElaboratedTypeKeyword::None,
/*Qualifier=*/std::nullopt, getBuiltinMSVaListDecl());
} }
/// Retrieve the implicitly-predeclared 'struct _GUID' declaration. /// Retrieve the implicitly-predeclared 'struct _GUID' declaration.
TagDecl *getMSGuidTagDecl() const { return MSGuidTagDecl; } TagDecl *getMSGuidTagDecl() const { return MSGuidTagDecl; }
/// Retrieve the implicitly-predeclared 'struct _GUID' type. /// Retrieve the implicitly-predeclared 'struct _GUID' type.
QualType getMSGuidType() const { CanQualType getMSGuidType() const {
assert(MSGuidTagDecl && "asked for GUID type but MS extensions disabled"); assert(MSGuidTagDecl && "asked for GUID type but MS extensions disabled");
return getTagDeclType(MSGuidTagDecl); return getCanonicalTagType(MSGuidTagDecl);
} }
/// Retrieve the implicitly-predeclared 'struct type_info' declaration. /// Retrieve the implicitly-predeclared 'struct type_info' declaration.
@ -2477,7 +2504,7 @@ public:
UnresolvedSetIterator End) const; UnresolvedSetIterator End) const;
TemplateName getAssumedTemplateName(DeclarationName Name) const; TemplateName getAssumedTemplateName(DeclarationName Name) const;
TemplateName getQualifiedTemplateName(NestedNameSpecifier *NNS, TemplateName getQualifiedTemplateName(NestedNameSpecifier Qualifier,
bool TemplateKeyword, bool TemplateKeyword,
TemplateName Template) const; TemplateName Template) const;
TemplateName TemplateName
@ -2919,32 +2946,6 @@ public:
/// Determine if two types are similar, ignoring only CVR qualifiers. /// Determine if two types are similar, ignoring only CVR qualifiers.
bool hasCvrSimilarType(QualType T1, QualType T2); bool hasCvrSimilarType(QualType T1, QualType T2);
/// Retrieves the "canonical" nested name specifier for a
/// given nested name specifier.
///
/// The canonical nested name specifier is a nested name specifier
/// that uniquely identifies a type or namespace within the type
/// system. For example, given:
///
/// \code
/// namespace N {
/// struct S {
/// template<typename T> struct X { typename T* type; };
/// };
/// }
///
/// template<typename T> struct Y {
/// typename N::S::X<T>::type member;
/// };
/// \endcode
///
/// Here, the nested-name-specifier for N::S::X<T>:: will be
/// S::X<template-param-0-0>, since 'S' and 'X' are uniquely defined
/// by declarations in the type system and the canonical type for
/// the template type parameter 'T' is template-param-0-0.
NestedNameSpecifier *
getCanonicalNestedNameSpecifier(NestedNameSpecifier *NNS) const;
/// Retrieves the default calling convention for the current context. /// Retrieves the default calling convention for the current context.
/// ///
/// The context's default calling convention may differ from the current /// The context's default calling convention may differ from the current
@ -3158,7 +3159,7 @@ public:
mergeExceptionSpecs(FunctionProtoType::ExceptionSpecInfo ESI1, mergeExceptionSpecs(FunctionProtoType::ExceptionSpecInfo ESI1,
FunctionProtoType::ExceptionSpecInfo ESI2, FunctionProtoType::ExceptionSpecInfo ESI2,
SmallVectorImpl<QualType> &ExceptionTypeStorage, SmallVectorImpl<QualType> &ExceptionTypeStorage,
bool AcceptDependent); bool AcceptDependent) const;
// For two "same" types, return a type which has // For two "same" types, return a type which has
// the common sugar between them. If Unqualified is true, // the common sugar between them. If Unqualified is true,
@ -3166,7 +3167,7 @@ public:
// The result will drop the qualifiers which do not occur // The result will drop the qualifiers which do not occur
// in both types. // in both types.
QualType getCommonSugaredType(QualType X, QualType Y, QualType getCommonSugaredType(QualType X, QualType Y,
bool Unqualified = false); bool Unqualified = false) const;
private: private:
// Helper for integer ordering // Helper for integer ordering
@ -3184,23 +3185,11 @@ public:
bool propertyTypesAreCompatible(QualType, QualType); bool propertyTypesAreCompatible(QualType, QualType);
bool typesAreBlockPointerCompatible(QualType, QualType); bool typesAreBlockPointerCompatible(QualType, QualType);
bool isObjCIdType(QualType T) const { bool isObjCIdType(QualType T) const { return T == getObjCIdType(); }
if (const auto *ET = dyn_cast<ElaboratedType>(T))
T = ET->getNamedType();
return T == getObjCIdType();
}
bool isObjCClassType(QualType T) const { bool isObjCClassType(QualType T) const { return T == getObjCClassType(); }
if (const auto *ET = dyn_cast<ElaboratedType>(T))
T = ET->getNamedType();
return T == getObjCClassType();
}
bool isObjCSelType(QualType T) const { bool isObjCSelType(QualType T) const { return T == getObjCSelType(); }
if (const auto *ET = dyn_cast<ElaboratedType>(T))
T = ET->getNamedType();
return T == getObjCSelType();
}
bool ObjCQualifiedIdTypesAreCompatible(const ObjCObjectPointerType *LHS, bool ObjCQualifiedIdTypesAreCompatible(const ObjCObjectPointerType *LHS,
const ObjCObjectPointerType *RHS, const ObjCObjectPointerType *RHS,

View File

@ -404,7 +404,7 @@ class TypeSourceInfo;
/// ///
/// \returns The equivalent nested-name-specifier in the "to" /// \returns The equivalent nested-name-specifier in the "to"
/// context, or the import error. /// context, or the import error.
llvm::Expected<NestedNameSpecifier *> Import(NestedNameSpecifier *FromNNS); llvm::Expected<NestedNameSpecifier> Import(NestedNameSpecifier FromNNS);
/// Import the given nested-name-specifier-loc from the "from" /// Import the given nested-name-specifier-loc from the "from"
/// context into the "to" context. /// context into the "to" context.

View File

@ -394,12 +394,14 @@ public:
} }
void VisitMemberPointerType(const MemberPointerType *T) { void VisitMemberPointerType(const MemberPointerType *T) {
// FIXME: Provide a NestedNameSpecifier visitor. // FIXME: Provide a NestedNameSpecifier visitor.
NestedNameSpecifier *Qualifier = T->getQualifier(); NestedNameSpecifier Qualifier = T->getQualifier();
if (NestedNameSpecifier::SpecifierKind K = Qualifier->getKind(); if (NestedNameSpecifier::Kind K = Qualifier.getKind();
K == NestedNameSpecifier::TypeSpec) K == NestedNameSpecifier::Kind::Type)
Visit(Qualifier->getAsType()); Visit(Qualifier.getAsType());
if (T->isSugared()) if (T->isSugared())
Visit(T->getMostRecentCXXRecordDecl()->getTypeForDecl()); Visit(cast<MemberPointerType>(T->getCanonicalTypeUnqualified())
->getQualifier()
.getAsType());
Visit(T->getPointeeType()); Visit(T->getPointeeType());
} }
void VisitArrayType(const ArrayType *T) { Visit(T->getElementType()); } void VisitArrayType(const ArrayType *T) { Visit(T->getElementType()); }
@ -510,7 +512,7 @@ public:
} }
void VisitMemberPointerTypeLoc(MemberPointerTypeLoc TL) { void VisitMemberPointerTypeLoc(MemberPointerTypeLoc TL) {
// FIXME: Provide NestedNamespecifierLoc visitor. // FIXME: Provide NestedNamespecifierLoc visitor.
Visit(TL.getQualifierLoc().getTypeLoc()); Visit(TL.getQualifierLoc().castAsTypeLoc());
} }
void VisitVariableArrayTypeLoc(VariableArrayTypeLoc TL) { void VisitVariableArrayTypeLoc(VariableArrayTypeLoc TL) {
Visit(TL.getSizeExpr()); Visit(TL.getSizeExpr());
@ -772,17 +774,16 @@ public:
} }
void VisitUsingShadowDecl(const UsingShadowDecl *D) { void VisitUsingShadowDecl(const UsingShadowDecl *D) {
if (auto *TD = dyn_cast<TypeDecl>(D->getUnderlyingDecl())) Visit(D->getTargetDecl());
Visit(TD->getTypeForDecl());
} }
void VisitFriendDecl(const FriendDecl *D) { void VisitFriendDecl(const FriendDecl *D) {
if (D->getFriendType()) { if (D->getFriendType()) {
// Traverse any CXXRecordDecl owned by this type, since // Traverse any CXXRecordDecl owned by this type, since
// it will not be in the parent context: // it will not be in the parent context:
if (auto *ET = D->getFriendType()->getType()->getAs<ElaboratedType>()) if (auto *TT = D->getFriendType()->getType()->getAs<TagType>())
if (auto *TD = ET->getOwnedTagDecl()) if (TT->isTagOwned())
Visit(TD); Visit(TT->getOriginalDecl());
} else { } else {
Visit(D->getFriendDecl()); Visit(D->getFriendDecl());
} }

View File

@ -307,7 +307,7 @@ public:
/// For nodes which represent textual entities in the source code, /// For nodes which represent textual entities in the source code,
/// return their SourceRange. For all other nodes, return SourceRange(). /// return their SourceRange. For all other nodes, return SourceRange().
SourceRange getSourceRange() const; SourceRange getSourceRange(bool IncludeQualifier = false) const;
/// @{ /// @{
/// Imposes an order on \c DynTypedNode. /// Imposes an order on \c DynTypedNode.
@ -336,9 +336,9 @@ public:
NodeKind)) { NodeKind)) {
auto NNSLA = getUnchecked<NestedNameSpecifierLoc>(); auto NNSLA = getUnchecked<NestedNameSpecifierLoc>();
auto NNSLB = Other.getUnchecked<NestedNameSpecifierLoc>(); auto NNSLB = Other.getUnchecked<NestedNameSpecifierLoc>();
return std::make_pair(NNSLA.getNestedNameSpecifier(), return std::make_pair(NNSLA.getNestedNameSpecifier().getAsVoidPointer(),
NNSLA.getOpaqueData()) < NNSLA.getOpaqueData()) <
std::make_pair(NNSLB.getNestedNameSpecifier(), std::make_pair(NNSLB.getNestedNameSpecifier().getAsVoidPointer(),
NNSLB.getOpaqueData()); NNSLB.getOpaqueData());
} }
@ -393,8 +393,9 @@ public:
if (ASTNodeKind::getFromNodeKind<NestedNameSpecifierLoc>().isSame( if (ASTNodeKind::getFromNodeKind<NestedNameSpecifierLoc>().isSame(
Val.NodeKind)) { Val.NodeKind)) {
auto NNSL = Val.getUnchecked<NestedNameSpecifierLoc>(); auto NNSL = Val.getUnchecked<NestedNameSpecifierLoc>();
return llvm::hash_combine(NNSL.getNestedNameSpecifier(), return llvm::hash_combine(
NNSL.getOpaqueData()); NNSL.getNestedNameSpecifier().getAsVoidPointer(),
NNSL.getOpaqueData());
} }
assert(Val.getMemoizationData()); assert(Val.getMemoizationData());
@ -539,8 +540,8 @@ struct DynTypedNode::BaseConverter<
: public DynCastPtrConverter<T, Attr> {}; : public DynCastPtrConverter<T, Attr> {};
template <> template <>
struct DynTypedNode::BaseConverter< struct DynTypedNode::BaseConverter<NestedNameSpecifier, void>
NestedNameSpecifier, void> : public PtrConverter<NestedNameSpecifier> {}; : public ValueConverter<NestedNameSpecifier> {};
template <> template <>
struct DynTypedNode::BaseConverter< struct DynTypedNode::BaseConverter<

View File

@ -197,7 +197,7 @@ public:
unsigned int_ = asImpl().readUInt32(); unsigned int_ = asImpl().readUInt32();
Decl *decl = asImpl().template readDeclAs<Decl>(); Decl *decl = asImpl().template readDeclAs<Decl>();
if (auto *recordDecl = dyn_cast<CXXRecordDecl>(decl)) if (auto *recordDecl = dyn_cast<CXXRecordDecl>(decl))
elemTy = getASTContext().getRecordType(recordDecl); elemTy = getASTContext().getCanonicalTagType(recordDecl);
else else
elemTy = cast<ValueDecl>(decl)->getType(); elemTy = cast<ValueDecl>(decl)->getType();
path.push_back( path.push_back(
@ -252,39 +252,34 @@ public:
return EffectConditionExpr{asImpl().readExprRef()}; return EffectConditionExpr{asImpl().readExprRef()};
} }
NestedNameSpecifier *readNestedNameSpecifier() { NestedNameSpecifier readNestedNameSpecifier() {
auto &ctx = getASTContext(); auto &ctx = getASTContext();
// We build this up iteratively. // We build this up iteratively.
NestedNameSpecifier *cur = nullptr; NestedNameSpecifier cur = std::nullopt;
uint32_t depth = asImpl().readUInt32(); uint32_t depth = asImpl().readUInt32();
for (uint32_t i = 0; i != depth; ++i) { for (uint32_t i = 0; i != depth; ++i) {
auto kind = asImpl().readNestedNameSpecifierKind(); auto kind = asImpl().readNestedNameSpecifierKind();
switch (kind) { switch (kind) {
case NestedNameSpecifier::Identifier: case NestedNameSpecifier::Kind::Namespace:
cur = NestedNameSpecifier::Create(ctx, cur, cur =
asImpl().readIdentifier()); NestedNameSpecifier(ctx, asImpl().readNamespaceBaseDeclRef(), cur);
continue; continue;
case NestedNameSpecifier::Kind::Type:
case NestedNameSpecifier::Namespace: assert(!cur);
cur = NestedNameSpecifier::Create(ctx, cur, cur = NestedNameSpecifier(asImpl().readQualType().getTypePtr());
asImpl().readNamespaceBaseDeclRef());
continue; continue;
case NestedNameSpecifier::Kind::Global:
case NestedNameSpecifier::TypeSpec: assert(!cur);
cur = NestedNameSpecifier::Create(ctx, cur, cur = NestedNameSpecifier::getGlobal();
asImpl().readQualType().getTypePtr());
continue; continue;
case NestedNameSpecifier::Kind::MicrosoftSuper:
case NestedNameSpecifier::Global: assert(!cur);
cur = NestedNameSpecifier::GlobalSpecifier(ctx); cur = NestedNameSpecifier(asImpl().readCXXRecordDeclRef());
continue;
case NestedNameSpecifier::Super:
cur = NestedNameSpecifier::SuperSpecifier(ctx,
asImpl().readCXXRecordDeclRef());
continue; continue;
case NestedNameSpecifier::Kind::Null:
llvm_unreachable("unexpected null nested name specifier");
} }
llvm_unreachable("bad nested name specifier kind"); llvm_unreachable("bad nested name specifier kind");
} }

View File

@ -181,7 +181,7 @@ public:
const Decl *baseOrMember = elem.getAsBaseOrMember().getPointer(); const Decl *baseOrMember = elem.getAsBaseOrMember().getPointer();
if (const auto *recordDecl = dyn_cast<CXXRecordDecl>(baseOrMember)) { if (const auto *recordDecl = dyn_cast<CXXRecordDecl>(baseOrMember)) {
asImpl().writeDeclRef(recordDecl); asImpl().writeDeclRef(recordDecl);
elemTy = ctx.getRecordType(recordDecl); elemTy = ctx.getCanonicalTagType(recordDecl);
} else { } else {
const auto *valueDecl = cast<ValueDecl>(baseOrMember); const auto *valueDecl = cast<ValueDecl>(baseOrMember);
asImpl().writeDeclRef(valueDecl); asImpl().writeDeclRef(valueDecl);
@ -229,42 +229,43 @@ public:
asImpl().writeExprRef(CE.getCondition()); asImpl().writeExprRef(CE.getCondition());
} }
void writeNestedNameSpecifier(NestedNameSpecifier *NNS) { void writeNestedNameSpecifier(NestedNameSpecifier NNS) {
// Nested name specifiers usually aren't too long. I think that 8 would // Nested name specifiers usually aren't too long. I think that 8 would
// typically accommodate the vast majority. // typically accommodate the vast majority.
SmallVector<NestedNameSpecifier *, 8> nestedNames; SmallVector<NestedNameSpecifier, 8> nestedNames;
// Push each of the NNS's onto a stack for serialization in reverse order. // Push each of the NNS's onto a stack for serialization in reverse order.
while (NNS) { while (NNS) {
nestedNames.push_back(NNS); nestedNames.push_back(NNS);
NNS = NNS->getPrefix(); NNS = NNS.getKind() == NestedNameSpecifier::Kind::Namespace
? NNS.getAsNamespaceAndPrefix().Prefix
: std::nullopt;
} }
asImpl().writeUInt32(nestedNames.size()); asImpl().writeUInt32(nestedNames.size());
while (!nestedNames.empty()) { while (!nestedNames.empty()) {
NNS = nestedNames.pop_back_val(); NNS = nestedNames.pop_back_val();
NestedNameSpecifier::SpecifierKind kind = NNS->getKind(); NestedNameSpecifier::Kind kind = NNS.getKind();
asImpl().writeNestedNameSpecifierKind(kind); asImpl().writeNestedNameSpecifierKind(kind);
switch (kind) { switch (kind) {
case NestedNameSpecifier::Identifier: case NestedNameSpecifier::Kind::Namespace:
asImpl().writeIdentifier(NNS->getAsIdentifier()); asImpl().writeNamespaceBaseDeclRef(
NNS.getAsNamespaceAndPrefix().Namespace);
continue;
case NestedNameSpecifier::Kind::Type:
asImpl().writeQualType(QualType(NNS.getAsType(), 0));
continue; continue;
case NestedNameSpecifier::Namespace: case NestedNameSpecifier::Kind::Global:
asImpl().writeNamespaceBaseDeclRef(NNS->getAsNamespace());
continue;
case NestedNameSpecifier::TypeSpec:
asImpl().writeQualType(QualType(NNS->getAsType(), 0));
continue;
case NestedNameSpecifier::Global:
// Don't need to write an associated value. // Don't need to write an associated value.
continue; continue;
case NestedNameSpecifier::Super: case NestedNameSpecifier::Kind::MicrosoftSuper:
asImpl().writeDeclRef(NNS->getAsRecordDecl()); asImpl().writeDeclRef(NNS.getAsMicrosoftSuper());
continue; continue;
case NestedNameSpecifier::Kind::Null:
llvm_unreachable("unexpected null nested name specifier");
} }
llvm_unreachable("bad nested name specifier kind"); llvm_unreachable("bad nested name specifier kind");
} }

View File

@ -453,7 +453,7 @@ template<>
struct CanProxyAdaptor<MemberPointerType> struct CanProxyAdaptor<MemberPointerType>
: public CanProxyBase<MemberPointerType> { : public CanProxyBase<MemberPointerType> {
LLVM_CLANG_CANPROXY_TYPE_ACCESSOR(getPointeeType) LLVM_CLANG_CANPROXY_TYPE_ACCESSOR(getPointeeType)
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(NestedNameSpecifier *, getQualifier) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(NestedNameSpecifier, getQualifier)
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(const CXXRecordDecl *, LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(const CXXRecordDecl *,
getMostRecentCXXRecordDecl) getMostRecentCXXRecordDecl)
}; };
@ -551,21 +551,18 @@ struct CanProxyAdaptor<UnaryTransformType>
template<> template<>
struct CanProxyAdaptor<TagType> : public CanProxyBase<TagType> { struct CanProxyAdaptor<TagType> : public CanProxyBase<TagType> {
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(TagDecl *, getDecl) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(TagDecl *, getOriginalDecl)
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isBeingDefined)
}; };
template<> template<>
struct CanProxyAdaptor<RecordType> : public CanProxyBase<RecordType> { struct CanProxyAdaptor<RecordType> : public CanProxyBase<RecordType> {
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(RecordDecl *, getDecl) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(RecordDecl *, getOriginalDecl)
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isBeingDefined)
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, hasConstFields) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, hasConstFields)
}; };
template<> template<>
struct CanProxyAdaptor<EnumType> : public CanProxyBase<EnumType> { struct CanProxyAdaptor<EnumType> : public CanProxyBase<EnumType> {
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(EnumDecl *, getDecl) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(EnumDecl *, getOriginalDecl)
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isBeingDefined)
}; };
template<> template<>

View File

@ -20,7 +20,7 @@
#include "clang/AST/DeclBase.h" #include "clang/AST/DeclBase.h"
#include "clang/AST/DeclarationName.h" #include "clang/AST/DeclarationName.h"
#include "clang/AST/ExternalASTSource.h" #include "clang/AST/ExternalASTSource.h"
#include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/NestedNameSpecifierBase.h"
#include "clang/AST/Redeclarable.h" #include "clang/AST/Redeclarable.h"
#include "clang/AST/Type.h" #include "clang/AST/Type.h"
#include "clang/Basic/AddressSpaces.h" #include "clang/Basic/AddressSpaces.h"
@ -833,9 +833,9 @@ public:
/// Retrieve the nested-name-specifier that qualifies the name of this /// Retrieve the nested-name-specifier that qualifies the name of this
/// declaration, if it was present in the source. /// declaration, if it was present in the source.
NestedNameSpecifier *getQualifier() const { NestedNameSpecifier getQualifier() const {
return hasExtInfo() ? getExtInfo()->QualifierLoc.getNestedNameSpecifier() return hasExtInfo() ? getExtInfo()->QualifierLoc.getNestedNameSpecifier()
: nullptr; : std::nullopt;
} }
/// Retrieve the nested-name-specifier (with source-location /// Retrieve the nested-name-specifier (with source-location
@ -3528,8 +3528,14 @@ public:
// check out ASTContext::getTypeDeclType or one of // check out ASTContext::getTypeDeclType or one of
// ASTContext::getTypedefType, ASTContext::getRecordType, etc. if you // ASTContext::getTypedefType, ASTContext::getRecordType, etc. if you
// already know the specific kind of node this is. // already know the specific kind of node this is.
const Type *getTypeForDecl() const { return TypeForDecl; } const Type *getTypeForDecl() const {
void setTypeForDecl(const Type *TD) { TypeForDecl = TD; } assert(!isa<TagDecl>(this));
return TypeForDecl;
}
void setTypeForDecl(const Type *TD) {
assert(!isa<TagDecl>(this));
TypeForDecl = TD;
}
SourceLocation getBeginLoc() const LLVM_READONLY { return LocStart; } SourceLocation getBeginLoc() const LLVM_READONLY { return LocStart; }
void setLocStart(SourceLocation L) { LocStart = L; } void setLocStart(SourceLocation L) { LocStart = L; }
@ -3635,6 +3641,10 @@ public:
return isTransparentTagSlow(); return isTransparentTagSlow();
} }
// These types are created lazily, use the ASTContext methods to obtain them.
const Type *getTypeForDecl() const = delete;
void setTypeForDecl(const Type *TD) = delete;
// Implement isa/cast/dyncast/etc. // Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) { static bool classofKind(Kind K) {
@ -3754,14 +3764,6 @@ protected:
/// True if this decl is currently being defined. /// True if this decl is currently being defined.
void setBeingDefined(bool V = true) { TagDeclBits.IsBeingDefined = V; } void setBeingDefined(bool V = true) { TagDeclBits.IsBeingDefined = V; }
/// Indicates whether it is possible for declarations of this kind
/// to have an out-of-date definition.
///
/// This option is only enabled when modules are enabled.
void setMayHaveOutOfDateDef(bool V = true) {
TagDeclBits.MayHaveOutOfDateDef = V;
}
public: public:
friend class ASTDeclReader; friend class ASTDeclReader;
friend class ASTDeclWriter; friend class ASTDeclWriter;
@ -3842,12 +3844,6 @@ public:
TagDeclBits.IsFreeStanding = isFreeStanding; TagDeclBits.IsFreeStanding = isFreeStanding;
} }
/// Indicates whether it is possible for declarations of this kind
/// to have an out-of-date definition.
///
/// This option is only enabled when modules are enabled.
bool mayHaveOutOfDateDef() const { return TagDeclBits.MayHaveOutOfDateDef; }
/// Whether this declaration declares a type that is /// Whether this declaration declares a type that is
/// dependent, i.e., a type that somehow depends on template /// dependent, i.e., a type that somehow depends on template
/// parameters. /// parameters.
@ -3888,6 +3884,19 @@ public:
/// the struct/union/class/enum. /// the struct/union/class/enum.
TagDecl *getDefinition() const; TagDecl *getDefinition() const;
TagDecl *getDefinitionOrSelf() const {
if (TagDecl *Def = getDefinition())
return Def;
return const_cast<TagDecl *>(this);
}
/// Determines whether this entity is in the process of being defined.
bool isEntityBeingDefined() const {
if (const TagDecl *Def = getDefinition())
return Def->isBeingDefined();
return false;
}
StringRef getKindName() const { StringRef getKindName() const {
return TypeWithKeyword::getTagTypeKindName(getTagKind()); return TypeWithKeyword::getTagTypeKindName(getTagKind());
} }
@ -3934,9 +3943,9 @@ public:
/// Retrieve the nested-name-specifier that qualifies the name of this /// Retrieve the nested-name-specifier that qualifies the name of this
/// declaration, if it was present in the source. /// declaration, if it was present in the source.
NestedNameSpecifier *getQualifier() const { NestedNameSpecifier getQualifier() const {
return hasExtInfo() ? getExtInfo()->QualifierLoc.getNestedNameSpecifier() return hasExtInfo() ? getExtInfo()->QualifierLoc.getNestedNameSpecifier()
: nullptr; : std::nullopt;
} }
/// Retrieve the nested-name-specifier (with source-location /// Retrieve the nested-name-specifier (with source-location
@ -3958,6 +3967,10 @@ public:
return getExtInfo()->TemplParamLists[i]; return getExtInfo()->TemplParamLists[i];
} }
// These types are created lazily, use the ASTContext methods to obtain them.
const Type *getTypeForDecl() const = delete;
void setTypeForDecl(const Type *TD) = delete;
using TypeDecl::printName; using TypeDecl::printName;
void printName(raw_ostream &OS, const PrintingPolicy &Policy) const override; void printName(raw_ostream &OS, const PrintingPolicy &Policy) const override;
@ -4087,6 +4100,10 @@ public:
return cast_or_null<EnumDecl>(TagDecl::getDefinition()); return cast_or_null<EnumDecl>(TagDecl::getDefinition());
} }
EnumDecl *getDefinitionOrSelf() const {
return cast_or_null<EnumDecl>(TagDecl::getDefinitionOrSelf());
}
static EnumDecl *Create(ASTContext &C, DeclContext *DC, static EnumDecl *Create(ASTContext &C, DeclContext *DC,
SourceLocation StartLoc, SourceLocation IdLoc, SourceLocation StartLoc, SourceLocation IdLoc,
IdentifierInfo *Id, EnumDecl *PrevDecl, IdentifierInfo *Id, EnumDecl *PrevDecl,
@ -4469,6 +4486,10 @@ public:
return cast_or_null<RecordDecl>(TagDecl::getDefinition()); return cast_or_null<RecordDecl>(TagDecl::getDefinition());
} }
RecordDecl *getDefinitionOrSelf() const {
return cast_or_null<RecordDecl>(TagDecl::getDefinitionOrSelf());
}
/// Returns whether this record is a union, or contains (at any nesting level) /// Returns whether this record is a union, or contains (at any nesting level)
/// a union member. This is used by CMSE to warn about possible information /// a union member. This is used by CMSE to warn about possible information
/// leaks. /// leaks.
@ -5299,6 +5320,8 @@ void Redeclarable<decl_type>::setPreviousDecl(decl_type *PrevDecl) {
/// We use this function to break a cycle between the inline definitions in /// We use this function to break a cycle between the inline definitions in
/// Type.h and Decl.h. /// Type.h and Decl.h.
inline bool IsEnumDeclComplete(EnumDecl *ED) { inline bool IsEnumDeclComplete(EnumDecl *ED) {
if (const auto *Def = ED->getDefinition())
return Def->isComplete();
return ED->isComplete(); return ED->isComplete();
} }

View File

@ -410,9 +410,6 @@ protected:
virtual ~Decl(); virtual ~Decl();
/// Update a potentially out-of-date declaration.
void updateOutOfDate(IdentifierInfo &II) const;
Linkage getCachedLinkage() const { Linkage getCachedLinkage() const {
return static_cast<Linkage>(CacheValidAndLinkage); return static_cast<Linkage>(CacheValidAndLinkage);
} }
@ -625,6 +622,12 @@ public:
void setReferenced(bool R = true) { Referenced = R; } void setReferenced(bool R = true) { Referenced = R; }
/// When doing manipulations which might change the computed linkage,
/// such as changing the DeclContext after the declaration has already been
/// used, invalidating the cache will make sure its linkage will be
/// recomputed.
void invalidateCachedLinkage() { setCachedLinkage(Linkage::Invalid); }
/// Whether this declaration is a top-level declaration (function, /// Whether this declaration is a top-level declaration (function,
/// global variable, etc.) that is lexically inside an objc container /// global variable, etc.) that is lexically inside an objc container
/// definition. /// definition.
@ -1564,13 +1567,6 @@ protected:
LLVM_PREFERRED_TYPE(bool) LLVM_PREFERRED_TYPE(bool)
uint64_t IsFreeStanding : 1; uint64_t IsFreeStanding : 1;
/// Indicates whether it is possible for declarations of this kind
/// to have an out-of-date definition.
///
/// This option is only enabled when modules are enabled.
LLVM_PREFERRED_TYPE(bool)
uint64_t MayHaveOutOfDateDef : 1;
/// Has the full definition of this type been required by a use somewhere in /// Has the full definition of this type been required by a use somewhere in
/// the TU. /// the TU.
LLVM_PREFERRED_TYPE(bool) LLVM_PREFERRED_TYPE(bool)

View File

@ -545,20 +545,6 @@ public:
return const_cast<CXXRecordDecl*>(this)->getMostRecentDecl(); return const_cast<CXXRecordDecl*>(this)->getMostRecentDecl();
} }
CXXRecordDecl *getMostRecentNonInjectedDecl() {
CXXRecordDecl *Recent = getMostRecentDecl();
while (Recent->isInjectedClassName()) {
// FIXME: Does injected class name need to be in the redeclarations chain?
assert(Recent->getPreviousDecl());
Recent = Recent->getPreviousDecl();
}
return Recent;
}
const CXXRecordDecl *getMostRecentNonInjectedDecl() const {
return const_cast<CXXRecordDecl*>(this)->getMostRecentNonInjectedDecl();
}
CXXRecordDecl *getDefinition() const { CXXRecordDecl *getDefinition() const {
// We only need an update if we don't already know which // We only need an update if we don't already know which
// declaration is the definition. // declaration is the definition.
@ -566,13 +552,18 @@ public:
return DD ? DD->Definition : nullptr; return DD ? DD->Definition : nullptr;
} }
CXXRecordDecl *getDefinitionOrSelf() const {
if (auto *Def = getDefinition())
return Def;
return const_cast<CXXRecordDecl *>(this);
}
bool hasDefinition() const { return DefinitionData || dataPtr(); } bool hasDefinition() const { return DefinitionData || dataPtr(); }
static CXXRecordDecl *Create(const ASTContext &C, TagKind TK, DeclContext *DC, static CXXRecordDecl *Create(const ASTContext &C, TagKind TK, DeclContext *DC,
SourceLocation StartLoc, SourceLocation IdLoc, SourceLocation StartLoc, SourceLocation IdLoc,
IdentifierInfo *Id, IdentifierInfo *Id,
CXXRecordDecl *PrevDecl = nullptr, CXXRecordDecl *PrevDecl = nullptr);
bool DelayTypeCreation = false);
static CXXRecordDecl *CreateLambda(const ASTContext &C, DeclContext *DC, static CXXRecordDecl *CreateLambda(const ASTContext &C, DeclContext *DC,
TypeSourceInfo *Info, SourceLocation Loc, TypeSourceInfo *Info, SourceLocation Loc,
unsigned DependencyKind, bool IsGeneric, unsigned DependencyKind, bool IsGeneric,
@ -1903,6 +1894,20 @@ public:
/// \endcode /// \endcode
bool isInjectedClassName() const; bool isInjectedClassName() const;
/// Determines whether this declaration has is canonically of an injected
/// class type. These are non-instantiated class template patterns, which can
/// be used from within the class template itself. For example:
///
/// \code
/// template<class T> struct C {
/// C *t; // Here `C *` is a pointer to an injected class type.
/// };
/// \endcode
bool hasInjectedClassType() const;
CanQualType
getCanonicalTemplateSpecializationType(const ASTContext &Ctx) const;
// Determine whether this type is an Interface Like type for // Determine whether this type is an Interface Like type for
// __interface inheritance purposes. // __interface inheritance purposes.
bool isInterfaceLike() const; bool isInterfaceLike() const;
@ -3131,7 +3136,7 @@ public:
/// Retrieve the nested-name-specifier that qualifies the /// Retrieve the nested-name-specifier that qualifies the
/// name of the namespace. /// name of the namespace.
NestedNameSpecifier *getQualifier() const { NestedNameSpecifier getQualifier() const {
return QualifierLoc.getNestedNameSpecifier(); return QualifierLoc.getNestedNameSpecifier();
} }
@ -3252,7 +3257,7 @@ public:
/// Retrieve the nested-name-specifier that qualifies the /// Retrieve the nested-name-specifier that qualifies the
/// name of the namespace. /// name of the namespace.
NestedNameSpecifier *getQualifier() const { NestedNameSpecifier getQualifier() const {
return QualifierLoc.getNestedNameSpecifier(); return QualifierLoc.getNestedNameSpecifier();
} }
@ -3614,7 +3619,7 @@ public:
NestedNameSpecifierLoc getQualifierLoc() const { return QualifierLoc; } NestedNameSpecifierLoc getQualifierLoc() const { return QualifierLoc; }
/// Retrieve the nested-name-specifier that qualifies the name. /// Retrieve the nested-name-specifier that qualifies the name.
NestedNameSpecifier *getQualifier() const { NestedNameSpecifier getQualifier() const {
return QualifierLoc.getNestedNameSpecifier(); return QualifierLoc.getNestedNameSpecifier();
} }
@ -3804,13 +3809,11 @@ public:
/// The source location of the 'enum' keyword. /// The source location of the 'enum' keyword.
SourceLocation getEnumLoc() const { return EnumLocation; } SourceLocation getEnumLoc() const { return EnumLocation; }
void setEnumLoc(SourceLocation L) { EnumLocation = L; } void setEnumLoc(SourceLocation L) { EnumLocation = L; }
NestedNameSpecifier *getQualifier() const { NestedNameSpecifier getQualifier() const {
return getQualifierLoc().getNestedNameSpecifier(); return getQualifierLoc().getNestedNameSpecifier();
} }
NestedNameSpecifierLoc getQualifierLoc() const { NestedNameSpecifierLoc getQualifierLoc() const {
if (auto ETL = EnumType->getTypeLoc().getAs<ElaboratedTypeLoc>()) return getEnumTypeLoc().getPrefix();
return ETL.getQualifierLoc();
return NestedNameSpecifierLoc();
} }
// Returns the "qualifier::Name" part as a TypeLoc. // Returns the "qualifier::Name" part as a TypeLoc.
TypeLoc getEnumTypeLoc() const { TypeLoc getEnumTypeLoc() const {
@ -3970,7 +3973,7 @@ public:
NestedNameSpecifierLoc getQualifierLoc() const { return QualifierLoc; } NestedNameSpecifierLoc getQualifierLoc() const { return QualifierLoc; }
/// Retrieve the nested-name-specifier that qualifies the name. /// Retrieve the nested-name-specifier that qualifies the name.
NestedNameSpecifier *getQualifier() const { NestedNameSpecifier getQualifier() const {
return QualifierLoc.getNestedNameSpecifier(); return QualifierLoc.getNestedNameSpecifier();
} }
@ -4060,7 +4063,7 @@ public:
NestedNameSpecifierLoc getQualifierLoc() const { return QualifierLoc; } NestedNameSpecifierLoc getQualifierLoc() const { return QualifierLoc; }
/// Retrieve the nested-name-specifier that qualifies the name. /// Retrieve the nested-name-specifier that qualifies the name.
NestedNameSpecifier *getQualifier() const { NestedNameSpecifier getQualifier() const {
return QualifierLoc.getNestedNameSpecifier(); return QualifierLoc.getNestedNameSpecifier();
} }

View File

@ -643,6 +643,9 @@ public:
/// from the explicitly-specified bound. /// from the explicitly-specified bound.
SourceLocation getColonLoc() const { return ColonLoc; } SourceLocation getColonLoc() const { return ColonLoc; }
using TypeDecl::getTypeForDecl;
using TypeDecl::setTypeForDecl;
// Implement isa/cast/dyncast/etc. // Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) { return K == ObjCTypeParam; } static bool classofKind(Kind K) { return K == ObjCTypeParam; }

View File

@ -1898,14 +1898,14 @@ public:
void getNameForDiagnostic(raw_ostream &OS, const PrintingPolicy &Policy, void getNameForDiagnostic(raw_ostream &OS, const PrintingPolicy &Policy,
bool Qualified) const override; bool Qualified) const override;
// FIXME: This is broken. CXXRecordDecl::getMostRecentDecl() returns a
// different "most recent" declaration from this function for the same
// declaration, because we don't override getMostRecentDeclImpl(). But
// it's not clear that we should override that, because the most recent
// declaration as a CXXRecordDecl sometimes is the injected-class-name.
ClassTemplateSpecializationDecl *getMostRecentDecl() { ClassTemplateSpecializationDecl *getMostRecentDecl() {
return cast<ClassTemplateSpecializationDecl>( return cast<ClassTemplateSpecializationDecl>(
getMostRecentNonInjectedDecl()); CXXRecordDecl::getMostRecentDecl());
}
ClassTemplateSpecializationDecl *getDefinitionOrSelf() const {
return cast<ClassTemplateSpecializationDecl>(
CXXRecordDecl::getDefinitionOrSelf());
} }
/// Retrieve the template that this specialization specializes. /// Retrieve the template that this specialization specializes.
@ -2123,10 +2123,13 @@ class ClassTemplatePartialSpecializationDecl
llvm::PointerIntPair<ClassTemplatePartialSpecializationDecl *, 1, bool> llvm::PointerIntPair<ClassTemplatePartialSpecializationDecl *, 1, bool>
InstantiatedFromMember; InstantiatedFromMember;
mutable CanQualType CanonInjectedTST;
ClassTemplatePartialSpecializationDecl( ClassTemplatePartialSpecializationDecl(
ASTContext &Context, TagKind TK, DeclContext *DC, SourceLocation StartLoc, ASTContext &Context, TagKind TK, DeclContext *DC, SourceLocation StartLoc,
SourceLocation IdLoc, TemplateParameterList *Params, SourceLocation IdLoc, TemplateParameterList *Params,
ClassTemplateDecl *SpecializedTemplate, ArrayRef<TemplateArgument> Args, ClassTemplateDecl *SpecializedTemplate, ArrayRef<TemplateArgument> Args,
CanQualType CanonInjectedTST,
ClassTemplatePartialSpecializationDecl *PrevDecl); ClassTemplatePartialSpecializationDecl *PrevDecl);
ClassTemplatePartialSpecializationDecl(ASTContext &C) ClassTemplatePartialSpecializationDecl(ASTContext &C)
@ -2143,7 +2146,7 @@ public:
Create(ASTContext &Context, TagKind TK, DeclContext *DC, Create(ASTContext &Context, TagKind TK, DeclContext *DC,
SourceLocation StartLoc, SourceLocation IdLoc, SourceLocation StartLoc, SourceLocation IdLoc,
TemplateParameterList *Params, ClassTemplateDecl *SpecializedTemplate, TemplateParameterList *Params, ClassTemplateDecl *SpecializedTemplate,
ArrayRef<TemplateArgument> Args, QualType CanonInjectedType, ArrayRef<TemplateArgument> Args, CanQualType CanonInjectedTST,
ClassTemplatePartialSpecializationDecl *PrevDecl); ClassTemplatePartialSpecializationDecl *PrevDecl);
static ClassTemplatePartialSpecializationDecl * static ClassTemplatePartialSpecializationDecl *
@ -2160,12 +2163,6 @@ public:
return TemplateParams; return TemplateParams;
} }
/// Get the template argument list of the template parameter list.
ArrayRef<TemplateArgument>
getInjectedTemplateArgs(const ASTContext &Context) const {
return getTemplateParameters()->getInjectedTemplateArgs(Context);
}
/// \brief All associated constraints of this partial specialization, /// \brief All associated constraints of this partial specialization,
/// including the requires clause and any constraints derived from /// including the requires clause and any constraints derived from
/// constrained-parameters. /// constrained-parameters.
@ -2247,14 +2244,10 @@ public:
return First->InstantiatedFromMember.setInt(true); return First->InstantiatedFromMember.setInt(true);
} }
/// Retrieves the injected specialization type for this partial /// Retrieves the canonical injected specialization type for this partial
/// specialization. This is not the same as the type-decl-type for /// specialization.
/// this partial specialization, which is an InjectedClassNameType. CanQualType
QualType getInjectedSpecializationType() const { getCanonicalInjectedSpecializationType(const ASTContext &Ctx) const;
assert(getTypeForDecl() && "partial specialization has no type set!");
return cast<InjectedClassNameType>(getTypeForDecl())
->getInjectedSpecializationType();
}
SourceRange getSourceRange() const override LLVM_READONLY; SourceRange getSourceRange() const override LLVM_READONLY;
@ -2289,8 +2282,8 @@ protected:
llvm::FoldingSetVector<ClassTemplatePartialSpecializationDecl> llvm::FoldingSetVector<ClassTemplatePartialSpecializationDecl>
PartialSpecializations; PartialSpecializations;
/// The injected-class-name type for this class template. /// The Injected Template Specialization Type for this declaration.
QualType InjectedClassNameType; CanQualType CanonInjectedTST;
Common() = default; Common() = default;
}; };
@ -2427,7 +2420,7 @@ public:
findPartialSpecInstantiatedFromMember( findPartialSpecInstantiatedFromMember(
ClassTemplatePartialSpecializationDecl *D); ClassTemplatePartialSpecializationDecl *D);
/// Retrieve the template specialization type of the /// Retrieve the canonical template specialization type of the
/// injected-class-name for this class template. /// injected-class-name for this class template.
/// ///
/// The injected-class-name for a class template \c X is \c /// The injected-class-name for a class template \c X is \c
@ -2441,7 +2434,8 @@ public:
/// typedef array this_type; // "array" is equivalent to "array<T, N>" /// typedef array this_type; // "array" is equivalent to "array<T, N>"
/// }; /// };
/// \endcode /// \endcode
QualType getInjectedClassNameSpecialization(); CanQualType
getCanonicalInjectedSpecializationType(const ASTContext &Ctx) const;
using spec_iterator = SpecIterator<ClassTemplateSpecializationDecl>; using spec_iterator = SpecIterator<ClassTemplateSpecializationDecl>;
using spec_range = llvm::iterator_range<spec_iterator>; using spec_range = llvm::iterator_range<spec_iterator>;

View File

@ -293,7 +293,7 @@ inline TypeDependence toSemanticDependence(TypeDependence D) {
} }
inline NestedNameSpecifierDependence inline NestedNameSpecifierDependence
toNestedNameSpecifierDependendence(TypeDependence D) { toNestedNameSpecifierDependence(TypeDependence D) {
return Dependence(D).nestedNameSpecifier(); return Dependence(D).nestedNameSpecifier();
} }

View File

@ -134,8 +134,7 @@ public:
/// Recursively visit a C++ nested-name-specifier. /// Recursively visit a C++ nested-name-specifier.
/// ///
/// \returns false if the visitation was terminated early, true otherwise. /// \returns false if the visitation was terminated early, true otherwise.
virtual bool virtual bool TraverseNestedNameSpecifier(NestedNameSpecifier NNS);
TraverseNestedNameSpecifier(MaybeConst<NestedNameSpecifier> *NNS);
/// Recursively visit a C++ nested-name-specifier with location /// Recursively visit a C++ nested-name-specifier with location
/// information. /// information.
@ -181,14 +180,14 @@ public:
/// ///
/// \returns false if the visitation was terminated early, true /// \returns false if the visitation was terminated early, true
/// otherwise (including when the argument is a Null type). /// otherwise (including when the argument is a Null type).
virtual bool TraverseType(QualType T); virtual bool TraverseType(QualType T, bool TraverseQualifier = true);
/// Recursively visit a type with location, by dispatching to /// Recursively visit a type with location, by dispatching to
/// Traverse*TypeLoc() based on the argument type's getTypeClass() property. /// Traverse*TypeLoc() based on the argument type's getTypeClass() property.
/// ///
/// \returns false if the visitation was terminated early, true /// \returns false if the visitation was terminated early, true
/// otherwise (including when the argument is a Null type location). /// otherwise (including when the argument is a Null type location).
virtual bool TraverseTypeLoc(TypeLoc TL); virtual bool TraverseTypeLoc(TypeLoc TL, bool TraverseQualifier = true);
/// Recursively visit an Objective-C protocol reference with location /// Recursively visit an Objective-C protocol reference with location
/// information. /// information.
@ -273,7 +272,8 @@ public:
#define ABSTRACT_TYPE(CLASS, BASE) #define ABSTRACT_TYPE(CLASS, BASE)
#define TYPE(CLASS, BASE) \ #define TYPE(CLASS, BASE) \
bool WalkUpFrom##CLASS##Type(MaybeConst<CLASS##Type> *T); \ bool WalkUpFrom##CLASS##Type(MaybeConst<CLASS##Type> *T); \
virtual bool Traverse##CLASS##Type(MaybeConst<CLASS##Type> *T); virtual bool Traverse##CLASS##Type(MaybeConst<CLASS##Type> *T, \
bool TraverseQualifier = true);
#include "clang/AST/TypeNodes.inc" #include "clang/AST/TypeNodes.inc"
#define TYPE(CLASS, BASE) \ #define TYPE(CLASS, BASE) \
@ -283,7 +283,8 @@ public:
// TypeLocs. // TypeLocs.
#define ABSTRACT_TYPELOC(CLASS, BASE) #define ABSTRACT_TYPELOC(CLASS, BASE)
#define TYPELOC(CLASS, BASE) \ #define TYPELOC(CLASS, BASE) \
virtual bool Traverse##CLASS##TypeLoc(CLASS##TypeLoc TL); virtual bool Traverse##CLASS##TypeLoc(CLASS##TypeLoc TL, \
bool TraverseQualifier);
#include "clang/AST/TypeLocNodes.def" #include "clang/AST/TypeLocNodes.def"
#define TYPELOC(CLASS, BASE) \ #define TYPELOC(CLASS, BASE) \

View File

@ -1369,7 +1369,7 @@ public:
/// If the name was qualified, retrieves the nested-name-specifier /// If the name was qualified, retrieves the nested-name-specifier
/// that precedes the name. Otherwise, returns NULL. /// that precedes the name. Otherwise, returns NULL.
NestedNameSpecifier *getQualifier() const { NestedNameSpecifier getQualifier() const {
return getQualifierLoc().getNestedNameSpecifier(); return getQualifierLoc().getNestedNameSpecifier();
} }
@ -3398,7 +3398,7 @@ public:
/// If the member name was qualified, retrieves the /// If the member name was qualified, retrieves the
/// nested-name-specifier that precedes the member name. Otherwise, returns /// nested-name-specifier that precedes the member name. Otherwise, returns
/// NULL. /// NULL.
NestedNameSpecifier *getQualifier() const { NestedNameSpecifier getQualifier() const {
return getQualifierLoc().getNestedNameSpecifier(); return getQualifierLoc().getNestedNameSpecifier();
} }

View File

@ -2781,7 +2781,7 @@ public:
/// If the member name was qualified, retrieves the /// If the member name was qualified, retrieves the
/// nested-name-specifier that precedes the member name. Otherwise, returns /// nested-name-specifier that precedes the member name. Otherwise, returns
/// null. /// null.
NestedNameSpecifier *getQualifier() const { NestedNameSpecifier getQualifier() const {
return QualifierLoc.getNestedNameSpecifier(); return QualifierLoc.getNestedNameSpecifier();
} }
@ -3222,7 +3222,7 @@ public:
SourceLocation getNameLoc() const { return NameInfo.getLoc(); } SourceLocation getNameLoc() const { return NameInfo.getLoc(); }
/// Fetches the nested-name qualifier, if one was given. /// Fetches the nested-name qualifier, if one was given.
NestedNameSpecifier *getQualifier() const { NestedNameSpecifier getQualifier() const {
return QualifierLoc.getNestedNameSpecifier(); return QualifierLoc.getNestedNameSpecifier();
} }
@ -3540,7 +3540,7 @@ public:
/// Retrieve the nested-name-specifier that qualifies this /// Retrieve the nested-name-specifier that qualifies this
/// declaration. /// declaration.
NestedNameSpecifier *getQualifier() const { NestedNameSpecifier getQualifier() const {
return QualifierLoc.getNestedNameSpecifier(); return QualifierLoc.getNestedNameSpecifier();
} }
@ -3955,7 +3955,7 @@ public:
} }
/// Retrieve the nested-name-specifier that qualifies the member name. /// Retrieve the nested-name-specifier that qualifies the member name.
NestedNameSpecifier *getQualifier() const { NestedNameSpecifier getQualifier() const {
return QualifierLoc.getNestedNameSpecifier(); return QualifierLoc.getNestedNameSpecifier();
} }

View File

@ -240,7 +240,6 @@ public:
void VisitInjectedClassNameType(const InjectedClassNameType *ICNT); void VisitInjectedClassNameType(const InjectedClassNameType *ICNT);
void VisitObjCInterfaceType(const ObjCInterfaceType *OIT); void VisitObjCInterfaceType(const ObjCInterfaceType *OIT);
void VisitPackExpansionType(const PackExpansionType *PET); void VisitPackExpansionType(const PackExpansionType *PET);
void VisitElaboratedType(const ElaboratedType *ET);
void VisitMacroQualifiedType(const MacroQualifiedType *MQT); void VisitMacroQualifiedType(const MacroQualifiedType *MQT);
void VisitMemberPointerType(const MemberPointerType *MPT); void VisitMemberPointerType(const MemberPointerType *MPT);

View File

@ -6,507 +6,266 @@
// //
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// //
// This file defines the NestedNameSpecifier class, which represents // This file completes the definition of the NestedNameSpecifier class.
// a C++ nested-name-specifier.
// //
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_AST_NESTEDNAMESPECIFIER_H #ifndef LLVM_CLANG_AST_NESTEDNAMESPECIFIER_H
#define LLVM_CLANG_AST_NESTEDNAMESPECIFIER_H #define LLVM_CLANG_AST_NESTEDNAMESPECIFIER_H
#include "clang/AST/DependenceFlags.h" #include "clang/AST/Decl.h"
#include "clang/Basic/Diagnostic.h" #include "clang/AST/NestedNameSpecifierBase.h"
#include "clang/Basic/SourceLocation.h" #include "clang/AST/Type.h"
#include "clang/AST/TypeLoc.h"
#include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/DenseMapInfo.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/Support/Compiler.h"
#include <cstdint>
#include <cstdlib>
#include <utility>
namespace clang { namespace clang {
class ASTContext; auto NestedNameSpecifier::getKind() const -> Kind {
class CXXRecordDecl; if (!isStoredKind()) {
class IdentifierInfo; switch (getFlagKind()) {
class LangOptions; case FlagKind::Null:
class NamespaceBaseDecl; return Kind::Null;
struct PrintingPolicy; case FlagKind::Global:
class Type; return Kind::Global;
class TypeLoc; case FlagKind::Invalid:
llvm_unreachable("use of invalid NestedNameSpecifier");
}
llvm_unreachable("unhandled FlagKind");
}
switch (auto [K, Ptr] = getStored(); K) {
case StoredKind::Type:
return Kind::Type;
case StoredKind::NamespaceWithGlobal:
case StoredKind::NamespaceWithNamespace:
return Kind::Namespace;
case StoredKind::NamespaceOrSuper:
switch (static_cast<const Decl *>(Ptr)->getKind()) {
case Decl::Namespace:
case Decl::NamespaceAlias:
return Kind::Namespace;
case Decl::CXXRecord:
case Decl::ClassTemplateSpecialization:
case Decl::ClassTemplatePartialSpecialization:
return Kind::MicrosoftSuper;
default:
llvm_unreachable("unexpected decl kind");
}
}
llvm_unreachable("unknown StoredKind");
}
/// Represents a C++ nested name specifier, such as NestedNameSpecifier::NestedNameSpecifier(const Type *T)
/// "\::std::vector<int>::". : NestedNameSpecifier({StoredKind::Type, T}) {
/// assert(getKind() == Kind::Type);
/// C++ nested name specifiers are the prefixes to qualified }
/// names. For example, "foo::" in "foo::x" is a nested name
/// specifier. Nested name specifiers are made up of a sequence of
/// specifiers, each of which can be a namespace, type, identifier
/// (for dependent names), decltype specifier, or the global specifier ('::').
/// The last two specifiers can only appear at the start of a
/// nested-namespace-specifier.
class NestedNameSpecifier : public llvm::FoldingSetNode {
/// Enumeration describing
enum StoredSpecifierKind {
StoredIdentifier = 0,
StoredDecl = 1,
StoredTypeSpec = 2
};
/// The nested name specifier that precedes this nested name auto NestedNameSpecifier::MakeNamespacePtrKind(
/// specifier. const ASTContext &Ctx, const NamespaceBaseDecl *Namespace,
/// NestedNameSpecifier Prefix) -> PtrKind {
/// The pointer is the nested-name-specifier that precedes this switch (Prefix.getKind()) {
/// one. The integer stores one of the first four values of type case Kind::Null:
/// SpecifierKind. return {StoredKind::NamespaceOrSuper, Namespace};
llvm::PointerIntPair<NestedNameSpecifier *, 2, StoredSpecifierKind> Prefix; case Kind::Global:
return {StoredKind::NamespaceWithGlobal, Namespace};
case Kind::Namespace:
return {StoredKind::NamespaceWithNamespace,
MakeNamespaceAndPrefixStorage(Ctx, Namespace, Prefix)};
case Kind::MicrosoftSuper:
case Kind::Type:
llvm_unreachable("invalid prefix for namespace");
}
llvm_unreachable("unhandled kind");
}
/// The last component in the nested name specifier, which /// Builds a nested name specifier that names a namespace.
/// can be an identifier, a declaration, or a type. NestedNameSpecifier::NestedNameSpecifier(const ASTContext &Ctx,
/// const NamespaceBaseDecl *Namespace,
/// When the pointer is NULL, this specifier represents the global NestedNameSpecifier Prefix)
/// specifier '::'. Otherwise, the pointer is one of : NestedNameSpecifier(MakeNamespacePtrKind(Ctx, Namespace, Prefix)) {
/// IdentifierInfo*, Namespace*, or Type*, depending on the kind of assert(getKind() == Kind::Namespace);
/// specifier as encoded within the prefix. }
void* Specifier = nullptr;
public: /// Builds a nested name specifier that names a class through microsoft's
/// The kind of specifier that completes this nested name /// __super specifier.
/// specifier. NestedNameSpecifier::NestedNameSpecifier(CXXRecordDecl *RD)
enum SpecifierKind { : NestedNameSpecifier({StoredKind::NamespaceOrSuper, RD}) {
/// An identifier, stored as an IdentifierInfo*. assert(getKind() == Kind::MicrosoftSuper);
Identifier, }
/// A namespace-like entity, stored as a NamespaceBaseDecl*.
Namespace,
/// A type, stored as a Type*.
TypeSpec,
/// The global specifier '::'. There is no stored value.
Global,
/// Microsoft's '__super' specifier, stored as a CXXRecordDecl* of
/// the class it appeared in.
Super
};
private:
/// Builds the global specifier.
NestedNameSpecifier() : Prefix(nullptr, StoredIdentifier) {}
/// Copy constructor used internally to clone nested name
/// specifiers.
NestedNameSpecifier(const NestedNameSpecifier &Other) = default;
/// Either find or insert the given nested name specifier
/// mockup in the given context.
static NestedNameSpecifier *FindOrInsert(const ASTContext &Context,
const NestedNameSpecifier &Mockup);
public:
NestedNameSpecifier &operator=(const NestedNameSpecifier &) = delete;
/// Builds a specifier combining a prefix and an identifier.
///
/// The prefix must be dependent, since nested name specifiers
/// referencing an identifier are only permitted when the identifier
/// cannot be resolved.
static NestedNameSpecifier *Create(const ASTContext &Context,
NestedNameSpecifier *Prefix,
const IdentifierInfo *II);
/// Builds a nested name specifier that names a namespace or namespace alias.
static NestedNameSpecifier *Create(const ASTContext &Context,
NestedNameSpecifier *Prefix,
const NamespaceBaseDecl *NS);
/// Builds a nested name specifier that names a type.
static NestedNameSpecifier *
Create(const ASTContext &Context, NestedNameSpecifier *Prefix, const Type *T);
/// Builds a specifier that consists of just an identifier.
///
/// The nested-name-specifier is assumed to be dependent, but has no
/// prefix because the prefix is implied by something outside of the
/// nested name specifier, e.g., in "x->Base::f", the "x" has a dependent
/// type.
static NestedNameSpecifier *Create(const ASTContext &Context,
const IdentifierInfo *II);
/// Returns the nested name specifier representing the global
/// scope.
static NestedNameSpecifier *GlobalSpecifier(const ASTContext &Context);
/// Returns the nested name specifier representing the __super scope
/// for the given CXXRecordDecl.
static NestedNameSpecifier *SuperSpecifier(const ASTContext &Context,
CXXRecordDecl *RD);
/// Return the prefix of this nested name specifier.
///
/// The prefix contains all of the parts of the nested name
/// specifier that precede this current specifier. For example, for a
/// nested name specifier that represents "foo::bar::", the current
/// specifier will contain "bar::" and the prefix will contain
/// "foo::".
NestedNameSpecifier *getPrefix() const { return Prefix.getPointer(); }
/// Determine what kind of nested name specifier is stored.
SpecifierKind getKind() const;
/// Retrieve the identifier stored in this nested name
/// specifier.
IdentifierInfo *getAsIdentifier() const {
if (Prefix.getInt() == StoredIdentifier)
return (IdentifierInfo *)Specifier;
CXXRecordDecl *NestedNameSpecifier::getAsRecordDecl() const {
switch (getKind()) {
case Kind::MicrosoftSuper:
return getAsMicrosoftSuper();
case Kind::Type:
return getAsType()->getAsCXXRecordDecl();
case Kind::Global:
case Kind::Namespace:
case Kind::Null:
return nullptr; return nullptr;
} }
llvm_unreachable("Invalid NNS Kind!");
}
/// Retrieve the namespace or namespace alias stored in this nested name NestedNameSpecifier NestedNameSpecifier::getCanonical() const {
/// specifier. switch (getKind()) {
NamespaceBaseDecl *getAsNamespace() const; case NestedNameSpecifier::Kind::Null:
case NestedNameSpecifier::Kind::Global:
case NestedNameSpecifier::Kind::MicrosoftSuper:
// These are canonical and unique.
return *this;
case NestedNameSpecifier::Kind::Namespace: {
// A namespace is canonical; build a nested-name-specifier with
// this namespace and no prefix.
const NamespaceBaseDecl *ND = getAsNamespaceAndPrefix().Namespace;
return NestedNameSpecifier(
{StoredKind::NamespaceOrSuper, ND->getNamespace()->getCanonicalDecl()});
}
case NestedNameSpecifier::Kind::Type:
return NestedNameSpecifier(
getAsType()->getCanonicalTypeInternal().getTypePtr());
}
llvm_unreachable("unhandled kind");
}
/// Retrieve the record declaration stored in this nested name bool NestedNameSpecifier::isCanonical() const {
/// specifier. return *this == getCanonical();
CXXRecordDecl *getAsRecordDecl() const; }
/// Retrieve the type stored in this nested name specifier. TypeLoc NestedNameSpecifierLoc::castAsTypeLoc() const {
const Type *getAsType() const { return TypeLoc(Qualifier.getAsType(), LoadPointer(/*Offset=*/0));
if (Prefix.getInt() == StoredTypeSpec) }
return (const Type *)Specifier;
return nullptr; TypeLoc NestedNameSpecifierLoc::getAsTypeLoc() const {
if (Qualifier.getKind() != NestedNameSpecifier::Kind::Type)
return TypeLoc();
return castAsTypeLoc();
}
unsigned
NestedNameSpecifierLoc::getLocalDataLength(NestedNameSpecifier Qualifier) {
// Location of the trailing '::'.
unsigned Length = sizeof(SourceLocation::UIntTy);
switch (Qualifier.getKind()) {
case NestedNameSpecifier::Kind::Global:
// Nothing more to add.
break;
case NestedNameSpecifier::Kind::Namespace:
case NestedNameSpecifier::Kind::MicrosoftSuper:
// The location of the identifier or namespace name.
Length += sizeof(SourceLocation::UIntTy);
break;
case NestedNameSpecifier::Kind::Type:
// The "void*" that points at the TypeLoc data.
// Note: the 'template' keyword is part of the TypeLoc.
Length += sizeof(void *);
break;
case NestedNameSpecifier::Kind::Null:
llvm_unreachable("Expected a non-NULL qualifier");
} }
/// Fully translate this nested name specifier to a type. return Length;
/// Unlike getAsType, this will convert this entire nested }
/// name specifier chain into its equivalent type.
const Type *translateToType(const ASTContext &Context) const;
NestedNameSpecifierDependence getDependence() const; NamespaceAndPrefixLoc NestedNameSpecifierLoc::castAsNamespaceAndPrefix() const {
auto [Namespace, Prefix] = Qualifier.getAsNamespaceAndPrefix();
return {Namespace, NestedNameSpecifierLoc(Prefix, Data)};
}
/// Whether this nested name specifier refers to a dependent NamespaceAndPrefixLoc NestedNameSpecifierLoc::getAsNamespaceAndPrefix() const {
/// type or not. if (Qualifier.getKind() != NestedNameSpecifier::Kind::Namespace)
bool isDependent() const; return {};
return castAsNamespaceAndPrefix();
}
/// Whether this nested name specifier involves a template unsigned NestedNameSpecifierLoc::getDataLength(NestedNameSpecifier Qualifier) {
/// parameter. unsigned Length = 0;
bool isInstantiationDependent() const; for (; Qualifier; Qualifier = Qualifier.getAsNamespaceAndPrefix().Prefix) {
Length += getLocalDataLength(Qualifier);
if (Qualifier.getKind() != NestedNameSpecifier::Kind::Namespace)
break;
}
return Length;
}
/// Whether this nested-name-specifier contains an unexpanded unsigned NestedNameSpecifierLoc::getDataLength() const {
/// parameter pack (for C++11 variadic templates). return getDataLength(Qualifier);
bool containsUnexpandedParameterPack() const; }
/// Whether this nested name specifier contains an error. SourceRange NestedNameSpecifierLoc::getLocalSourceRange() const {
bool containsErrors() const; switch (auto Kind = Qualifier.getKind()) {
case NestedNameSpecifier::Kind::Null:
/// Print this nested name specifier to the given output stream. If return SourceRange();
/// `ResolveTemplateArguments` is true, we'll print actual types, e.g. case NestedNameSpecifier::Kind::Global:
/// `ns::SomeTemplate<int, MyClass>` instead of return LoadSourceLocation(/*Offset=*/0);
/// `ns::SomeTemplate<Container::value_type, T>`. case NestedNameSpecifier::Kind::Namespace:
void print(raw_ostream &OS, const PrintingPolicy &Policy, case NestedNameSpecifier::Kind::MicrosoftSuper: {
bool ResolveTemplateArguments = false, unsigned Offset =
bool PrintFinalScopeResOp = true) const; Kind == NestedNameSpecifier::Kind::Namespace
? getDataLength(Qualifier.getAsNamespaceAndPrefix().Prefix)
void Profile(llvm::FoldingSetNodeID &ID) const { : 0;
ID.AddPointer(Prefix.getOpaqueValue()); return SourceRange(
ID.AddPointer(Specifier); LoadSourceLocation(Offset),
LoadSourceLocation(Offset + sizeof(SourceLocation::UIntTy)));
}
case NestedNameSpecifier::Kind::Type: {
// The "void*" that points at the TypeLoc data.
// Note: the 'template' keyword is part of the TypeLoc.
void *TypeData = LoadPointer(/*Offset=*/0);
TypeLoc TL(Qualifier.getAsType(), TypeData);
return SourceRange(TL.getBeginLoc(), LoadSourceLocation(sizeof(void *)));
}
} }
/// Dump the nested name specifier to standard output to aid llvm_unreachable("Invalid NNS Kind!");
/// in debugging. }
void dump(const LangOptions &LO) const;
void dump() const;
void dump(llvm::raw_ostream &OS) const;
void dump(llvm::raw_ostream &OS, const LangOptions &LO) const;
};
/// A C++ nested-name-specifier augmented with source location SourceRange NestedNameSpecifierLoc::getSourceRange() const {
/// information. return SourceRange(getBeginLoc(), getEndLoc());
class NestedNameSpecifierLoc { }
NestedNameSpecifier *Qualifier = nullptr;
void *Data = nullptr;
/// Determines the data length for the last component in the SourceLocation NestedNameSpecifierLoc::getEndLoc() const {
/// given nested-name-specifier. return getLocalSourceRange().getEnd();
static unsigned getLocalDataLength(NestedNameSpecifier *Qualifier); }
/// Determines the data length for the entire /// Retrieve the location of the beginning of this
/// nested-name-specifier. /// component of the nested-name-specifier.
static unsigned getDataLength(NestedNameSpecifier *Qualifier); SourceLocation NestedNameSpecifierLoc::getLocalBeginLoc() const {
return getLocalSourceRange().getBegin();
}
public: /// Retrieve the location of the end of this component of the
/// Construct an empty nested-name-specifier.
NestedNameSpecifierLoc() = default;
/// Construct a nested-name-specifier with source location information
/// from
NestedNameSpecifierLoc(NestedNameSpecifier *Qualifier, void *Data)
: Qualifier(Qualifier), Data(Data) {}
/// Evaluates true when this nested-name-specifier location is
/// non-empty.
explicit operator bool() const { return Qualifier; }
/// Evaluates true when this nested-name-specifier location is
/// non-empty.
bool hasQualifier() const { return Qualifier; }
/// Retrieve the nested-name-specifier to which this instance
/// refers.
NestedNameSpecifier *getNestedNameSpecifier() const {
return Qualifier;
}
/// Retrieve the opaque pointer that refers to source-location data.
void *getOpaqueData() const { return Data; }
/// Retrieve the source range covering the entirety of this
/// nested-name-specifier.
///
/// For example, if this instance refers to a nested-name-specifier
/// \c \::std::vector<int>::, the returned source range would cover
/// from the initial '::' to the last '::'.
SourceRange getSourceRange() const LLVM_READONLY {
return SourceRange(getBeginLoc(), getEndLoc());
}
/// Retrieve the source range covering just the last part of
/// this nested-name-specifier, not including the prefix.
///
/// For example, if this instance refers to a nested-name-specifier
/// \c \::std::vector<int>::, the returned source range would cover
/// from "vector" to the last '::'.
SourceRange getLocalSourceRange() const;
/// Retrieve the location of the beginning of this
/// nested-name-specifier.
SourceLocation getBeginLoc() const {
if (!Qualifier)
return SourceLocation();
NestedNameSpecifierLoc First = *this;
while (NestedNameSpecifierLoc Prefix = First.getPrefix())
First = Prefix;
return First.getLocalSourceRange().getBegin();
}
/// Retrieve the location of the end of this
/// nested-name-specifier.
SourceLocation getEndLoc() const { return getLocalSourceRange().getEnd(); }
/// Retrieve the location of the beginning of this
/// component of the nested-name-specifier.
SourceLocation getLocalBeginLoc() const {
return getLocalSourceRange().getBegin();
}
/// Retrieve the location of the end of this component of the
/// nested-name-specifier.
SourceLocation getLocalEndLoc() const {
return getLocalSourceRange().getEnd();
}
/// Return the prefix of this nested-name-specifier.
///
/// For example, if this instance refers to a nested-name-specifier
/// \c \::std::vector<int>::, the prefix is \c \::std::. Note that the
/// returned prefix may be empty, if this is the first component of
/// the nested-name-specifier.
NestedNameSpecifierLoc getPrefix() const {
if (!Qualifier)
return *this;
return NestedNameSpecifierLoc(Qualifier->getPrefix(), Data);
}
/// For a nested-name-specifier that refers to a type,
/// retrieve the type with source-location information.
TypeLoc getTypeLoc() const;
/// Determines the data length for the entire
/// nested-name-specifier.
unsigned getDataLength() const { return getDataLength(Qualifier); }
friend bool operator==(NestedNameSpecifierLoc X,
NestedNameSpecifierLoc Y) {
return X.Qualifier == Y.Qualifier && X.Data == Y.Data;
}
friend bool operator!=(NestedNameSpecifierLoc X,
NestedNameSpecifierLoc Y) {
return !(X == Y);
}
};
/// Class that aids in the construction of nested-name-specifiers along
/// with source-location information for all of the components of the
/// nested-name-specifier. /// nested-name-specifier.
class NestedNameSpecifierLocBuilder { SourceLocation NestedNameSpecifierLoc::getLocalEndLoc() const {
/// The current representation of the nested-name-specifier we're return getLocalSourceRange().getEnd();
/// building. }
NestedNameSpecifier *Representation = nullptr;
/// Buffer used to store source-location information for the SourceRange NestedNameSpecifierLocBuilder::getSourceRange() const {
/// nested-name-specifier. return NestedNameSpecifierLoc(Representation, Buffer).getSourceRange();
///
/// Note that we explicitly manage the buffer (rather than using a
/// SmallVector) because \c Declarator expects it to be possible to memcpy()
/// a \c CXXScopeSpec, and CXXScopeSpec uses a NestedNameSpecifierLocBuilder.
char *Buffer = nullptr;
/// The size of the buffer used to store source-location information
/// for the nested-name-specifier.
unsigned BufferSize = 0;
/// The capacity of the buffer used to store source-location
/// information for the nested-name-specifier.
unsigned BufferCapacity = 0;
public:
NestedNameSpecifierLocBuilder() = default;
NestedNameSpecifierLocBuilder(const NestedNameSpecifierLocBuilder &Other);
NestedNameSpecifierLocBuilder &
operator=(const NestedNameSpecifierLocBuilder &Other);
~NestedNameSpecifierLocBuilder() {
if (BufferCapacity)
free(Buffer);
}
/// Retrieve the representation of the nested-name-specifier.
NestedNameSpecifier *getRepresentation() const { return Representation; }
/// Extend the current nested-name-specifier by another
/// nested-name-specifier component of the form 'type::'.
///
/// \param Context The AST context in which this nested-name-specifier
/// resides.
///
/// \param TL The TypeLoc that describes the type preceding the '::'.
///
/// \param ColonColonLoc The location of the trailing '::'.
void Extend(ASTContext &Context, TypeLoc TL, SourceLocation ColonColonLoc);
/// Extend the current nested-name-specifier by another
/// nested-name-specifier component of the form 'identifier::'.
///
/// \param Context The AST context in which this nested-name-specifier
/// resides.
///
/// \param Identifier The identifier.
///
/// \param IdentifierLoc The location of the identifier.
///
/// \param ColonColonLoc The location of the trailing '::'.
void Extend(ASTContext &Context, IdentifierInfo *Identifier,
SourceLocation IdentifierLoc, SourceLocation ColonColonLoc);
/// Extend the current nested-name-specifier by another
/// nested-name-specifier component of the form 'namespace::'.
///
/// \param Context The AST context in which this nested-name-specifier
/// resides.
///
/// \param Namespace The namespace or namespace alias.
///
/// \param NamespaceLoc The location of the namespace name or the namespace
// alias.
///
/// \param ColonColonLoc The location of the trailing '::'.
void Extend(ASTContext &Context, NamespaceBaseDecl *Namespace,
SourceLocation NamespaceLoc, SourceLocation ColonColonLoc);
/// Turn this (empty) nested-name-specifier into the global
/// nested-name-specifier '::'.
void MakeGlobal(ASTContext &Context, SourceLocation ColonColonLoc);
/// Turns this (empty) nested-name-specifier into '__super'
/// nested-name-specifier.
///
/// \param Context The AST context in which this nested-name-specifier
/// resides.
///
/// \param RD The declaration of the class in which nested-name-specifier
/// appeared.
///
/// \param SuperLoc The location of the '__super' keyword.
/// name.
///
/// \param ColonColonLoc The location of the trailing '::'.
void MakeSuper(ASTContext &Context, CXXRecordDecl *RD,
SourceLocation SuperLoc, SourceLocation ColonColonLoc);
/// Make a new nested-name-specifier from incomplete source-location
/// information.
///
/// This routine should be used very, very rarely, in cases where we
/// need to synthesize a nested-name-specifier. Most code should instead use
/// \c Adopt() with a proper \c NestedNameSpecifierLoc.
void MakeTrivial(ASTContext &Context, NestedNameSpecifier *Qualifier,
SourceRange R);
/// Adopt an existing nested-name-specifier (with source-range
/// information).
void Adopt(NestedNameSpecifierLoc Other);
/// Retrieve the source range covered by this nested-name-specifier.
SourceRange getSourceRange() const LLVM_READONLY {
return NestedNameSpecifierLoc(Representation, Buffer).getSourceRange();
}
/// Retrieve a nested-name-specifier with location information,
/// copied into the given AST context.
///
/// \param Context The context into which this nested-name-specifier will be
/// copied.
NestedNameSpecifierLoc getWithLocInContext(ASTContext &Context) const;
/// Retrieve a nested-name-specifier with location
/// information based on the information in this builder.
///
/// This loc will contain references to the builder's internal data and may
/// be invalidated by any change to the builder.
NestedNameSpecifierLoc getTemporary() const {
return NestedNameSpecifierLoc(Representation, Buffer);
}
/// Clear out this builder, and prepare it to build another
/// nested-name-specifier with source-location information.
void Clear() {
Representation = nullptr;
BufferSize = 0;
}
/// Retrieve the underlying buffer.
///
/// \returns A pair containing a pointer to the buffer of source-location
/// data and the size of the source-location data that resides in that
/// buffer.
std::pair<char *, unsigned> getBuffer() const {
return std::make_pair(Buffer, BufferSize);
}
};
/// Insertion operator for diagnostics. This allows sending
/// NestedNameSpecifiers into a diagnostic with <<.
inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB,
NestedNameSpecifier *NNS) {
DB.AddTaggedVal(reinterpret_cast<uint64_t>(NNS),
DiagnosticsEngine::ak_nestednamespec);
return DB;
} }
} // namespace clang } // namespace clang
namespace llvm { namespace llvm {
template <> struct DenseMapInfo<clang::NestedNameSpecifier> {
static clang::NestedNameSpecifier getEmptyKey() { return std::nullopt; }
static clang::NestedNameSpecifier getTombstoneKey() {
return clang::NestedNameSpecifier::getInvalid();
}
static unsigned getHashValue(const clang::NestedNameSpecifier &V) {
return hash_combine(V.getAsVoidPointer());
}
};
template <> struct DenseMapInfo<clang::NestedNameSpecifierLoc> { template <> struct DenseMapInfo<clang::NestedNameSpecifierLoc> {
using FirstInfo = DenseMapInfo<clang::NestedNameSpecifier *>; using FirstInfo = DenseMapInfo<clang::NestedNameSpecifier>;
using SecondInfo = DenseMapInfo<void *>; using SecondInfo = DenseMapInfo<void *>;
static clang::NestedNameSpecifierLoc getEmptyKey() { static clang::NestedNameSpecifierLoc getEmptyKey() {

View File

@ -0,0 +1,586 @@
//===- NestedNameSpecifier.h - C++ nested name specifiers -------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines the NestedNameSpecifier class, which represents
// a C++ nested-name-specifier.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_AST_NESTEDNAMESPECIFIERBASE_H
#define LLVM_CLANG_AST_NESTEDNAMESPECIFIERBASE_H
#include "clang/AST/DependenceFlags.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/PointerLikeTypeTraits.h"
#include <cstdint>
#include <cstdlib>
#include <utility>
namespace clang {
class ASTContext;
class CXXRecordDecl;
class NamedDecl;
class IdentifierInfo;
class LangOptions;
class NamespaceBaseDecl;
struct PrintingPolicy;
class Type;
class TypeLoc;
struct NamespaceAndPrefix;
struct alignas(8) NamespaceAndPrefixStorage;
/// Represents a C++ nested name specifier, such as
/// "\::std::vector<int>::".
///
/// C++ nested name specifiers are the prefixes to qualified
/// names. For example, "foo::" in "foo::x" is a nested name
/// specifier. Nested name specifiers are made up of a sequence of
/// specifiers, each of which can be a namespace, type, decltype specifier, or
/// the global specifier ('::'). The last two specifiers can only appear at the
/// start of a nested-namespace-specifier.
class NestedNameSpecifier {
enum class FlagKind { Null, Global, Invalid };
enum class StoredKind {
Type,
NamespaceOrSuper,
NamespaceWithGlobal,
NamespaceWithNamespace
};
static constexpr uintptr_t FlagBits = 2, FlagMask = (1u << FlagBits) - 1u,
FlagOffset = 1, PtrOffset = FlagBits + FlagOffset,
PtrMask = (1u << PtrOffset) - 1u;
uintptr_t StoredOrFlag;
explicit NestedNameSpecifier(uintptr_t StoredOrFlag)
: StoredOrFlag(StoredOrFlag) {}
struct PtrKind {
StoredKind SK;
const void *Ptr;
};
explicit NestedNameSpecifier(PtrKind PK)
: StoredOrFlag(uintptr_t(PK.Ptr) | (uintptr_t(PK.SK) << FlagOffset)) {
assert(PK.Ptr != nullptr);
assert((uintptr_t(PK.Ptr) & ((1u << PtrOffset) - 1u)) == 0);
assert((uintptr_t(PK.Ptr) >> PtrOffset) != 0);
}
explicit constexpr NestedNameSpecifier(FlagKind K)
: StoredOrFlag(uintptr_t(K) << FlagOffset) {}
bool isStoredKind() const { return (StoredOrFlag >> PtrOffset) != 0; }
std::pair<StoredKind, const void *> getStored() const {
assert(isStoredKind());
return {StoredKind(StoredOrFlag >> FlagOffset & FlagMask),
reinterpret_cast<const void *>(StoredOrFlag & ~PtrMask)};
}
FlagKind getFlagKind() const {
assert(!isStoredKind());
return FlagKind(StoredOrFlag >> FlagOffset);
}
static const NamespaceAndPrefixStorage *
MakeNamespaceAndPrefixStorage(const ASTContext &Ctx,
const NamespaceBaseDecl *Namespace,
NestedNameSpecifier Prefix);
static inline PtrKind MakeNamespacePtrKind(const ASTContext &Ctx,
const NamespaceBaseDecl *Namespace,
NestedNameSpecifier Prefix);
public:
static constexpr NestedNameSpecifier getInvalid() {
return NestedNameSpecifier(FlagKind::Invalid);
}
static constexpr NestedNameSpecifier getGlobal() {
return NestedNameSpecifier(FlagKind::Global);
}
NestedNameSpecifier() : NestedNameSpecifier(FlagKind::Invalid) {}
/// The kind of specifier that completes this nested name
/// specifier.
enum class Kind {
/// Empty.
Null,
/// The global specifier '::'. There is no stored value.
Global,
/// A type, stored as a Type*.
Type,
/// A namespace-like entity, stored as a NamespaceBaseDecl*.
Namespace,
/// Microsoft's '__super' specifier, stored as a CXXRecordDecl* of
/// the class it appeared in.
MicrosoftSuper,
};
inline Kind getKind() const;
NestedNameSpecifier(std::nullopt_t) : StoredOrFlag(0) {}
explicit inline NestedNameSpecifier(const Type *T);
/// Builds a nested name specifier that names a namespace.
inline NestedNameSpecifier(const ASTContext &Ctx,
const NamespaceBaseDecl *Namespace,
NestedNameSpecifier Prefix);
/// Builds a nested name specifier that names a class through microsoft's
/// __super specifier.
explicit inline NestedNameSpecifier(CXXRecordDecl *RD);
explicit operator bool() const { return StoredOrFlag != 0; }
void *getAsVoidPointer() const {
return reinterpret_cast<void *>(StoredOrFlag);
}
static NestedNameSpecifier getFromVoidPointer(const void *Ptr) {
return NestedNameSpecifier(reinterpret_cast<uintptr_t>(Ptr));
}
const Type *getAsType() const {
auto [Kind, Ptr] = getStored();
assert(Kind == StoredKind::Type);
assert(Ptr != nullptr);
return static_cast<const Type *>(Ptr);
}
inline NamespaceAndPrefix getAsNamespaceAndPrefix() const;
CXXRecordDecl *getAsMicrosoftSuper() const {
auto [Kind, Ptr] = getStored();
assert(Kind == StoredKind::NamespaceOrSuper);
assert(Ptr != nullptr);
return static_cast<CXXRecordDecl *>(const_cast<void *>(Ptr));
}
/// Retrieve the record declaration stored in this nested name
/// specifier, or null.
inline CXXRecordDecl *getAsRecordDecl() const;
friend bool operator==(NestedNameSpecifier LHS, NestedNameSpecifier RHS) {
return LHS.StoredOrFlag == RHS.StoredOrFlag;
}
friend bool operator!=(NestedNameSpecifier LHS, NestedNameSpecifier RHS) {
return LHS.StoredOrFlag != RHS.StoredOrFlag;
}
/// Retrieves the "canonical" nested name specifier for a
/// given nested name specifier.
///
/// The canonical nested name specifier is a nested name specifier
/// that uniquely identifies a type or namespace within the type
/// system. For example, given:
///
/// \code
/// namespace N {
/// struct S {
/// template<typename T> struct X { typename T* type; };
/// };
/// }
///
/// template<typename T> struct Y {
/// typename N::S::X<T>::type member;
/// };
/// \endcode
///
/// Here, the nested-name-specifier for N::S::X<T>:: will be
/// S::X<template-param-0-0>, since 'S' and 'X' are uniquely defined
/// by declarations in the type system and the canonical type for
/// the template type parameter 'T' is template-param-0-0.
inline NestedNameSpecifier getCanonical() const;
/// Whether this nested name specifier is canonical.
inline bool isCanonical() const;
/// Whether this nested name specifier starts with a '::'.
bool isFullyQualified() const;
NestedNameSpecifierDependence getDependence() const;
/// Whether this nested name specifier refers to a dependent
/// type or not.
bool isDependent() const {
return getDependence() & NestedNameSpecifierDependence::Dependent;
}
/// Whether this nested name specifier involves a template
/// parameter.
bool isInstantiationDependent() const {
return getDependence() & NestedNameSpecifierDependence::Instantiation;
}
/// Whether this nested-name-specifier contains an unexpanded
/// parameter pack (for C++11 variadic templates).
bool containsUnexpandedParameterPack() const {
return getDependence() & NestedNameSpecifierDependence::UnexpandedPack;
}
/// Whether this nested name specifier contains an error.
bool containsErrors() const {
return getDependence() & NestedNameSpecifierDependence::Error;
}
/// Print this nested name specifier to the given output stream. If
/// `ResolveTemplateArguments` is true, we'll print actual types, e.g.
/// `ns::SomeTemplate<int, MyClass>` instead of
/// `ns::SomeTemplate<Container::value_type, T>`.
void print(raw_ostream &OS, const PrintingPolicy &Policy,
bool ResolveTemplateArguments = false,
bool PrintFinalScopeResOp = true) const;
void Profile(llvm::FoldingSetNodeID &ID) const {
ID.AddInteger(StoredOrFlag);
}
/// Dump the nested name specifier to aid in debugging.
void dump(llvm::raw_ostream *OS = nullptr,
const LangOptions *LO = nullptr) const;
void dump(const LangOptions &LO) const;
void dump(llvm::raw_ostream &OS) const;
void dump(llvm::raw_ostream &OS, const LangOptions &LO) const;
static constexpr auto NumLowBitsAvailable = FlagOffset;
};
struct NamespaceAndPrefix {
const NamespaceBaseDecl *Namespace;
NestedNameSpecifier Prefix;
};
struct alignas(8) NamespaceAndPrefixStorage : NamespaceAndPrefix,
llvm::FoldingSetNode {
NamespaceAndPrefixStorage(const NamespaceBaseDecl *Namespace,
NestedNameSpecifier Prefix)
: NamespaceAndPrefix{Namespace, Prefix} {}
void Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, Namespace, Prefix); }
static void Profile(llvm::FoldingSetNodeID &ID,
const NamespaceBaseDecl *Namespace,
NestedNameSpecifier Prefix) {
ID.AddPointer(Namespace);
Prefix.Profile(ID);
}
};
NamespaceAndPrefix NestedNameSpecifier::getAsNamespaceAndPrefix() const {
auto [Kind, Ptr] = getStored();
switch (Kind) {
case StoredKind::NamespaceOrSuper:
case StoredKind::NamespaceWithGlobal:
return {static_cast<const NamespaceBaseDecl *>(Ptr),
Kind == StoredKind::NamespaceWithGlobal
? NestedNameSpecifier::getGlobal()
: std::nullopt};
case StoredKind::NamespaceWithNamespace:
return *static_cast<const NamespaceAndPrefixStorage *>(Ptr);
case StoredKind::Type:;
}
llvm_unreachable("unexpected stored kind");
}
struct NamespaceAndPrefixLoc;
/// A C++ nested-name-specifier augmented with source location
/// information.
class NestedNameSpecifierLoc {
NestedNameSpecifier Qualifier = std::nullopt;
void *Data = nullptr;
/// Load a (possibly unaligned) source location from a given address
/// and offset.
SourceLocation LoadSourceLocation(unsigned Offset) const {
SourceLocation::UIntTy Raw;
memcpy(&Raw, static_cast<char *>(Data) + Offset, sizeof(Raw));
return SourceLocation::getFromRawEncoding(Raw);
}
/// Load a (possibly unaligned) pointer from a given address and
/// offset.
void *LoadPointer(unsigned Offset) const {
void *Result;
memcpy(&Result, static_cast<char *>(Data) + Offset, sizeof(void *));
return Result;
}
/// Determines the data length for the last component in the
/// given nested-name-specifier.
static inline unsigned getLocalDataLength(NestedNameSpecifier Qualifier);
/// Determines the data length for the entire
/// nested-name-specifier.
static inline unsigned getDataLength(NestedNameSpecifier Qualifier);
public:
/// Construct an empty nested-name-specifier.
NestedNameSpecifierLoc() = default;
/// Construct a nested-name-specifier with source location information
/// from
NestedNameSpecifierLoc(NestedNameSpecifier Qualifier, void *Data)
: Qualifier(Qualifier), Data(Data) {}
/// Evaluates true when this nested-name-specifier location is
/// non-empty.
explicit operator bool() const { return bool(Qualifier); }
/// Evaluates true when this nested-name-specifier location is
/// non-empty.
bool hasQualifier() const { return bool(Qualifier); }
/// Retrieve the nested-name-specifier to which this instance
/// refers.
NestedNameSpecifier getNestedNameSpecifier() const { return Qualifier; }
/// Retrieve the opaque pointer that refers to source-location data.
void *getOpaqueData() const { return Data; }
/// Retrieve the source range covering the entirety of this
/// nested-name-specifier.
///
/// For example, if this instance refers to a nested-name-specifier
/// \c \::std::vector<int>::, the returned source range would cover
/// from the initial '::' to the last '::'.
inline SourceRange getSourceRange() const LLVM_READONLY;
/// Retrieve the source range covering just the last part of
/// this nested-name-specifier, not including the prefix.
///
/// For example, if this instance refers to a nested-name-specifier
/// \c \::std::vector<int>::, the returned source range would cover
/// from "vector" to the last '::'.
inline SourceRange getLocalSourceRange() const;
/// Retrieve the location of the beginning of this
/// nested-name-specifier.
SourceLocation getBeginLoc() const;
/// Retrieve the location of the end of this
/// nested-name-specifier.
inline SourceLocation getEndLoc() const;
/// Retrieve the location of the beginning of this
/// component of the nested-name-specifier.
inline SourceLocation getLocalBeginLoc() const;
/// Retrieve the location of the end of this component of the
/// nested-name-specifier.
inline SourceLocation getLocalEndLoc() const;
/// For a nested-name-specifier that refers to a namespace,
/// retrieve the namespace and its prefix.
///
/// For example, if this instance refers to a nested-name-specifier
/// \c \::std::chrono::, the prefix is \c \::std::. Note that the
/// returned prefix may be empty, if this is the first component of
/// the nested-name-specifier.
inline NamespaceAndPrefixLoc castAsNamespaceAndPrefix() const;
inline NamespaceAndPrefixLoc getAsNamespaceAndPrefix() const;
/// For a nested-name-specifier that refers to a type,
/// retrieve the type with source-location information.
inline TypeLoc castAsTypeLoc() const;
inline TypeLoc getAsTypeLoc() const;
/// Determines the data length for the entire
/// nested-name-specifier.
inline unsigned getDataLength() const;
friend bool operator==(NestedNameSpecifierLoc X, NestedNameSpecifierLoc Y) {
return X.Qualifier == Y.Qualifier && X.Data == Y.Data;
}
friend bool operator!=(NestedNameSpecifierLoc X, NestedNameSpecifierLoc Y) {
return !(X == Y);
}
};
struct NamespaceAndPrefixLoc {
const NamespaceBaseDecl *Namespace = nullptr;
NestedNameSpecifierLoc Prefix;
explicit operator bool() const { return Namespace != nullptr; }
};
/// Class that aids in the construction of nested-name-specifiers along
/// with source-location information for all of the components of the
/// nested-name-specifier.
class NestedNameSpecifierLocBuilder {
/// The current representation of the nested-name-specifier we're
/// building.
NestedNameSpecifier Representation = std::nullopt;
/// Buffer used to store source-location information for the
/// nested-name-specifier.
///
/// Note that we explicitly manage the buffer (rather than using a
/// SmallVector) because \c Declarator expects it to be possible to memcpy()
/// a \c CXXScopeSpec, and CXXScopeSpec uses a NestedNameSpecifierLocBuilder.
char *Buffer = nullptr;
/// The size of the buffer used to store source-location information
/// for the nested-name-specifier.
unsigned BufferSize = 0;
/// The capacity of the buffer used to store source-location
/// information for the nested-name-specifier.
unsigned BufferCapacity = 0;
void PushTrivial(ASTContext &Context, NestedNameSpecifier Qualifier,
SourceRange R);
public:
NestedNameSpecifierLocBuilder() = default;
NestedNameSpecifierLocBuilder(const NestedNameSpecifierLocBuilder &Other);
NestedNameSpecifierLocBuilder &
operator=(const NestedNameSpecifierLocBuilder &Other);
~NestedNameSpecifierLocBuilder() {
if (BufferCapacity)
free(Buffer);
}
/// Retrieve the representation of the nested-name-specifier.
NestedNameSpecifier getRepresentation() const { return Representation; }
/// Make a nested-name-specifier of the form 'type::'.
///
/// \param Context The AST context in which this nested-name-specifier
/// resides.
///
/// \param TL The TypeLoc that describes the type preceding the '::'.
///
/// \param ColonColonLoc The location of the trailing '::'.
void Make(ASTContext &Context, TypeLoc TL, SourceLocation ColonColonLoc);
/// Extend the current nested-name-specifier by another
/// nested-name-specifier component of the form 'namespace::'.
///
/// \param Context The AST context in which this nested-name-specifier
/// resides.
///
/// \param Namespace The namespace.
///
/// \param NamespaceLoc The location of the namespace name.
///
/// \param ColonColonLoc The location of the trailing '::'.
void Extend(ASTContext &Context, const NamespaceBaseDecl *Namespace,
SourceLocation NamespaceLoc, SourceLocation ColonColonLoc);
/// Turn this (empty) nested-name-specifier into the global
/// nested-name-specifier '::'.
void MakeGlobal(ASTContext &Context, SourceLocation ColonColonLoc);
/// Turns this (empty) nested-name-specifier into '__super'
/// nested-name-specifier.
///
/// \param Context The AST context in which this nested-name-specifier
/// resides.
///
/// \param RD The declaration of the class in which nested-name-specifier
/// appeared.
///
/// \param SuperLoc The location of the '__super' keyword.
/// name.
///
/// \param ColonColonLoc The location of the trailing '::'.
void MakeMicrosoftSuper(ASTContext &Context, CXXRecordDecl *RD,
SourceLocation SuperLoc,
SourceLocation ColonColonLoc);
/// Make a new nested-name-specifier from incomplete source-location
/// information.
///
/// This routine should be used very, very rarely, in cases where we
/// need to synthesize a nested-name-specifier. Most code should instead use
/// \c Adopt() with a proper \c NestedNameSpecifierLoc.
void MakeTrivial(ASTContext &Context, NestedNameSpecifier Qualifier,
SourceRange R) {
Representation = Qualifier;
BufferSize = 0;
PushTrivial(Context, Qualifier, R);
}
/// Adopt an existing nested-name-specifier (with source-range
/// information).
void Adopt(NestedNameSpecifierLoc Other);
/// Retrieve the source range covered by this nested-name-specifier.
inline SourceRange getSourceRange() const LLVM_READONLY;
/// Retrieve a nested-name-specifier with location information,
/// copied into the given AST context.
///
/// \param Context The context into which this nested-name-specifier will be
/// copied.
NestedNameSpecifierLoc getWithLocInContext(ASTContext &Context) const;
/// Retrieve a nested-name-specifier with location
/// information based on the information in this builder.
///
/// This loc will contain references to the builder's internal data and may
/// be invalidated by any change to the builder.
NestedNameSpecifierLoc getTemporary() const {
return NestedNameSpecifierLoc(Representation, Buffer);
}
/// Clear out this builder, and prepare it to build another
/// nested-name-specifier with source-location information.
void Clear() {
Representation = std::nullopt;
BufferSize = 0;
}
/// Retrieve the underlying buffer.
///
/// \returns A pair containing a pointer to the buffer of source-location
/// data and the size of the source-location data that resides in that
/// buffer.
std::pair<char *, unsigned> getBuffer() const {
return std::make_pair(Buffer, BufferSize);
}
};
/// Insertion operator for diagnostics. This allows sending
/// NestedNameSpecifiers into a diagnostic with <<.
inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB,
NestedNameSpecifier NNS) {
DB.AddTaggedVal(reinterpret_cast<uintptr_t>(NNS.getAsVoidPointer()),
DiagnosticsEngine::ak_nestednamespec);
return DB;
}
} // namespace clang
namespace llvm {
template <> struct PointerLikeTypeTraits<clang::NestedNameSpecifier> {
static void *getAsVoidPointer(clang::NestedNameSpecifier P) {
return P.getAsVoidPointer();
}
static clang::NestedNameSpecifier getFromVoidPointer(const void *P) {
return clang::NestedNameSpecifier::getFromVoidPointer(P);
}
static constexpr int NumLowBitsAvailable =
clang::NestedNameSpecifier::NumLowBitsAvailable;
};
} // namespace llvm
#endif // LLVM_CLANG_AST_NESTEDNAMESPECIFIERBASE_H

View File

@ -93,7 +93,7 @@ public:
void AddQualType(QualType T); void AddQualType(QualType T);
void AddStmt(const Stmt *S); void AddStmt(const Stmt *S);
void AddIdentifierInfo(const IdentifierInfo *II); void AddIdentifierInfo(const IdentifierInfo *II);
void AddNestedNameSpecifier(const NestedNameSpecifier *NNS); void AddNestedNameSpecifier(NestedNameSpecifier NNS);
void AddDependentTemplateName(const DependentTemplateStorage &Name); void AddDependentTemplateName(const DependentTemplateStorage &Name);
void AddTemplateName(TemplateName Name); void AddTemplateName(TemplateName Name);
void AddDeclarationNameInfo(DeclarationNameInfo NameInfo, void AddDeclarationNameInfo(DeclarationNameInfo NameInfo,

View File

@ -63,9 +63,9 @@ struct PrintingPolicy {
SuppressTagKeyword(LO.CPlusPlus), IncludeTagDefinition(false), SuppressTagKeyword(LO.CPlusPlus), IncludeTagDefinition(false),
SuppressScope(false), SuppressUnwrittenScope(false), SuppressScope(false), SuppressUnwrittenScope(false),
SuppressInlineNamespace(SuppressInlineNamespaceMode::Redundant), SuppressInlineNamespace(SuppressInlineNamespaceMode::Redundant),
SuppressElaboration(false), SuppressInitializers(false), SuppressInitializers(false), ConstantArraySizeAsWritten(false),
ConstantArraySizeAsWritten(false), AnonymousTagLocations(true), AnonymousTagLocations(true), SuppressStrongLifetime(false),
SuppressStrongLifetime(false), SuppressLifetimeQualifiers(false), SuppressLifetimeQualifiers(false),
SuppressTemplateArgsInCXXConstructors(false), SuppressTemplateArgsInCXXConstructors(false),
SuppressDefaultTemplateArgs(true), Bool(LO.Bool), SuppressDefaultTemplateArgs(true), Bool(LO.Bool),
Nullptr(LO.CPlusPlus11 || LO.C23), NullptrTypeInNamespace(LO.CPlusPlus), Nullptr(LO.CPlusPlus11 || LO.C23), NullptrTypeInNamespace(LO.CPlusPlus),
@ -150,11 +150,6 @@ struct PrintingPolicy {
LLVM_PREFERRED_TYPE(SuppressInlineNamespaceMode) LLVM_PREFERRED_TYPE(SuppressInlineNamespaceMode)
unsigned SuppressInlineNamespace : 2; unsigned SuppressInlineNamespace : 2;
/// Ignore qualifiers and tag keywords as specified by elaborated type sugar,
/// instead letting the underlying type print as normal.
LLVM_PREFERRED_TYPE(bool)
unsigned SuppressElaboration : 1;
/// Suppress printing of variable initializers. /// Suppress printing of variable initializers.
/// ///
/// This flag is used when printing the loop variable in a for-range /// This flag is used when printing the loop variable in a for-range

View File

@ -127,9 +127,8 @@ def LValuePathSerializationHelper :
PropertyType<"APValue::LValuePathSerializationHelper"> { PropertyType<"APValue::LValuePathSerializationHelper"> {
let BufferElementTypes = [ LValuePathEntry ]; let BufferElementTypes = [ LValuePathEntry ];
} }
def NestedNameSpecifier : PropertyType<"NestedNameSpecifier *">; def NestedNameSpecifier : PropertyType<"NestedNameSpecifier">;
def NestedNameSpecifierKind : def NestedNameSpecifierKind : EnumPropertyType<"NestedNameSpecifier::Kind">;
EnumPropertyType<"NestedNameSpecifier::SpecifierKind">;
def OverloadedOperatorKind : EnumPropertyType; def OverloadedOperatorKind : EnumPropertyType;
def Qualifiers : PropertyType; def Qualifiers : PropertyType;
def QualType : DefaultValuePropertyType; def QualType : DefaultValuePropertyType;

View File

@ -87,6 +87,16 @@ std::string getFullyQualifiedName(QualType QT, const ASTContext &Ctx,
/// specifier "::" should be prepended or not. /// specifier "::" should be prepended or not.
QualType getFullyQualifiedType(QualType QT, const ASTContext &Ctx, QualType getFullyQualifiedType(QualType QT, const ASTContext &Ctx,
bool WithGlobalNsPrefix = false); bool WithGlobalNsPrefix = false);
/// Get the fully qualified name for the declared context of a declaration.
///
/// \param[in] Ctx - the ASTContext to be used.
/// \param[in] Decl - the declaration for which to get the fully qualified name.
/// \param[in] WithGlobalNsPrefix - If true, then the global namespace
/// specifier "::" will be prepended to the fully qualified name.
NestedNameSpecifier
getFullyQualifiedDeclaredContext(const ASTContext &Ctx, const Decl *Decl,
bool WithGlobalNsPrefix = false);
} // end namespace TypeName } // end namespace TypeName
} // end namespace clang } // end namespace clang
#endif // LLVM_CLANG_AST_QUALTYPENAMES_H #endif // LLVM_CLANG_AST_QUALTYPENAMES_H

View File

@ -216,14 +216,14 @@ public:
/// ///
/// \returns false if the visitation was terminated early, true /// \returns false if the visitation was terminated early, true
/// otherwise (including when the argument is a Null type). /// otherwise (including when the argument is a Null type).
bool TraverseType(QualType T); bool TraverseType(QualType T, bool TraverseQualifier = true);
/// Recursively visit a type with location, by dispatching to /// Recursively visit a type with location, by dispatching to
/// Traverse*TypeLoc() based on the argument type's getTypeClass() property. /// Traverse*TypeLoc() based on the argument type's getTypeClass() property.
/// ///
/// \returns false if the visitation was terminated early, true /// \returns false if the visitation was terminated early, true
/// otherwise (including when the argument is a Null type location). /// otherwise (including when the argument is a Null type location).
bool TraverseTypeLoc(TypeLoc TL); bool TraverseTypeLoc(TypeLoc TL, bool TraverseQualifier = true);
/// Recursively visit an attribute, by dispatching to /// Recursively visit an attribute, by dispatching to
/// Traverse*Attr() based on the argument's dynamic type. /// Traverse*Attr() based on the argument's dynamic type.
@ -242,7 +242,7 @@ public:
/// Recursively visit a C++ nested-name-specifier. /// Recursively visit a C++ nested-name-specifier.
/// ///
/// \returns false if the visitation was terminated early, true otherwise. /// \returns false if the visitation was terminated early, true otherwise.
bool TraverseNestedNameSpecifier(NestedNameSpecifier *NNS); bool TraverseNestedNameSpecifier(NestedNameSpecifier NNS);
/// Recursively visit a C++ nested-name-specifier with location /// Recursively visit a C++ nested-name-specifier with location
/// information. /// information.
@ -389,7 +389,8 @@ public:
// Declare Traverse*() for all concrete Type classes. // Declare Traverse*() for all concrete Type classes.
#define ABSTRACT_TYPE(CLASS, BASE) #define ABSTRACT_TYPE(CLASS, BASE)
#define TYPE(CLASS, BASE) bool Traverse##CLASS##Type(CLASS##Type *T); #define TYPE(CLASS, BASE) \
bool Traverse##CLASS##Type(CLASS##Type *T, bool TraverseQualifier);
#include "clang/AST/TypeNodes.inc" #include "clang/AST/TypeNodes.inc"
// The above header #undefs ABSTRACT_TYPE and TYPE upon exit. // The above header #undefs ABSTRACT_TYPE and TYPE upon exit.
@ -410,7 +411,8 @@ public:
// Declare Traverse*() for all concrete TypeLoc classes. // Declare Traverse*() for all concrete TypeLoc classes.
#define ABSTRACT_TYPELOC(CLASS, BASE) #define ABSTRACT_TYPELOC(CLASS, BASE)
#define TYPELOC(CLASS, BASE) bool Traverse##CLASS##TypeLoc(CLASS##TypeLoc TL); #define TYPELOC(CLASS, BASE) \
bool Traverse##CLASS##TypeLoc(CLASS##TypeLoc TL, bool TraverseQualifier);
#include "clang/AST/TypeLocNodes.def" #include "clang/AST/TypeLocNodes.def"
// The above header #undefs ABSTRACT_TYPELOC and TYPELOC upon exit. // The above header #undefs ABSTRACT_TYPELOC and TYPELOC upon exit.
@ -499,6 +501,8 @@ private:
bool TraverseOMPExecutableDirective(OMPExecutableDirective *S); bool TraverseOMPExecutableDirective(OMPExecutableDirective *S);
bool TraverseOMPLoopDirective(OMPLoopDirective *S); bool TraverseOMPLoopDirective(OMPLoopDirective *S);
bool TraverseOMPClause(OMPClause *C); bool TraverseOMPClause(OMPClause *C);
bool TraverseTagType(TagType *T, bool TraverseQualifier);
bool TraverseTagTypeLoc(TagTypeLoc TL, bool TraverseQualifier);
#define GEN_CLANG_CLAUSE_CLASS #define GEN_CLANG_CLAUSE_CLASS
#define CLAUSE_CLASS(Enum, Str, Class) bool Visit##Class(Class *C); #define CLAUSE_CLASS(Enum, Str, Class) bool Visit##Class(Class *C);
#include "llvm/Frontend/OpenMP/OMP.inc" #include "llvm/Frontend/OpenMP/OMP.inc"
@ -698,7 +702,8 @@ RecursiveASTVisitor<Derived>::TraverseStmt(Stmt *S, DataRecursionQueue *Queue) {
} }
template <typename Derived> template <typename Derived>
bool RecursiveASTVisitor<Derived>::TraverseType(QualType T) { bool RecursiveASTVisitor<Derived>::TraverseType(QualType T,
bool TraverseQualifier) {
if (T.isNull()) if (T.isNull())
return true; return true;
@ -707,7 +712,8 @@ bool RecursiveASTVisitor<Derived>::TraverseType(QualType T) {
#define TYPE(CLASS, BASE) \ #define TYPE(CLASS, BASE) \
case Type::CLASS: \ case Type::CLASS: \
return getDerived().Traverse##CLASS##Type( \ return getDerived().Traverse##CLASS##Type( \
static_cast<CLASS##Type *>(const_cast<Type *>(T.getTypePtr()))); static_cast<CLASS##Type *>(const_cast<Type *>(T.getTypePtr())), \
TraverseQualifier);
#include "clang/AST/TypeNodes.inc" #include "clang/AST/TypeNodes.inc"
} }
@ -715,7 +721,8 @@ bool RecursiveASTVisitor<Derived>::TraverseType(QualType T) {
} }
template <typename Derived> template <typename Derived>
bool RecursiveASTVisitor<Derived>::TraverseTypeLoc(TypeLoc TL) { bool RecursiveASTVisitor<Derived>::TraverseTypeLoc(TypeLoc TL,
bool TraverseQualifier) {
if (TL.isNull()) if (TL.isNull())
return true; return true;
@ -723,7 +730,8 @@ bool RecursiveASTVisitor<Derived>::TraverseTypeLoc(TypeLoc TL) {
#define ABSTRACT_TYPELOC(CLASS, BASE) #define ABSTRACT_TYPELOC(CLASS, BASE)
#define TYPELOC(CLASS, BASE) \ #define TYPELOC(CLASS, BASE) \
case TypeLoc::CLASS: \ case TypeLoc::CLASS: \
return getDerived().Traverse##CLASS##TypeLoc(TL.castAs<CLASS##TypeLoc>()); return getDerived().Traverse##CLASS##TypeLoc(TL.castAs<CLASS##TypeLoc>(), \
TraverseQualifier);
#include "clang/AST/TypeLocNodes.def" #include "clang/AST/TypeLocNodes.def"
} }
@ -779,46 +787,43 @@ bool RecursiveASTVisitor<Derived>::TraverseDecl(Decl *D) {
template <typename Derived> template <typename Derived>
bool RecursiveASTVisitor<Derived>::TraverseNestedNameSpecifier( bool RecursiveASTVisitor<Derived>::TraverseNestedNameSpecifier(
NestedNameSpecifier *NNS) { NestedNameSpecifier NNS) {
if (!NNS) switch (NNS.getKind()) {
case NestedNameSpecifier::Kind::Null:
case NestedNameSpecifier::Kind::Global:
case NestedNameSpecifier::Kind::MicrosoftSuper:
return true; return true;
case NestedNameSpecifier::Kind::Namespace:
if (NNS->getPrefix()) TRY_TO(TraverseNestedNameSpecifier(NNS.getAsNamespaceAndPrefix().Prefix));
TRY_TO(TraverseNestedNameSpecifier(NNS->getPrefix())); return true;
case NestedNameSpecifier::Kind::Type: {
switch (NNS->getKind()) { auto *T = const_cast<Type *>(NNS.getAsType());
case NestedNameSpecifier::Identifier: TRY_TO(TraverseNestedNameSpecifier(T->getPrefix()));
case NestedNameSpecifier::Namespace: TRY_TO(TraverseType(QualType(T, 0), /*TraverseQualifier=*/false));
case NestedNameSpecifier::Global:
case NestedNameSpecifier::Super:
return true; return true;
case NestedNameSpecifier::TypeSpec:
TRY_TO(TraverseType(QualType(NNS->getAsType(), 0)));
} }
}
return true; llvm_unreachable("unhandled kind");
} }
template <typename Derived> template <typename Derived>
bool RecursiveASTVisitor<Derived>::TraverseNestedNameSpecifierLoc( bool RecursiveASTVisitor<Derived>::TraverseNestedNameSpecifierLoc(
NestedNameSpecifierLoc NNS) { NestedNameSpecifierLoc NNS) {
if (!NNS) switch (NNS.getNestedNameSpecifier().getKind()) {
case NestedNameSpecifier::Kind::Null:
case NestedNameSpecifier::Kind::Global:
case NestedNameSpecifier::Kind::MicrosoftSuper:
return true; return true;
case NestedNameSpecifier::Kind::Namespace:
if (NestedNameSpecifierLoc Prefix = NNS.getPrefix()) TRY_TO(
TRY_TO(TraverseNestedNameSpecifierLoc(Prefix)); TraverseNestedNameSpecifierLoc(NNS.castAsNamespaceAndPrefix().Prefix));
switch (NNS.getNestedNameSpecifier()->getKind()) {
case NestedNameSpecifier::Identifier:
case NestedNameSpecifier::Namespace:
case NestedNameSpecifier::Global:
case NestedNameSpecifier::Super:
return true; return true;
case NestedNameSpecifier::Kind::Type: {
case NestedNameSpecifier::TypeSpec: TypeLoc TL = NNS.castAsTypeLoc();
TRY_TO(TraverseTypeLoc(NNS.getTypeLoc())); TRY_TO(TraverseNestedNameSpecifierLoc(TL.getPrefix()));
break; TRY_TO(TraverseTypeLoc(TL, /*TraverseQualifier=*/false));
return true;
}
} }
return true; return true;
@ -975,10 +980,13 @@ RecursiveASTVisitor<Derived>::TraverseLambdaCapture(LambdaExpr *LE,
// This macro makes available a variable T, the passed-in type. // This macro makes available a variable T, the passed-in type.
#define DEF_TRAVERSE_TYPE(TYPE, CODE) \ #define DEF_TRAVERSE_TYPE(TYPE, CODE) \
template <typename Derived> \ template <typename Derived> \
bool RecursiveASTVisitor<Derived>::Traverse##TYPE(TYPE *T) { \ bool RecursiveASTVisitor<Derived>::Traverse##TYPE(TYPE *T, \
bool TraverseQualifier) { \
if (!getDerived().shouldTraversePostOrder()) \ if (!getDerived().shouldTraversePostOrder()) \
TRY_TO(WalkUpFrom##TYPE(T)); \ TRY_TO(WalkUpFrom##TYPE(T)); \
{ CODE; } \ { \
CODE; \
} \
if (getDerived().shouldTraversePostOrder()) \ if (getDerived().shouldTraversePostOrder()) \
TRY_TO(WalkUpFrom##TYPE(T)); \ TRY_TO(WalkUpFrom##TYPE(T)); \
return true; \ return true; \
@ -1000,10 +1008,11 @@ DEF_TRAVERSE_TYPE(RValueReferenceType,
{ TRY_TO(TraverseType(T->getPointeeType())); }) { TRY_TO(TraverseType(T->getPointeeType())); })
DEF_TRAVERSE_TYPE(MemberPointerType, { DEF_TRAVERSE_TYPE(MemberPointerType, {
TRY_TO(TraverseNestedNameSpecifier(T->getQualifier())); NestedNameSpecifier Qualifier =
if (T->isSugared()) T->isSugared() ? cast<MemberPointerType>(T->getCanonicalTypeUnqualified())
TRY_TO(TraverseType( ->getQualifier()
QualType(T->getMostRecentCXXRecordDecl()->getTypeForDecl(), 0))); : T->getQualifier();
TRY_TO(TraverseNestedNameSpecifier(Qualifier));
TRY_TO(TraverseType(T->getPointeeType())); TRY_TO(TraverseType(T->getPointeeType()));
}) })
@ -1087,9 +1096,18 @@ DEF_TRAVERSE_TYPE(FunctionProtoType, {
TRY_TO(TraverseStmt(NE)); TRY_TO(TraverseStmt(NE));
}) })
DEF_TRAVERSE_TYPE(UsingType, {}) DEF_TRAVERSE_TYPE(UsingType, {
DEF_TRAVERSE_TYPE(UnresolvedUsingType, {}) if (TraverseQualifier)
DEF_TRAVERSE_TYPE(TypedefType, {}) TRY_TO(TraverseNestedNameSpecifier(T->getQualifier()));
})
DEF_TRAVERSE_TYPE(UnresolvedUsingType, {
if (TraverseQualifier)
TRY_TO(TraverseNestedNameSpecifier(T->getQualifier()));
})
DEF_TRAVERSE_TYPE(TypedefType, {
if (TraverseQualifier)
TRY_TO(TraverseNestedNameSpecifier(T->getQualifier()));
})
DEF_TRAVERSE_TYPE(TypeOfExprType, DEF_TRAVERSE_TYPE(TypeOfExprType,
{ TRY_TO(TraverseStmt(T->getUnderlyingExpr())); }) { TRY_TO(TraverseStmt(T->getUnderlyingExpr())); })
@ -1115,13 +1133,7 @@ DEF_TRAVERSE_TYPE(AutoType, {
TRY_TO(TraverseTemplateArguments(T->getTypeConstraintArguments())); TRY_TO(TraverseTemplateArguments(T->getTypeConstraintArguments()));
} }
}) })
DEF_TRAVERSE_TYPE(DeducedTemplateSpecializationType, {
TRY_TO(TraverseTemplateName(T->getTemplateName()));
TRY_TO(TraverseType(T->getDeducedType()));
})
DEF_TRAVERSE_TYPE(RecordType, {})
DEF_TRAVERSE_TYPE(EnumType, {})
DEF_TRAVERSE_TYPE(TemplateTypeParmType, {}) DEF_TRAVERSE_TYPE(TemplateTypeParmType, {})
DEF_TRAVERSE_TYPE(SubstTemplateTypeParmType, { DEF_TRAVERSE_TYPE(SubstTemplateTypeParmType, {
TRY_TO(TraverseType(T->getReplacementType())); TRY_TO(TraverseType(T->getReplacementType()));
@ -1130,13 +1142,6 @@ DEF_TRAVERSE_TYPE(SubstTemplateTypeParmPackType, {
TRY_TO(TraverseTemplateArgument(T->getArgumentPack())); TRY_TO(TraverseTemplateArgument(T->getArgumentPack()));
}) })
DEF_TRAVERSE_TYPE(TemplateSpecializationType, {
TRY_TO(TraverseTemplateName(T->getTemplateName()));
TRY_TO(TraverseTemplateArguments(T->template_arguments()));
})
DEF_TRAVERSE_TYPE(InjectedClassNameType, {})
DEF_TRAVERSE_TYPE(AttributedType, DEF_TRAVERSE_TYPE(AttributedType,
{ TRY_TO(TraverseType(T->getModifiedType())); }) { TRY_TO(TraverseType(T->getModifiedType())); })
@ -1165,22 +1170,54 @@ DEF_TRAVERSE_TYPE(ParenType, { TRY_TO(TraverseType(T->getInnerType())); })
DEF_TRAVERSE_TYPE(MacroQualifiedType, DEF_TRAVERSE_TYPE(MacroQualifiedType,
{ TRY_TO(TraverseType(T->getUnderlyingType())); }) { TRY_TO(TraverseType(T->getUnderlyingType())); })
DEF_TRAVERSE_TYPE(ElaboratedType, { template <typename Derived>
if (T->getQualifier()) { bool RecursiveASTVisitor<Derived>::TraverseTagType(TagType *T,
bool TraverseQualifier) {
if (TraverseQualifier)
TRY_TO(TraverseNestedNameSpecifier(T->getQualifier())); TRY_TO(TraverseNestedNameSpecifier(T->getQualifier()));
} return true;
TRY_TO(TraverseType(T->getNamedType())); }
})
DEF_TRAVERSE_TYPE(DependentNameType, DEF_TRAVERSE_TYPE(EnumType, { TRY_TO(TraverseTagType(T, TraverseQualifier)); })
{ TRY_TO(TraverseNestedNameSpecifier(T->getQualifier())); }) DEF_TRAVERSE_TYPE(RecordType,
{ TRY_TO(TraverseTagType(T, TraverseQualifier)); })
DEF_TRAVERSE_TYPE(InjectedClassNameType,
{ TRY_TO(TraverseTagType(T, TraverseQualifier)); })
DEF_TRAVERSE_TYPE(DependentNameType, {
if (TraverseQualifier)
TRY_TO(TraverseNestedNameSpecifier(T->getQualifier()));
})
DEF_TRAVERSE_TYPE(DependentTemplateSpecializationType, { DEF_TRAVERSE_TYPE(DependentTemplateSpecializationType, {
const DependentTemplateStorage &S = T->getDependentTemplateName(); const DependentTemplateStorage &S = T->getDependentTemplateName();
TRY_TO(TraverseNestedNameSpecifier(S.getQualifier())); if (TraverseQualifier)
TRY_TO(TraverseNestedNameSpecifier(S.getQualifier()));
TRY_TO(TraverseTemplateArguments(T->template_arguments())); TRY_TO(TraverseTemplateArguments(T->template_arguments()));
}) })
DEF_TRAVERSE_TYPE(TemplateSpecializationType, {
if (TraverseQualifier) {
TRY_TO(TraverseTemplateName(T->getTemplateName()));
} else {
// FIXME: Try to preserve the rest of the template name.
TRY_TO(TraverseTemplateName(TemplateName(
T->getTemplateName().getAsTemplateDecl(/*IgnoreDeduced=*/true))));
}
TRY_TO(TraverseTemplateArguments(T->template_arguments()));
})
DEF_TRAVERSE_TYPE(DeducedTemplateSpecializationType, {
if (TraverseQualifier) {
TRY_TO(TraverseTemplateName(T->getTemplateName()));
} else {
// FIXME: Try to preserve the rest of the template name.
TRY_TO(TraverseTemplateName(TemplateName(
T->getTemplateName().getAsTemplateDecl(/*IgnoreDeduced=*/true))));
}
TRY_TO(TraverseType(T->getDeducedType()));
})
DEF_TRAVERSE_TYPE(PackExpansionType, { TRY_TO(TraverseType(T->getPattern())); }) DEF_TRAVERSE_TYPE(PackExpansionType, { TRY_TO(TraverseType(T->getPattern())); })
DEF_TRAVERSE_TYPE(ObjCTypeParamType, {}) DEF_TRAVERSE_TYPE(ObjCTypeParamType, {})
@ -1221,13 +1258,16 @@ DEF_TRAVERSE_TYPE(PredefinedSugarType, {})
// continue to work. // continue to work.
#define DEF_TRAVERSE_TYPELOC(TYPE, CODE) \ #define DEF_TRAVERSE_TYPELOC(TYPE, CODE) \
template <typename Derived> \ template <typename Derived> \
bool RecursiveASTVisitor<Derived>::Traverse##TYPE##Loc(TYPE##Loc TL) { \ bool RecursiveASTVisitor<Derived>::Traverse##TYPE##Loc( \
TYPE##Loc TL, bool TraverseQualifier) { \
if (!getDerived().shouldTraversePostOrder()) { \ if (!getDerived().shouldTraversePostOrder()) { \
TRY_TO(WalkUpFrom##TYPE##Loc(TL)); \ TRY_TO(WalkUpFrom##TYPE##Loc(TL)); \
if (getDerived().shouldWalkTypesOfTypeLocs()) \ if (getDerived().shouldWalkTypesOfTypeLocs()) \
TRY_TO(WalkUpFrom##TYPE(const_cast<TYPE *>(TL.getTypePtr()))); \ TRY_TO(WalkUpFrom##TYPE(const_cast<TYPE *>(TL.getTypePtr()))); \
} \ } \
{ CODE; } \ { \
CODE; \
} \
if (getDerived().shouldTraversePostOrder()) { \ if (getDerived().shouldTraversePostOrder()) { \
TRY_TO(WalkUpFrom##TYPE##Loc(TL)); \ TRY_TO(WalkUpFrom##TYPE##Loc(TL)); \
if (getDerived().shouldWalkTypesOfTypeLocs()) \ if (getDerived().shouldWalkTypesOfTypeLocs()) \
@ -1237,8 +1277,10 @@ DEF_TRAVERSE_TYPE(PredefinedSugarType, {})
} }
template <typename Derived> template <typename Derived>
bool bool RecursiveASTVisitor<Derived>::TraverseQualifiedTypeLoc(
RecursiveASTVisitor<Derived>::TraverseQualifiedTypeLoc(QualifiedTypeLoc TL) { QualifiedTypeLoc TL, bool TraverseQualifier) {
assert(TraverseQualifier &&
"Qualifiers should never occur within NestedNameSpecifiers");
// Move this over to the 'main' typeloc tree. Note that this is a // Move this over to the 'main' typeloc tree. Note that this is a
// move -- we pretend that we were really looking at the unqualified // move -- we pretend that we were really looking at the unqualified
// typeloc all along -- rather than a recursion, so we don't follow // typeloc all along -- rather than a recursion, so we don't follow
@ -1391,9 +1433,21 @@ DEF_TRAVERSE_TYPELOC(FunctionProtoType, {
TRY_TO(TraverseStmt(NE)); TRY_TO(TraverseStmt(NE));
}) })
DEF_TRAVERSE_TYPELOC(UsingType, {}) DEF_TRAVERSE_TYPELOC(UsingType, {
DEF_TRAVERSE_TYPELOC(UnresolvedUsingType, {}) if (NestedNameSpecifierLoc QualifierLoc = TL.getQualifierLoc();
DEF_TRAVERSE_TYPELOC(TypedefType, {}) TraverseQualifier && QualifierLoc)
TRY_TO(TraverseNestedNameSpecifierLoc(QualifierLoc));
})
DEF_TRAVERSE_TYPELOC(UnresolvedUsingType, {
if (NestedNameSpecifierLoc QualifierLoc = TL.getQualifierLoc();
TraverseQualifier && QualifierLoc)
TRY_TO(TraverseNestedNameSpecifierLoc(QualifierLoc));
})
DEF_TRAVERSE_TYPELOC(TypedefType, {
if (NestedNameSpecifierLoc QualifierLoc = TL.getQualifierLoc();
TraverseQualifier && QualifierLoc)
TRY_TO(TraverseNestedNameSpecifierLoc(QualifierLoc));
})
DEF_TRAVERSE_TYPELOC(TypeOfExprType, DEF_TRAVERSE_TYPELOC(TypeOfExprType,
{ TRY_TO(TraverseStmt(TL.getUnderlyingExpr())); }) { TRY_TO(TraverseStmt(TL.getUnderlyingExpr())); })
@ -1423,13 +1477,6 @@ DEF_TRAVERSE_TYPELOC(AutoType, {
} }
}) })
DEF_TRAVERSE_TYPELOC(DeducedTemplateSpecializationType, {
TRY_TO(TraverseTemplateName(TL.getTypePtr()->getTemplateName()));
TRY_TO(TraverseType(TL.getTypePtr()->getDeducedType()));
})
DEF_TRAVERSE_TYPELOC(RecordType, {})
DEF_TRAVERSE_TYPELOC(EnumType, {})
DEF_TRAVERSE_TYPELOC(TemplateTypeParmType, {}) DEF_TRAVERSE_TYPELOC(TemplateTypeParmType, {})
DEF_TRAVERSE_TYPELOC(SubstTemplateTypeParmType, { DEF_TRAVERSE_TYPELOC(SubstTemplateTypeParmType, {
TRY_TO(TraverseType(TL.getTypePtr()->getReplacementType())); TRY_TO(TraverseType(TL.getTypePtr()->getReplacementType()));
@ -1438,16 +1485,6 @@ DEF_TRAVERSE_TYPELOC(SubstTemplateTypeParmPackType, {
TRY_TO(TraverseTemplateArgument(TL.getTypePtr()->getArgumentPack())); TRY_TO(TraverseTemplateArgument(TL.getTypePtr()->getArgumentPack()));
}) })
// FIXME: use the loc for the template name?
DEF_TRAVERSE_TYPELOC(TemplateSpecializationType, {
TRY_TO(TraverseTemplateName(TL.getTypePtr()->getTemplateName()));
for (unsigned I = 0, E = TL.getNumArgs(); I != E; ++I) {
TRY_TO(TraverseTemplateArgumentLoc(TL.getArgLoc(I)));
}
})
DEF_TRAVERSE_TYPELOC(InjectedClassNameType, {})
DEF_TRAVERSE_TYPELOC(ParenType, { TRY_TO(TraverseTypeLoc(TL.getInnerLoc())); }) DEF_TRAVERSE_TYPELOC(ParenType, { TRY_TO(TraverseTypeLoc(TL.getInnerLoc())); })
DEF_TRAVERSE_TYPELOC(MacroQualifiedType, DEF_TRAVERSE_TYPELOC(MacroQualifiedType,
@ -1468,27 +1505,63 @@ DEF_TRAVERSE_TYPELOC(HLSLAttributedResourceType,
DEF_TRAVERSE_TYPELOC(HLSLInlineSpirvType, DEF_TRAVERSE_TYPELOC(HLSLInlineSpirvType,
{ TRY_TO(TraverseType(TL.getType())); }) { TRY_TO(TraverseType(TL.getType())); })
DEF_TRAVERSE_TYPELOC(ElaboratedType, { template <typename Derived>
if (TL.getQualifierLoc()) { bool RecursiveASTVisitor<Derived>::TraverseTagTypeLoc(TagTypeLoc TL,
TRY_TO(TraverseNestedNameSpecifierLoc(TL.getQualifierLoc())); bool TraverseQualifier) {
} if (NestedNameSpecifierLoc QualifierLoc = TL.getQualifierLoc();
TRY_TO(TraverseTypeLoc(TL.getNamedTypeLoc())); TraverseQualifier && QualifierLoc)
}) TRY_TO(TraverseNestedNameSpecifierLoc(QualifierLoc));
return true;
}
DEF_TRAVERSE_TYPELOC(EnumType,
{ TRY_TO(TraverseTagTypeLoc(TL, TraverseQualifier)); })
DEF_TRAVERSE_TYPELOC(RecordType,
{ TRY_TO(TraverseTagTypeLoc(TL, TraverseQualifier)); })
DEF_TRAVERSE_TYPELOC(InjectedClassNameType,
{ TRY_TO(TraverseTagTypeLoc(TL, TraverseQualifier)); })
DEF_TRAVERSE_TYPELOC(DependentNameType, { DEF_TRAVERSE_TYPELOC(DependentNameType, {
TRY_TO(TraverseNestedNameSpecifierLoc(TL.getQualifierLoc())); if (TraverseQualifier)
TRY_TO(TraverseNestedNameSpecifierLoc(TL.getQualifierLoc()));
}) })
DEF_TRAVERSE_TYPELOC(DependentTemplateSpecializationType, { DEF_TRAVERSE_TYPELOC(DependentTemplateSpecializationType, {
if (TL.getQualifierLoc()) { if (TraverseQualifier)
TRY_TO(TraverseNestedNameSpecifierLoc(TL.getQualifierLoc())); TRY_TO(TraverseNestedNameSpecifierLoc(TL.getQualifierLoc()));
}
for (unsigned I = 0, E = TL.getNumArgs(); I != E; ++I) { for (unsigned I = 0, E = TL.getNumArgs(); I != E; ++I) {
TRY_TO(TraverseTemplateArgumentLoc(TL.getArgLoc(I))); TRY_TO(TraverseTemplateArgumentLoc(TL.getArgLoc(I)));
} }
}) })
DEF_TRAVERSE_TYPELOC(TemplateSpecializationType, {
if (TraverseQualifier)
TRY_TO(TraverseNestedNameSpecifierLoc(TL.getQualifierLoc()));
// FIXME: Try to preserve the rest of the template name.
TRY_TO(TraverseTemplateName(
TemplateName(TL.getTypePtr()->getTemplateName().getAsTemplateDecl(
/*IgnoreDeduced=*/true))));
for (unsigned I = 0, E = TL.getNumArgs(); I != E; ++I) {
TRY_TO(TraverseTemplateArgumentLoc(TL.getArgLoc(I)));
}
})
DEF_TRAVERSE_TYPELOC(DeducedTemplateSpecializationType, {
if (TraverseQualifier)
TRY_TO(TraverseNestedNameSpecifierLoc(TL.getQualifierLoc()));
const auto *T = TL.getTypePtr();
// FIXME: Try to preserve the rest of the template name.
TRY_TO(
TraverseTemplateName(TemplateName(T->getTemplateName().getAsTemplateDecl(
/*IgnoreDeduced=*/true))));
TRY_TO(TraverseType(T->getDeducedType()));
})
DEF_TRAVERSE_TYPELOC(PackExpansionType, DEF_TRAVERSE_TYPELOC(PackExpansionType,
{ TRY_TO(TraverseTypeLoc(TL.getPatternLoc())); }) { TRY_TO(TraverseTypeLoc(TL.getPatternLoc())); })
@ -1631,8 +1704,9 @@ DEF_TRAVERSE_DECL(FriendDecl, {
TRY_TO(TraverseTypeLoc(D->getFriendType()->getTypeLoc())); TRY_TO(TraverseTypeLoc(D->getFriendType()->getTypeLoc()));
// Traverse any CXXRecordDecl owned by this type, since // Traverse any CXXRecordDecl owned by this type, since
// it will not be in the parent context: // it will not be in the parent context:
if (auto *ET = D->getFriendType()->getType()->getAs<ElaboratedType>()) if (auto *TT = D->getFriendType()->getType()->getAs<TagType>();
TRY_TO(TraverseDecl(ET->getOwnedTagDecl())); TT && TT->isTagOwned())
TRY_TO(TraverseDecl(TT->getOriginalDecl()));
} else { } else {
TRY_TO(TraverseDecl(D->getFriendDecl())); TRY_TO(TraverseDecl(D->getFriendDecl()));
} }

View File

@ -15,7 +15,7 @@
#define LLVM_CLANG_AST_TEMPLATEBASE_H #define LLVM_CLANG_AST_TEMPLATEBASE_H
#include "clang/AST/DependenceFlags.h" #include "clang/AST/DependenceFlags.h"
#include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/NestedNameSpecifierBase.h"
#include "clang/AST/TemplateName.h" #include "clang/AST/TemplateName.h"
#include "clang/AST/Type.h" #include "clang/AST/Type.h"
#include "clang/Basic/LLVM.h" #include "clang/Basic/LLVM.h"
@ -478,31 +478,25 @@ public:
/// Location information for a TemplateArgument. /// Location information for a TemplateArgument.
struct TemplateArgumentLocInfo { struct TemplateArgumentLocInfo {
private:
struct TemplateTemplateArgLocInfo { struct TemplateTemplateArgLocInfo {
// FIXME: We'd like to just use the qualifier in the TemplateName,
// but template arguments get canonicalized too quickly.
NestedNameSpecifier *Qualifier;
void *QualifierLocData; void *QualifierLocData;
SourceLocation TemplateKwLoc;
SourceLocation TemplateNameLoc; SourceLocation TemplateNameLoc;
SourceLocation EllipsisLoc; SourceLocation EllipsisLoc;
}; };
llvm::PointerUnion<TemplateTemplateArgLocInfo *, Expr *, TypeSourceInfo *>
Pointer;
TemplateTemplateArgLocInfo *getTemplate() const { TemplateTemplateArgLocInfo *getTemplate() const {
return cast<TemplateTemplateArgLocInfo *>(Pointer); return cast<TemplateTemplateArgLocInfo *>(Pointer);
} }
public:
TemplateArgumentLocInfo() {} TemplateArgumentLocInfo() {}
TemplateArgumentLocInfo(TypeSourceInfo *Declarator) { Pointer = Declarator; } TemplateArgumentLocInfo(TypeSourceInfo *Declarator) { Pointer = Declarator; }
TemplateArgumentLocInfo(Expr *E) { Pointer = E; } TemplateArgumentLocInfo(Expr *E) { Pointer = E; }
// Ctx is used for allocation -- this case is unusually large and also rare, // Ctx is used for allocation -- this case is unusually large and also rare,
// so we store the payload out-of-line. // so we store the payload out-of-line.
TemplateArgumentLocInfo(ASTContext &Ctx, NestedNameSpecifierLoc QualifierLoc, TemplateArgumentLocInfo(ASTContext &Ctx, SourceLocation TemplateKwLoc,
NestedNameSpecifierLoc QualifierLoc,
SourceLocation TemplateNameLoc, SourceLocation TemplateNameLoc,
SourceLocation EllipsisLoc); SourceLocation EllipsisLoc);
@ -512,10 +506,8 @@ public:
Expr *getAsExpr() const { return cast<Expr *>(Pointer); } Expr *getAsExpr() const { return cast<Expr *>(Pointer); }
NestedNameSpecifierLoc getTemplateQualifierLoc() const { SourceLocation getTemplateKwLoc() const {
const auto *Template = getTemplate(); return getTemplate()->TemplateKwLoc;
return NestedNameSpecifierLoc(Template->Qualifier,
Template->QualifierLocData);
} }
SourceLocation getTemplateNameLoc() const { SourceLocation getTemplateNameLoc() const {
@ -525,6 +517,10 @@ public:
SourceLocation getTemplateEllipsisLoc() const { SourceLocation getTemplateEllipsisLoc() const {
return getTemplate()->EllipsisLoc; return getTemplate()->EllipsisLoc;
} }
private:
llvm::PointerUnion<TemplateTemplateArgLocInfo *, Expr *, TypeSourceInfo *>
Pointer;
}; };
/// Location wrapper for a TemplateArgument. TemplateArgument is to /// Location wrapper for a TemplateArgument. TemplateArgument is to
@ -558,14 +554,10 @@ public:
} }
TemplateArgumentLoc(ASTContext &Ctx, const TemplateArgument &Argument, TemplateArgumentLoc(ASTContext &Ctx, const TemplateArgument &Argument,
SourceLocation TemplateKWLoc,
NestedNameSpecifierLoc QualifierLoc, NestedNameSpecifierLoc QualifierLoc,
SourceLocation TemplateNameLoc, SourceLocation TemplateNameLoc,
SourceLocation EllipsisLoc = SourceLocation()) SourceLocation EllipsisLoc = SourceLocation());
: Argument(Argument),
LocInfo(Ctx, QualifierLoc, TemplateNameLoc, EllipsisLoc) {
assert(Argument.getKind() == TemplateArgument::Template ||
Argument.getKind() == TemplateArgument::TemplateExpansion);
}
/// - Fetches the primary location of the argument. /// - Fetches the primary location of the argument.
SourceLocation getLocation() const { SourceLocation getLocation() const {
@ -614,13 +606,15 @@ public:
return LocInfo.getAsExpr(); return LocInfo.getAsExpr();
} }
NestedNameSpecifierLoc getTemplateQualifierLoc() const { SourceLocation getTemplateKWLoc() const {
if (Argument.getKind() != TemplateArgument::Template && if (Argument.getKind() != TemplateArgument::Template &&
Argument.getKind() != TemplateArgument::TemplateExpansion) Argument.getKind() != TemplateArgument::TemplateExpansion)
return NestedNameSpecifierLoc(); return SourceLocation();
return LocInfo.getTemplateQualifierLoc(); return LocInfo.getTemplateKwLoc();
} }
NestedNameSpecifierLoc getTemplateQualifierLoc() const;
SourceLocation getTemplateNameLoc() const { SourceLocation getTemplateNameLoc() const {
if (Argument.getKind() != TemplateArgument::Template && if (Argument.getKind() != TemplateArgument::Template &&
Argument.getKind() != TemplateArgument::TemplateExpansion) Argument.getKind() != TemplateArgument::TemplateExpansion)

View File

@ -14,7 +14,7 @@
#define LLVM_CLANG_AST_TEMPLATENAME_H #define LLVM_CLANG_AST_TEMPLATENAME_H
#include "clang/AST/DependenceFlags.h" #include "clang/AST/DependenceFlags.h"
#include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/NestedNameSpecifierBase.h"
#include "clang/Basic/LLVM.h" #include "clang/Basic/LLVM.h"
#include "clang/Basic/OperatorKinds.h" #include "clang/Basic/OperatorKinds.h"
#include "clang/Basic/UnsignedOrNone.h" #include "clang/Basic/UnsignedOrNone.h"
@ -335,10 +335,18 @@ public:
/// structure, if any. /// structure, if any.
QualifiedTemplateName *getAsQualifiedTemplateName() const; QualifiedTemplateName *getAsQualifiedTemplateName() const;
/// Retrieve the underlying qualified template name,
/// looking through underlying nodes.
QualifiedTemplateName *getAsAdjustedQualifiedTemplateName() const;
/// Retrieve the underlying dependent template name /// Retrieve the underlying dependent template name
/// structure, if any. /// structure, if any.
DependentTemplateName *getAsDependentTemplateName() const; DependentTemplateName *getAsDependentTemplateName() const;
// Retrieve the qualifier stored in either a underlying DependentTemplateName
// or QualifiedTemplateName.
NestedNameSpecifier getQualifier() const;
/// Retrieve the using shadow declaration through which the underlying /// Retrieve the using shadow declaration through which the underlying
/// template declaration is introduced, if any. /// template declaration is introduced, if any.
UsingShadowDecl *getAsUsingShadowDecl() const; UsingShadowDecl *getAsUsingShadowDecl() const;
@ -503,7 +511,7 @@ class QualifiedTemplateName : public llvm::FoldingSetNode {
/// "template" keyword is always redundant in this case (otherwise, /// "template" keyword is always redundant in this case (otherwise,
/// the template name would be a dependent name and we would express /// the template name would be a dependent name and we would express
/// this name with DependentTemplateName). /// this name with DependentTemplateName).
llvm::PointerIntPair<NestedNameSpecifier *, 1> Qualifier; llvm::PointerIntPair<NestedNameSpecifier, 1, bool> Qualifier;
/// The underlying template name, it is either /// The underlying template name, it is either
/// 1) a Template -- a template declaration that this qualified name refers /// 1) a Template -- a template declaration that this qualified name refers
@ -512,7 +520,7 @@ class QualifiedTemplateName : public llvm::FoldingSetNode {
/// using-shadow declaration. /// using-shadow declaration.
TemplateName UnderlyingTemplate; TemplateName UnderlyingTemplate;
QualifiedTemplateName(NestedNameSpecifier *NNS, bool TemplateKeyword, QualifiedTemplateName(NestedNameSpecifier NNS, bool TemplateKeyword,
TemplateName Template) TemplateName Template)
: Qualifier(NNS, TemplateKeyword ? 1 : 0), UnderlyingTemplate(Template) { : Qualifier(NNS, TemplateKeyword ? 1 : 0), UnderlyingTemplate(Template) {
assert(UnderlyingTemplate.getKind() == TemplateName::Template || assert(UnderlyingTemplate.getKind() == TemplateName::Template ||
@ -521,7 +529,7 @@ class QualifiedTemplateName : public llvm::FoldingSetNode {
public: public:
/// Return the nested name specifier that qualifies this name. /// Return the nested name specifier that qualifies this name.
NestedNameSpecifier *getQualifier() const { return Qualifier.getPointer(); } NestedNameSpecifier getQualifier() const { return Qualifier.getPointer(); }
/// Whether the template name was prefixed by the "template" /// Whether the template name was prefixed by the "template"
/// keyword. /// keyword.
@ -534,9 +542,9 @@ public:
Profile(ID, getQualifier(), hasTemplateKeyword(), UnderlyingTemplate); Profile(ID, getQualifier(), hasTemplateKeyword(), UnderlyingTemplate);
} }
static void Profile(llvm::FoldingSetNodeID &ID, NestedNameSpecifier *NNS, static void Profile(llvm::FoldingSetNodeID &ID, NestedNameSpecifier NNS,
bool TemplateKeyword, TemplateName TN) { bool TemplateKeyword, TemplateName TN) {
ID.AddPointer(NNS); NNS.Profile(ID);
ID.AddBoolean(TemplateKeyword); ID.AddBoolean(TemplateKeyword);
ID.AddPointer(TN.getAsVoidPointer()); ID.AddPointer(TN.getAsVoidPointer());
} }
@ -585,18 +593,18 @@ class DependentTemplateStorage {
/// ///
/// The bit stored in this qualifier describes whether the \c Name field /// The bit stored in this qualifier describes whether the \c Name field
/// was preceeded by a template keyword. /// was preceeded by a template keyword.
llvm::PointerIntPair<NestedNameSpecifier *, 1, bool> Qualifier; llvm::PointerIntPair<NestedNameSpecifier, 1, bool> Qualifier;
/// The dependent template name. /// The dependent template name.
IdentifierOrOverloadedOperator Name; IdentifierOrOverloadedOperator Name;
public: public:
DependentTemplateStorage(NestedNameSpecifier *Qualifier, DependentTemplateStorage(NestedNameSpecifier Qualifier,
IdentifierOrOverloadedOperator Name, IdentifierOrOverloadedOperator Name,
bool HasTemplateKeyword); bool HasTemplateKeyword);
/// Return the nested name specifier that qualifies this name. /// Return the nested name specifier that qualifies this name.
NestedNameSpecifier *getQualifier() const { return Qualifier.getPointer(); } NestedNameSpecifier getQualifier() const { return Qualifier.getPointer(); }
IdentifierOrOverloadedOperator getName() const { return Name; } IdentifierOrOverloadedOperator getName() const { return Name; }
@ -609,10 +617,10 @@ public:
Profile(ID, getQualifier(), getName(), hasTemplateKeyword()); Profile(ID, getQualifier(), getName(), hasTemplateKeyword());
} }
static void Profile(llvm::FoldingSetNodeID &ID, NestedNameSpecifier *NNS, static void Profile(llvm::FoldingSetNodeID &ID, NestedNameSpecifier NNS,
IdentifierOrOverloadedOperator Name, IdentifierOrOverloadedOperator Name,
bool HasTemplateKeyword) { bool HasTemplateKeyword) {
ID.AddPointer(NNS); NNS.Profile(ID);
ID.AddBoolean(HasTemplateKeyword); ID.AddBoolean(HasTemplateKeyword);
Name.Profile(ID); Name.Profile(ID);
} }

View File

@ -211,7 +211,7 @@ public:
void dumpAccessSpecifier(AccessSpecifier AS); void dumpAccessSpecifier(AccessSpecifier AS);
void dumpCleanupObject(const ExprWithCleanups::CleanupObject &C); void dumpCleanupObject(const ExprWithCleanups::CleanupObject &C);
void dumpTemplateSpecializationKind(TemplateSpecializationKind TSK); void dumpTemplateSpecializationKind(TemplateSpecializationKind TSK);
void dumpNestedNameSpecifier(const NestedNameSpecifier *NNS); void dumpNestedNameSpecifier(NestedNameSpecifier NNS);
void dumpConceptReference(const ConceptReference *R); void dumpConceptReference(const ConceptReference *R);
void dumpTemplateArgument(const TemplateArgument &TA); void dumpTemplateArgument(const TemplateArgument &TA);
void dumpBareTemplateName(TemplateName TN); void dumpBareTemplateName(TemplateName TN);

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More