[clang-tools-extra] ce286ec - [IncludeCleaner] Add public API

Kadir Cetinkaya via cfe-commits cfe-commits at lists.llvm.org
Mon Oct 24 08:06:21 PDT 2022


Author: Kadir Cetinkaya
Date: 2022-10-24T17:06:15+02:00
New Revision: ce286eccacd660f2b71cba15d9b83fe2217e36b8

URL: https://github.com/llvm/llvm-project/commit/ce286eccacd660f2b71cba15d9b83fe2217e36b8
DIFF: https://github.com/llvm/llvm-project/commit/ce286eccacd660f2b71cba15d9b83fe2217e36b8.diff

LOG: [IncludeCleaner] Add public API

Introduces walkUsed, a very simple version of the public API to enable
incremental development on rest of the pieces.

Differential Revision: https://reviews.llvm.org/D136293

Added: 
    clang-tools-extra/include-cleaner/include/clang-include-cleaner/Analysis.h
    clang-tools-extra/include-cleaner/lib/Analysis.cpp
    clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp

Modified: 
    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

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/include-cleaner/include/clang-include-cleaner/Analysis.h b/clang-tools-extra/include-cleaner/include/clang-include-cleaner/Analysis.h
new file mode 100644
index 0000000000000..0c28119831988
--- /dev/null
+++ b/clang-tools-extra/include-cleaner/include/clang-include-cleaner/Analysis.h
@@ -0,0 +1,75 @@
+//===--- Analysis.h - Analyze symbol references in AST ------------- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// A library that provides usage analysis for symbols based on AST analysis.
+//===----------------------------------------------------------------------===//
+
+#ifndef CLANG_INCLUDE_CLEANER_ANALYSIS_H
+#define CLANG_INCLUDE_CLEANER_ANALYSIS_H
+
+#include "clang/Tooling/Inclusions/StandardLibrary.h"
+#include "llvm/ADT/STLFunctionalExtras.h"
+#include <variant>
+
+namespace clang {
+class SourceLocation;
+class Decl;
+class FileEntry;
+namespace include_cleaner {
+
+/// An entity that can be referenced in the code.
+struct Symbol {
+  Symbol(Decl &D) : Storage(&D) {}
+  Symbol(tooling::stdlib::Symbol S) : Storage(S) {}
+
+private:
+  // FIXME: Add support for macros.
+  std::variant<const Decl *, tooling::stdlib::Symbol> Storage;
+};
+
+/// Represents a file that provides some symbol. Might not be includeable, e.g.
+/// built-in or main-file itself.
+struct Header {
+  /// A physical (or logical, in case of a builtin) file.
+  Header(const FileEntry *FE) : Storage(FE) {}
+  /// A logical file representing a stdlib header.
+  Header(tooling::stdlib::Header H) : Storage(H) {}
+
+  bool operator==(const Header &RHS) const { return Storage == RHS.Storage; }
+
+private:
+  // FIXME: Handle verbatim spellings.
+  std::variant<const FileEntry *, tooling::stdlib::Header> Storage;
+};
+/// A UsedSymbolCB is a callback invoked for each symbol reference seen.
+///
+/// References occur at a particular location, refer to a single symbol, and
+/// that symbol may be provided by several headers.
+/// FIXME: Provide signals about the reference type and providing headers so the
+/// caller can filter and rank the results.
+using UsedSymbolCB = llvm::function_ref<void(
+    SourceLocation RefLoc, Symbol Target, llvm::ArrayRef<Header> Providers)>;
+
+/// Find and report all references to symbols in a region of code.
+///
+/// The AST traversal is rooted at ASTRoots - typically top-level declarations
+/// of a single source file.
+/// FIXME: Handle macro uses.
+///
+/// This is the main entrypoint of the include-cleaner library, and can be used:
+///  - to diagnose missing includes: a referenced symbol is provided by
+///    headers which don't match any #include in the main file
+///  - to diagnose unused includes: an #include in the main file does not match
+///    the headers for any referenced symbol
+/// FIXME: Take in an include structure to improve location to header mappings
+/// (e.g. IWYU pragmas).
+void walkUsed(llvm::ArrayRef<Decl *> ASTRoots, UsedSymbolCB CB);
+
+} // namespace include_cleaner
+} // namespace clang
+
+#endif

