kadircet created this revision. kadircet added reviewers: hokein, sammccall. Herald added a subscriber: mgrang. Herald added a project: All. kadircet requested review of this revision. Herald added a project: clang-tools-extra. Herald added a subscriber: cfe-commits.
Creates a one to many mapping, by returning all the possible locations providing a decl. Also includes an "is definition" signal for the location, that can be used for ranking afterwards. This also takes care of stdlib symbols by having a variant of locations. Depends on D135859 <https://reviews.llvm.org/D135859>. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D135953 Files: clang-tools-extra/include-cleaner/lib/AnalysisInternal.h clang-tools-extra/include-cleaner/lib/CMakeLists.txt clang-tools-extra/include-cleaner/lib/WalkAST.cpp clang-tools-extra/include-cleaner/unittests/CMakeLists.txt clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
Index: clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp =================================================================== --- clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp +++ clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp @@ -1,10 +1,15 @@ #include "AnalysisInternal.h" #include "clang/AST/ASTContext.h" -#include "clang/AST/Expr.h" +#include "clang/AST/Decl.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" #include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceLocation.h" #include "clang/Frontend/TextDiagnostic.h" #include "clang/Testing/TestAST.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/Casting.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Testing/Support/Annotations.h" @@ -12,6 +17,7 @@ #include <map> #include <unordered_map> #include <utility> +#include <variant> #include <vector> namespace clang { @@ -194,6 +200,58 @@ testWalk("enum class E : int {};", "enum class ^E : int ;"); } +// Looks for a decl named `foo` and performs locateDecl on it. Expects all the +// locations marked in `Code` with the right annotation to be generated. +void testLocate(llvm::StringRef Code) { + llvm::Annotations Target(Code); + + TestInputs Inputs(Target.code()); + Inputs.ExtraArgs.push_back("-std=c++17"); + TestAST AST(Inputs); + const auto &SM = AST.sourceManager(); + + const NamedDecl *Foo; + struct MatchCB : public ast_matchers::MatchFinder::MatchCallback { + MatchCB(const NamedDecl *&Out) : Out(Out) {} + void run(const ast_matchers::MatchFinder::MatchResult &Result) override { + Out = Result.Nodes.getNodeAs<NamedDecl>("id"); + assert(Out); + Out = llvm::cast<NamedDecl>(Out->getCanonicalDecl()); + } + const NamedDecl *&Out; + } CB(Foo); + ast_matchers::MatchFinder Finder; + Finder.addMatcher( + ast_matchers::namedDecl(ast_matchers::unless(ast_matchers::isImplicit()), + ast_matchers::hasName("foo")) + .bind("id"), + &CB); + Finder.matchAST(AST.context()); + ASSERT_TRUE(Foo); + std::vector<std::pair<size_t, llvm::StringRef>> ReferencedOffsets; + for (auto Loc : locateDecl(*Foo)) { + if (auto *SL = std::get_if<SourceLocation>(&Loc.first)) { + auto [FID, Offset] = SM.getDecomposedLoc(SM.getFileLoc(*SL)); + ASSERT_EQ(FID, SM.getMainFileID()); + ReferencedOffsets.push_back( + {Offset, llvm::StringRef(Loc.second ? "$def" : "")}); + } else { + ADD_FAILURE() << "Got stdlib symbol: " << Foo->getNameAsString(); + } + } + llvm::sort(ReferencedOffsets); + auto AnnotatedCode = Target.code().str(); + for (auto [Offset, Annotation] : llvm::reverse(ReferencedOffsets)) + AnnotatedCode.insert(Offset, (Annotation + "^").str()); + EXPECT_EQ(Code, AnnotatedCode); +} + +TEST(LocateDecl, General) { + testLocate("struct ^foo; struct $def^foo {};"); + testLocate("namespace ns { void ^foo(); void $def^foo() {} }"); + testLocate("enum class ^foo; enum class $def^foo {};"); +} + } // namespace } // namespace include_cleaner } // namespace clang Index: clang-tools-extra/include-cleaner/unittests/CMakeLists.txt =================================================================== --- clang-tools-extra/include-cleaner/unittests/CMakeLists.txt +++ clang-tools-extra/include-cleaner/unittests/CMakeLists.txt @@ -17,6 +17,7 @@ clangAST clangBasic clangFrontend + clangToolingInclusionsStdlib ) target_link_libraries(ClangIncludeCleanerTests Index: clang-tools-extra/include-cleaner/lib/WalkAST.cpp =================================================================== --- clang-tools-extra/include-cleaner/lib/WalkAST.cpp +++ clang-tools-extra/include-cleaner/lib/WalkAST.cpp @@ -16,8 +16,11 @@ #include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/SourceLocation.h" +#include "clang/Tooling/Inclusions/StandardLibrary.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Casting.h" +#include <utility> +#include <vector> namespace clang { namespace include_cleaner { @@ -137,11 +140,45 @@ } }; +// Looks for a visible definition of \p D. Returns nullptr if none is avilable, +// or there are possibly multiple definitions (e.g. namespaces). +const NamedDecl *getDefinition(const NamedDecl *D) { + if (const auto *TD = dyn_cast<TagDecl>(D)) + return TD->getDefinition(); + if (const auto *VD = dyn_cast<VarDecl>(D)) + return VD->getDefinition(); + if (const auto *FD = dyn_cast<FunctionDecl>(D)) + return FD->getDefinition(); + if (const auto *CTD = dyn_cast<ClassTemplateDecl>(D)) + if (const auto *RD = CTD->getTemplatedDecl()) + return RD->getDefinition(); + if (isa<ValueDecl>(D) || isa<TemplateTypeParmDecl>(D) || + isa<TemplateTemplateParmDecl>(D)) + return D; + return nullptr; +} + } // namespace void walkAST(Decl &Root, DeclCallback Callback) { ASTWalker(Callback).TraverseDecl(&Root); } +std::vector<std::pair<SymbolLocation, bool /*IsDefinition*/>> +locateDecl(const NamedDecl &ND) { + if (auto Symbol = tooling::stdlib::Recognizer()(&ND)) + return {{*Symbol, true}}; + std::vector<std::pair<SymbolLocation, bool>> Result; + SourceLocation DefLoc; + if (auto *Def = getDefinition(&ND)) { + DefLoc = Def->getLocation(); + Result.push_back({DefLoc, true}); + } + for (auto *Redecl : ND.redecls()) + if (Redecl->getLocation() != DefLoc) + Result.push_back({Redecl->getLocation(), false}); + return Result; +} + } // namespace include_cleaner } // namespace clang Index: clang-tools-extra/include-cleaner/lib/CMakeLists.txt =================================================================== --- clang-tools-extra/include-cleaner/lib/CMakeLists.txt +++ clang-tools-extra/include-cleaner/lib/CMakeLists.txt @@ -6,5 +6,6 @@ LINK_LIBS clangBasic clangAST + clangToolingInclusionsStdlib ) Index: clang-tools-extra/include-cleaner/lib/AnalysisInternal.h =================================================================== --- clang-tools-extra/include-cleaner/lib/AnalysisInternal.h +++ clang-tools-extra/include-cleaner/lib/AnalysisInternal.h @@ -22,7 +22,11 @@ #define CLANG_INCLUDE_CLEANER_ANALYSISINTERNAL_H #include "clang/Basic/SourceLocation.h" +#include "clang/Tooling/Inclusions/StandardLibrary.h" #include "llvm/ADT/STLFunctionalExtras.h" +#include <utility> +#include <variant> +#include <vector> namespace clang { class Decl; @@ -57,6 +61,12 @@ void walkAST(Decl &Root, llvm::function_ref<void(SourceLocation, NamedDecl &, RefType)>); +using SymbolLocation = std::variant<SourceLocation, tooling::stdlib::Symbol>; +/// A set of locations that provides the declaration, while indicating if +/// location provides the definition. +std::vector<std::pair<SymbolLocation, bool /*IsDefinition*/>> +locateDecl(const NamedDecl &ND); + } // namespace include_cleaner } // namespace clang
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits