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 @@ -218,31 +218,45 @@ } // TypeLoc visitors. + void reportType(SourceLocation RefLoc, NamedDecl *ND) { + // Reporting explicit references to types nested inside classes can cause + // issues, e.g. a type accessed through a derived class shouldn't require + // inclusion of the base. + // Hence we report all such references as implicit. The code must spell the + // outer type-location somewhere, which will trigger an explicit reference + // and per IWYS, it's that spelling's responsibility to bring in necessary + // declarations. + RefType RT = llvm::isa(ND->getDeclContext()) + ? RefType::Implicit + : RefType::Explicit; + return report(RefLoc, ND, RT); + } + bool VisitUsingTypeLoc(UsingTypeLoc TL) { - report(TL.getNameLoc(), TL.getFoundDecl()); + reportType(TL.getNameLoc(), TL.getFoundDecl()); return true; } bool VisitTagTypeLoc(TagTypeLoc TTL) { - report(TTL.getNameLoc(), TTL.getDecl()); + reportType(TTL.getNameLoc(), TTL.getDecl()); return true; } bool VisitTypedefTypeLoc(TypedefTypeLoc TTL) { - report(TTL.getNameLoc(), TTL.getTypedefNameDecl()); + reportType(TTL.getNameLoc(), TTL.getTypedefNameDecl()); return true; } bool VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc TL) { - report(TL.getTemplateNameLoc(), - getMostRelevantTemplatePattern(TL.getTypePtr())); + reportType(TL.getTemplateNameLoc(), + getMostRelevantTemplatePattern(TL.getTypePtr())); return true; } bool VisitDeducedTemplateSpecializationTypeLoc( DeducedTemplateSpecializationTypeLoc TL) { - report(TL.getTemplateNameLoc(), - getMostRelevantTemplatePattern(TL.getTypePtr())); + reportType(TL.getTemplateNameLoc(), + getMostRelevantTemplatePattern(TL.getTypePtr())); return true; } 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 @@ -341,6 +341,34 @@ testWalk("template struct $explicit^S { S(T); };", "^S s(42);"); } +TEST(WalkAST, NestedTypes) { + testWalk(R"cpp( + struct Base { typedef int $implicit^a; }; + struct Derived : public Base {};)cpp", + "void fun() { Derived::^a x; }"); + testWalk(R"cpp( + struct Base { using $implicit^a = int; }; + struct Derived : public Base {};)cpp", + "void fun() { Derived::^a x; }"); + testWalk(R"cpp( + struct ns { struct a {}; }; + struct Base : public ns { using ns::$implicit^a; }; + struct Derived : public Base {};)cpp", + "void fun() { Derived::^a x; }"); + testWalk(R"cpp( + struct Base { struct $implicit^a {}; }; + struct Derived : public Base {};)cpp", + "void fun() { Derived::^a x; }"); + testWalk("struct Base { struct $implicit^a {}; };", + "struct Derived : public Base { ^a x; };"); + testWalk(R"cpp( + struct Base { struct $implicit^a {}; }; + struct Derived : public Base {}; + struct SoDerived : public Derived {}; + )cpp", + "void fun() { SoDerived::Derived::^a x; }"); +} + TEST(WalkAST, MemberExprs) { testWalk("struct $implicit^S { static int f; };", "void foo() { S::^f; }"); testWalk("struct B { static int f; }; struct $implicit^S : B {};",