diff  --git a/clang-tools-extra/include-cleaner/lib/Analysis.cpp b/clang-tools-extra/include-cleaner/lib/Analysis.cpp
new file mode 100644
index 0000000000000..72fac3c9c0084
--- /dev/null
+++ b/clang-tools-extra/include-cleaner/lib/Analysis.cpp
@@ -0,0 +1,50 @@
+//===--- Analysis.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang-include-cleaner/Analysis.h"
+#include "AnalysisInternal.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Tooling/Inclusions/StandardLibrary.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+
+namespace clang::include_cleaner {
+namespace {
+llvm::SmallVector<Header>
+toHeader(llvm::ArrayRef<tooling::stdlib::Header> Headers) {
+  llvm::SmallVector<Header> Result;
+  llvm::for_each(Headers, [&](tooling::stdlib::Header H) {
+    Result.emplace_back(Header(H));
+  });
+  return Result;
+}
+
+} // namespace
+void walkUsed(llvm::ArrayRef<Decl *> ASTRoots, UsedSymbolCB CB) {
+  tooling::stdlib::Recognizer Recognizer;
+  for (auto *Root : ASTRoots) {
+    auto &SM = Root->getASTContext().getSourceManager();
+    walkAST(*Root, [&](SourceLocation Loc, NamedDecl &ND) {
+      if (auto SS = Recognizer(&ND)) {
+        // FIXME: Also report forward decls from main-file, so that the caller
+        // can decide to insert/ignore a header.
+        return CB(Loc, Symbol(*SS), toHeader(SS->headers()));
+      }
+      // FIXME: Extract locations from redecls.
+      // FIXME: Handle IWYU pragmas, non self-contained files.
+      // FIXME: Handle macro locations.
+      if (auto *FE = SM.getFileEntryForID(SM.getFileID(ND.getLocation())))
+        return CB(Loc, Symbol(ND), {Header(FE)});
+    });
+  }
+  // FIXME: Handle references of macros.
+}
+
+} // namespace clang::include_cleaner

diff  --git a/clang-tools-extra/include-cleaner/lib/CMakeLists.txt b/clang-tools-extra/include-cleaner/lib/CMakeLists.txt
index b255abca7499d..759261e90499a 100644
--- a/clang-tools-extra/include-cleaner/lib/CMakeLists.txt
+++ b/clang-tools-extra/include-cleaner/lib/CMakeLists.txt
@@ -1,6 +1,7 @@
 set(LLVM_LINK_COMPONENTS Support)
 
 add_clang_library(clangIncludeCleaner
+  Analysis.cpp
   HTMLReport.cpp
   Record.cpp
   WalkAST.cpp
@@ -9,5 +10,6 @@ add_clang_library(clangIncludeCleaner
   clangAST
   clangBasic
   clangLex
+  clangToolingInclusionsStdlib
   )
 

diff  --git a/clang-tools-extra/include-cleaner/lib/WalkAST.cpp b/clang-tools-extra/include-cleaner/lib/WalkAST.cpp
index b7354fe300e04..a86534337c273 100644
--- a/clang-tools-extra/include-cleaner/lib/WalkAST.cpp
+++ b/clang-tools-extra/include-cleaner/lib/WalkAST.cpp
@@ -9,8 +9,7 @@
 #include "AnalysisInternal.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 
-namespace clang {
-namespace include_cleaner {
+namespace clang::include_cleaner {
 namespace {
 using DeclCallback = llvm::function_ref<void(SourceLocation, NamedDecl &)>;
 
@@ -43,5 +42,4 @@ void walkAST(Decl &Root, DeclCallback Callback) {
   ASTWalker(Callback).TraverseDecl(&Root);
 }
 
-} // namespace include_cleaner
-} // namespace clang
+} // namespace clang::include_cleaner

diff  --git a/clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp b/clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp
new file mode 100644
index 0000000000000..c7bb12f34b972
--- /dev/null
+++ b/clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp
@@ -0,0 +1,69 @@
+//===--- AnalysisTest.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang-include-cleaner/Analysis.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Testing/TestAST.h"
+#include "clang/Tooling/Inclusions/StandardLibrary.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Testing/Support/Annotations.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <cstddef>
+
+namespace clang::include_cleaner {
+namespace {
+using testing::Pair;
+using testing::UnorderedElementsAre;
+
+TEST(WalkUsed, Basic) {
+  // FIXME: Have a fixture for setting up tests.
+  llvm::Annotations HeaderCode(R"cpp(
+  void foo();
+  namespace std { class vector {}; })cpp");
+  llvm::Annotations Code(R"cpp(
+  void bar() {
+    $foo^foo();
+    std::$vector^vector v;
+  }
+  )cpp");
+  TestInputs Inputs(Code.code());
+  Inputs.ExtraFiles["header.h"] = HeaderCode.code().str();
+  Inputs.ExtraArgs.push_back("-include");
+  Inputs.ExtraArgs.push_back("header.h");
+  TestAST AST(Inputs);
+
+  llvm::SmallVector<Decl *> TopLevelDecls;
+  for (Decl *D : AST.context().getTranslationUnitDecl()->decls()) {
+    TopLevelDecls.emplace_back(D);
+  }
+
+  auto &SM = AST.sourceManager();
+  llvm::DenseMap<size_t, std::vector<Header>> OffsetToProviders;
+  walkUsed(TopLevelDecls, [&](SourceLocation RefLoc, Symbol S,
+                              llvm::ArrayRef<Header> Providers) {
+    auto [FID, Offset] = SM.getDecomposedLoc(RefLoc);
+    EXPECT_EQ(FID, SM.getMainFileID());
+    OffsetToProviders.try_emplace(Offset, Providers.vec());
+  });
+  auto HeaderFile = AST.fileManager().getFile("header.h").get();
+  EXPECT_THAT(
+      OffsetToProviders,
+      UnorderedElementsAre(
+          Pair(Code.point("foo"), UnorderedElementsAre(Header(HeaderFile))),
+          Pair(Code.point("vector"),
+               UnorderedElementsAre(Header(
+                   tooling::stdlib::Header::named("<vector>").value())))));
+}
+
+} // namespace
+} // namespace clang::include_cleaner

diff  --git a/clang-tools-extra/include-cleaner/unittests/CMakeLists.txt b/clang-tools-extra/include-cleaner/unittests/CMakeLists.txt
index 7167d9214427d..ba9fc6419f388 100644
--- a/clang-tools-extra/include-cleaner/unittests/CMakeLists.txt
+++ b/clang-tools-extra/include-cleaner/unittests/CMakeLists.txt
@@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS
 
 add_custom_target(ClangIncludeCleanerUnitTests)
 add_unittest(ClangIncludeCleanerUnitTests ClangIncludeCleanerTests
+  AnalysisTest.cpp
   RecordTest.cpp
   WalkASTTest.cpp
 )
@@ -18,6 +19,7 @@ clang_target_link_libraries(ClangIncludeCleanerTests
   clangAST
   clangBasic
   clangFrontend
+  clangToolingInclusionsStdlib
   )
 
 target_link_libraries(ClangIncludeCleanerTests

diff  --git a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
index 3e4fd42bf9cc5..f53aa4e37dc3c 100644
--- a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
+++ b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
@@ -1,15 +1,28 @@
+//===--- WalkASTTest.cpp ------------------------------------------- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
 #include "AnalysisInternal.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/Basic/FileManager.h"
+#include "clang/Basic/SourceLocation.h"
 #include "clang/Frontend/TextDiagnostic.h"
 #include "clang/Testing/TestAST.h"
+#include "llvm/ADT/ArrayRef.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Testing/Support/Annotations.h"
+#include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include <cstddef>
+#include <vector>
 
-namespace clang {
-namespace include_cleaner {
+namespace clang::include_cleaner {
 namespace {
+using testing::Pair;
+using testing::UnorderedElementsAre;
 
 // Specifies a test of which symbols are referenced by a piece of code.
 //
@@ -106,5 +119,4 @@ TEST(WalkAST, Alias) {
 }
 
 } // namespace
-} // namespace include_cleaner
-} // namespace clang
+} // namespace clang::include_cleaner


        


More information about the cfe-commits mailing list