Skip to content

[clang] Improve nested name specifier AST representation #147835

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
53 changes: 17 additions & 36 deletions clang-tools-extra/clang-change-namespace/ChangeNamespace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,9 @@ llvm::SmallVector<llvm::StringRef, 4> splitSymbolName(llvm::StringRef Name) {
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) {
// Dig past any namespace or keyword qualifications.
while (TLoc.getTypeLocClass() == TypeLoc::Elaborated ||
TLoc.getTypeLocClass() == TypeLoc::Qualified)
TLoc = TLoc.getNextTypeLoc();
if (auto QTL = TLoc.getAs<QualifiedTypeLoc>())
TLoc = QTL.getUnqualifiedLoc();

// The location for template specializations (e.g. Foo<int>) includes the
// templated types in its location range. We want to restrict this to just
Expand Down Expand Up @@ -550,8 +535,8 @@ void ChangeNamespaceTool::run(
Result.Nodes.getNodeAs<NestedNameSpecifierLoc>(
"nested_specifier_loc")) {
SourceLocation Start = Specifier->getBeginLoc();
SourceLocation End = endLocationForType(Specifier->getTypeLoc());
fixTypeLoc(Result, Start, End, Specifier->getTypeLoc());
SourceLocation End = endLocationForType(Specifier->castAsTypeLoc());
fixTypeLoc(Result, Start, End, Specifier->castAsTypeLoc());
} else if (const auto *BaseInitializer =
Result.Nodes.getNodeAs<CXXCtorInitializer>(
"base_initializer")) {
Expand All @@ -562,19 +547,16 @@ void ChangeNamespaceTool::run(
// filtered by matchers in some cases, e.g. the type is templated. We should
// handle the record type qualifier instead.
TypeLoc Loc = *TLoc;
while (Loc.getTypeLocClass() == TypeLoc::Qualified)
Loc = Loc.getNextTypeLoc();
if (Loc.getTypeLocClass() == TypeLoc::Elaborated) {
NestedNameSpecifierLoc NestedNameSpecifier =
Loc.castAs<ElaboratedTypeLoc>().getQualifierLoc();
// FIXME: avoid changing injected class names.
if (auto *NNS = NestedNameSpecifier.getNestedNameSpecifier()) {
const Type *SpecifierType = NNS->getAsType();
if (SpecifierType && SpecifierType->isRecordType())
return;
}
}
fixTypeLoc(Result, startLocationForType(Loc), endLocationForType(Loc), Loc);
if (auto QTL = Loc.getAs<QualifiedTypeLoc>())
Loc = QTL.getUnqualifiedLoc();
// FIXME: avoid changing injected class names.
if (NestedNameSpecifier NestedNameSpecifier =
Loc.getPrefix().getNestedNameSpecifier();
NestedNameSpecifier.getKind() == NestedNameSpecifier::Kind::Type &&
NestedNameSpecifier.getAsType()->isRecordType())
return;
fixTypeLoc(Result, Loc.getNonElaboratedBeginLoc(), endLocationForType(Loc),
Loc);
} else if (const auto *VarRef =
Result.Nodes.getNodeAs<DeclRefExpr>("var_ref")) {
const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var_decl");
Expand All @@ -588,10 +570,9 @@ void ChangeNamespaceTool::run(
} else if (const auto *EnumConstRef =
Result.Nodes.getNodeAs<DeclRefExpr>("enum_const_ref")) {
// Do not rename the reference if it is already scoped by the EnumDecl name.
if (EnumConstRef->hasQualifier() &&
EnumConstRef->getQualifier()->getKind() ==
NestedNameSpecifier::SpecifierKind::TypeSpec &&
EnumConstRef->getQualifier()->getAsType()->isEnumeralType())
if (NestedNameSpecifier Qualifier = EnumConstRef->getQualifier();
Qualifier.getKind() == NestedNameSpecifier::Kind::Type &&
Qualifier.getAsType()->isEnumeralType())
return;
const auto *EnumConstDecl =
Result.Nodes.getNodeAs<EnumConstantDecl>("enum_const_decl");
Expand Down
4 changes: 2 additions & 2 deletions clang-tools-extra/clang-doc/Serialize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -883,8 +883,8 @@ parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,
return;
for (const CXXBaseSpecifier &B : D->bases()) {
if (const RecordType *Ty = B.getType()->getAs<RecordType>()) {
if (const CXXRecordDecl *Base =
cast_or_null<CXXRecordDecl>(Ty->getDecl()->getDefinition())) {
if (const CXXRecordDecl *Base = cast_or_null<CXXRecordDecl>(
Ty->getOriginalDecl()->getDefinition())) {
// Initialized without USR and name, this will be set in the following
// if-else stmt.
BaseRecordInfo BI(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,7 @@ void FindAllSymbols::registerMatchers(MatchFinder *MatchFinder) {
// Uses of most types: just look at what the typeLoc refers to.
MatchFinder->addMatcher(
typeLoc(isExpansionInMainFile(),
loc(qualType(allOf(unless(elaboratedType()),
hasDeclaration(Types.bind("use")))))),
loc(qualType(hasDeclaration(Types.bind("use"))))),
this);
// Uses of typedefs: these are often transparent to hasDeclaration, so we need
// to handle them explicitly.
Expand Down
3 changes: 2 additions & 1 deletion clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,8 @@ void ClangTidyDiagnosticConsumer::forwardDiagnostic(const Diagnostic &Info) {
Builder << reinterpret_cast<const NamedDecl *>(Info.getRawArg(Index));
break;
case clang::DiagnosticsEngine::ak_nestednamespec:
Builder << reinterpret_cast<NestedNameSpecifier *>(Info.getRawArg(Index));
Builder << NestedNameSpecifier::getFromVoidPointer(
reinterpret_cast<void *>(Info.getRawArg(Index)));
break;
case clang::DiagnosticsEngine::ak_declcontext:
Builder << reinterpret_cast<DeclContext *>(Info.getRawArg(Index));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ static bool isDerivedClassBefriended(const CXXRecordDecl *CRTP,
return false;
}

return FriendType->getType()->getAsCXXRecordDecl() == Derived;
return declaresSameEntity(FriendType->getType()->getAsCXXRecordDecl(),
Derived);
});
}

Expand All @@ -55,7 +56,8 @@ getDerivedParameter(const ClassTemplateSpecializationDecl *CRTP,
CRTP->getTemplateArgs().asArray(), [&](const TemplateArgument &Arg) {
++Idx;
return Arg.getKind() == TemplateArgument::Type &&
Arg.getAsType()->getAsCXXRecordDecl() == Derived;
declaresSameEntity(Arg.getAsType()->getAsCXXRecordDecl(),
Derived);
});

return AnyOf ? CRTP->getSpecializedTemplate()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@ approximateImplicitConversion(const TheCheck &Check, QualType LType,
ImplicitConversionModellingMode ImplicitMode);

static inline bool isUselessSugar(const Type *T) {
return isa<AttributedType, DecayedType, ElaboratedType, ParenType>(T);
return isa<AttributedType, DecayedType, ParenType>(T);
}

namespace {
Expand Down Expand Up @@ -1040,7 +1040,9 @@ approximateStandardConversionSequence(const TheCheck &Check, QualType From,
const auto *ToRecord = To->getAsCXXRecordDecl();
if (isDerivedToBase(FromRecord, ToRecord)) {
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) {
Expand Down Expand Up @@ -1072,9 +1074,9 @@ approximateStandardConversionSequence(const TheCheck &Check, QualType From,
WorkType = To;
}

if (WorkType == To) {
if (Ctx.hasSameType(WorkType, To)) {
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");
Expand Down Expand Up @@ -1219,7 +1221,7 @@ tryConversionOperators(const TheCheck &Check, const CXXRecordDecl *RD,

if (std::optional<UserDefinedConversionSelector::PreparedConversion>
SelectedConversion = ConversionSet()) {
QualType RecordType{RD->getTypeForDecl(), 0};
CanQualType RecordType = RD->getASTContext().getCanonicalTagType(RD);

ConversionSequence Result{RecordType, ToType};
// The conversion from the operator call's return type to ToType was
Expand Down Expand Up @@ -1270,7 +1272,7 @@ tryConvertingConstructors(const TheCheck &Check, QualType FromType,

if (std::optional<UserDefinedConversionSelector::PreparedConversion>
SelectedConversion = ConversionSet()) {
QualType RecordType{RD->getTypeForDecl(), 0};
CanQualType RecordType = RD->getASTContext().getCanonicalTagType(RD);

ConversionSequence Result{FromType, RecordType};
Result.AfterFirstStandard = SelectedConversion->Seq.AfterFirstStandard;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,9 @@ void ForwardDeclarationNamespaceCheck::check(
// struct B { friend A; };
// \endcode
// `A` will not be marked as "referenced" in the AST.
if (const TypeSourceInfo *Tsi = Decl->getFriendType()) {
QualType Desugared = Tsi->getType().getDesugaredType(*Result.Context);
FriendTypes.insert(Desugared.getTypePtr());
}
if (const TypeSourceInfo *Tsi = Decl->getFriendType())
FriendTypes.insert(
Tsi->getType()->getCanonicalTypeUnqualified().getTypePtr());
}
}

Expand Down Expand Up @@ -119,7 +118,9 @@ void ForwardDeclarationNamespaceCheck::onEndOfTranslationUnit() {
if (CurDecl->hasDefinition() || CurDecl->isReferenced()) {
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.
}
if (CurDecl->getLocation().isMacroID() ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,17 @@ AST_MATCHER(QualType, isEnableIf) {
BaseType = BaseType->getPointeeType().getTypePtr();
}
// Case: type parameter dependent (enable_if<is_integral<T>>).
if (const auto *Dependent = BaseType->getAs<DependentNameType>()) {
BaseType = Dependent->getQualifier()->getAsType();
}
if (const auto *Dependent = BaseType->getAs<DependentNameType>())
BaseType = Dependent->getQualifier().getAsType();
if (!BaseType)
return false;
if (CheckTemplate(BaseType->getAs<TemplateSpecializationType>()))
return true; // Case: enable_if_t< >.
if (const auto *Elaborated = BaseType->getAs<ElaboratedType>()) {
if (const auto *Q = Elaborated->getQualifier())
if (const auto *Qualifier = Q->getAsType()) {
if (CheckTemplate(Qualifier->getAs<TemplateSpecializationType>())) {
return true; // Case: enable_if< >::type.
}
}
}
if (const auto *TT = BaseType->getAs<TypedefType>())
if (NestedNameSpecifier Q = TT->getQualifier();
Q.getKind() == NestedNameSpecifier::Kind::Type)
if (CheckTemplate(Q.getAsType()->getAs<TemplateSpecializationType>()))
return true; // Case: enable_if< >::type.
return false;
}
AST_MATCHER_P(TemplateTypeParmDecl, hasDefaultArgument,
Expand Down
19 changes: 8 additions & 11 deletions clang-tools-extra/clang-tidy/bugprone/IncorrectEnableIfCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,27 +32,22 @@ AST_MATCHER_P(TemplateTypeParmDecl, hasUnnamedDefaultArgument,
void IncorrectEnableIfCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
templateTypeParmDecl(
hasUnnamedDefaultArgument(
elaboratedTypeLoc(
hasNamedTypeLoc(templateSpecializationTypeLoc(
loc(qualType(hasDeclaration(namedDecl(
hasName("::std::enable_if"))))))
.bind("enable_if_specialization")))
.bind("elaborated")))
hasUnnamedDefaultArgument(templateSpecializationTypeLoc(
loc(qualType(hasDeclaration(namedDecl(
hasName("::std::enable_if"))))))
.bind("enable_if_specialization")))
.bind("enable_if"),
this);
}

void IncorrectEnableIfCheck::check(const MatchFinder::MatchResult &Result) {
const auto *EnableIf =
Result.Nodes.getNodeAs<TemplateTypeParmDecl>("enable_if");
const auto *ElaboratedLoc =
Result.Nodes.getNodeAs<ElaboratedTypeLoc>("elaborated");
const auto *EnableIfSpecializationLoc =
Result.Nodes.getNodeAs<TemplateSpecializationTypeLoc>(
"enable_if_specialization");

if (!EnableIf || !ElaboratedLoc || !EnableIfSpecializationLoc)
if (!EnableIf || !EnableIfSpecializationLoc)
return;

const SourceManager &SM = *Result.SourceManager;
Expand All @@ -62,8 +57,10 @@ void IncorrectEnableIfCheck::check(const MatchFinder::MatchResult &Result) {
auto Diag = diag(EnableIf->getBeginLoc(),
"incorrect std::enable_if usage detected; use "
"'typename std::enable_if<...>::type'");
// FIXME: This should handle the enable_if specialization already having an
// elaborated keyword.
if (!getLangOpts().CPlusPlus20) {
Diag << FixItHint::CreateInsertion(ElaboratedLoc->getBeginLoc(),
Diag << FixItHint::CreateInsertion(EnableIfSpecializationLoc->getBeginLoc(),
"typename ");
}
Diag << FixItHint::CreateInsertion(RAngleLoc.getLocWithOffset(1), "::type");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,31 @@ static void replaceMoveWithForward(const UnresolvedLookupExpr *Callee,
// std::move(). This will hopefully prevent erroneous replacements if the
// code does unusual things (e.g. create an alias for std::move() in
// another namespace).
NestedNameSpecifier *NNS = Callee->getQualifier();
if (!NNS) {
NestedNameSpecifier NNS = Callee->getQualifier();
switch (NNS.getKind()) {
case NestedNameSpecifier::Kind::Null:
// 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 don't know whether the code also had a "using std::forward;".
Diag << FixItHint::CreateReplacement(CallRange, "std::" + ForwardName);
} else if (const NamespaceDecl *Namespace = NNS->getAsNamespace()) {
break;
case NestedNameSpecifier::Kind::Namespace: {
auto [Namespace, Prefix] = NNS.getAsNamespaceAndPrefix();
if (Namespace->getName() == "std") {
if (!NNS->getPrefix()) {
if (!Prefix) {
// Called as "std::move".
Diag << FixItHint::CreateReplacement(CallRange,
"std::" + ForwardName);
} else if (NNS->getPrefix()->getKind() == NestedNameSpecifier::Global) {
} else if (Prefix.getKind() == NestedNameSpecifier::Kind::Global) {
// Called as "::std::move".
Diag << FixItHint::CreateReplacement(CallRange,
"::std::" + ForwardName);
}
}
break;
}
default:
return;
}
}
}
Expand Down
10 changes: 6 additions & 4 deletions clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) {
"suspicious usage of 'sizeof(array)/sizeof(...)';"
" denominator differs from the size of array elements")
<< E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange();
} else if (NumTy && DenomTy && NumTy == DenomTy &&
} else if (NumTy && DenomTy && Ctx.hasSameType(NumTy, DenomTy) &&
!NumTy->isDependentType()) {
// Dependent type should not be compared.
diag(E->getOperatorLoc(),
Expand All @@ -434,7 +434,7 @@ void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) {
<< E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange();
} else if (!WarnOnSizeOfPointer) {
// When 'WarnOnSizeOfPointer' is enabled, these messages become redundant:
if (PointedTy && DenomTy && PointedTy == DenomTy) {
if (PointedTy && DenomTy && Ctx.hasSameType(PointedTy, DenomTy)) {
diag(E->getOperatorLoc(),
"suspicious usage of 'sizeof(...)/sizeof(...)'; size of pointer "
"is divided by size of pointed type")
Expand Down Expand Up @@ -463,7 +463,8 @@ void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) {
const auto *SizeOfExpr =
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 "
"pointer arithmetic")
<< SizeOfExpr->getSourceRange() << E->getOperatorLoc()
Expand All @@ -477,7 +478,8 @@ void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) {
const auto *SizeOfExpr =
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 "
"pointer arithmetic")
<< SizeOfExpr->getSourceRange() << E->getOperatorLoc()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ void NoSuspendWithLockCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
}

void NoSuspendWithLockCheck::registerMatchers(MatchFinder *Finder) {
auto LockType = elaboratedType(namesType(templateSpecializationType(
auto LockType = templateSpecializationType(
hasDeclaration(namedDecl(matchers::matchesAnyListedName(
utils::options::parseStringList(LockGuards)))))));
utils::options::parseStringList(LockGuards)))));

StatementMatcher Lock =
declStmt(has(varDecl(hasType(LockType)).bind("lock-decl")))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ struct InitializerInsertion {
// Convenience utility to get a RecordDecl from a QualType.
const RecordDecl *getCanonicalRecordDecl(const QualType &Type) {
if (const auto *RT = Type.getCanonicalType()->getAs<RecordType>())
return RT->getDecl();
return RT->getOriginalDecl();
return nullptr;
}

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