diff --git a/clang-tools-extra/include-cleaner/lib/WalkAST.cpp b/clang-tools-extra/include-cleaner/lib/WalkAST.cpp --- a/clang-tools-extra/include-cleaner/lib/WalkAST.cpp +++ b/clang-tools-extra/include-cleaner/lib/WalkAST.cpp @@ -27,16 +27,6 @@ class ASTWalker : public RecursiveASTVisitor { DeclCallback Callback; - bool handleTemplateName(SourceLocation Loc, TemplateName TN) { - // For using-templates, only mark the alias. - if (auto *USD = TN.getAsUsingShadowDecl()) { - report(Loc, USD); - return true; - } - report(Loc, TN.getAsTemplateDecl()); - return true; - } - void report(SourceLocation Loc, NamedDecl *ND, RefType RT = RefType::Explicit) { if (!ND || Loc.isInvalid()) @@ -44,10 +34,33 @@ Callback(Loc, *cast(ND->getCanonicalDecl()), RT); } - NamedDecl *resolveType(QualType Type) { - if (Type->isPointerType()) - Type = Type->getPointeeType(); - return Type->getAsRecordDecl(); + NamedDecl *resolveTemplateName(TemplateName TN) { + // For using-templates, only mark the alias. + if (auto *USD = TN.getAsUsingShadowDecl()) + return USD; + return TN.getAsTemplateDecl(); + } + NamedDecl *getMemberProvider(QualType Base) { + if (Base->isPointerType()) + Base = Base->getPointeeType(); + // Unwrap the sugar ElaboratedType. + if (const auto *ElTy = dyn_cast(Base)) + Base = ElTy->getNamedType(); + + if (const auto *TT = dyn_cast(Base)) + return TT->getDecl(); + if (const auto *UT = dyn_cast(Base)) + return UT->getFoundDecl(); + // 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 + // the template provides the member, and the critical case `unique_ptr` + // is supported (the base type is a Foo*). + // + // There are some exceptions that this heuristic could fail (dependent base, + // dependent typealias), but we believe these are rare. + if (const auto *TST = dyn_cast(Base)) + return resolveTemplateName(TST->getTemplateName()); + return Base->getAsRecordDecl(); } public: @@ -59,12 +72,16 @@ } bool VisitMemberExpr(MemberExpr *E) { - // A member expr implies a usage of the class type - // (e.g., to prevent inserting a header of base class when using base - // members from a derived object). + // Reporting a usage of the member decl would cause issues (e.g. force + // including the base class for inherited members). Instead, we report a + // usage of the base type of the MemberExpr, so that e.g. code + // `returnFoo().bar` can keep #include "foo.h" (rather than inserting + // "bar.h" for the underlying base type `Bar`). + // // FIXME: support dependent types, e.g., "std::vector().size()". QualType Type = E->getBase()->IgnoreImpCasts()->getType(); - report(E->getMemberLoc(), resolveType(Type)); + // FIXME: this should report as implicit reference. + report(E->getMemberLoc(), getMemberProvider(Type)); return true; } @@ -126,15 +143,17 @@ bool VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc TL) { // FIXME: Handle explicit specializations. - return handleTemplateName(TL.getTemplateNameLoc(), - TL.getTypePtr()->getTemplateName()); + report(TL.getTemplateNameLoc(), + resolveTemplateName(TL.getTypePtr()->getTemplateName())); + return true; } bool VisitDeducedTemplateSpecializationTypeLoc( DeducedTemplateSpecializationTypeLoc TL) { // FIXME: Handle specializations. - return handleTemplateName(TL.getTemplateNameLoc(), - TL.getTypePtr()->getTemplateName()); + report(TL.getTemplateNameLoc(), + resolveTemplateName(TL.getTypePtr()->getTemplateName())); + return true; } bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &TL) { @@ -142,9 +161,11 @@ // Template-template parameters require special attention, as there's no // TemplateNameLoc. if (Arg.getKind() == TemplateArgument::Template || - Arg.getKind() == TemplateArgument::TemplateExpansion) - return handleTemplateName(TL.getLocation(), - Arg.getAsTemplateOrTemplatePattern()); + Arg.getKind() == TemplateArgument::TemplateExpansion) { + report(TL.getLocation(), + resolveTemplateName(Arg.getAsTemplateOrTemplatePattern())); + return true; + } return RecursiveASTVisitor::TraverseTemplateArgumentLoc(TL); } }; diff --git a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp --- a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp +++ b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp @@ -210,6 +210,25 @@ }; struct Foo {};)cpp", "void test(unique_ptr &V) { V.^release(); }"); + // Respect the sugar type (typedef, using-type). + testWalk(R"cpp( + namespace ns { struct Foo { int a; }; } + using $explicit^Bar = ns::Foo;)cpp", + "void test(Bar b) { b.^a; }"); + testWalk(R"cpp( + namespace ns { struct Foo { int a; }; } + using ns::$explicit^Foo;)cpp", + "void test(Foo b) { b.^a; }"); + testWalk(R"cpp( + namespace ns { struct Foo { int a; }; } + namespace ns2 { using Bar = ns::Foo; } + using ns2::$explicit^Bar; + )cpp", + "void test(Bar b) { b.^a; }"); + testWalk(R"cpp( + namespace ns { template struct Foo { int a; }; } + using ns::$explicit^Foo;)cpp", + "void k(Foo b) { b.^a; }"); } TEST(WalkAST, ConstructExprs) { diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -2594,6 +2594,7 @@ /// This will check for a TypedefType by removing any existing sugar /// until it reaches a TypedefType or a non-sugared type. template <> const TypedefType *Type::getAs() const; +template <> const UsingType *Type::getAs() const; /// This will check for a TemplateSpecializationType by removing any /// existing sugar until it reaches a TemplateSpecializationType or a diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -524,6 +524,10 @@ return getAsSugar(this); } +template <> const UsingType *Type::getAs() const { + return getAsSugar(this); +} + template <> const TemplateSpecializationType *Type::getAs() const { return getAsSugar(this); }