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

Reply via email to