VitaNuo updated this revision to Diff 520307.
VitaNuo marked 2 inline comments as done.
VitaNuo added a comment.
Herald added a subscriber: arphaman.

Move the check from "google" to "misc".


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D148793/new/

https://reviews.llvm.org/D148793

Files:
  clang-tools-extra/clang-tidy/CMakeLists.txt
  clang-tools-extra/clang-tidy/misc/CMakeLists.txt
  clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.cpp
  clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.h
  clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp
  clang-tools-extra/clangd/TidyProvider.cpp
  clang-tools-extra/docs/ReleaseNotes.rst
  clang-tools-extra/docs/clang-tidy/checks/list.rst
  clang-tools-extra/docs/clang-tidy/checks/misc/include-cleaner.rst
  clang-tools-extra/include-cleaner/include/clang-include-cleaner/Record.h
  clang-tools-extra/include-cleaner/lib/Record.cpp
  clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/bar.h
  clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/baz.h
  clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/foo.h
  clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/private.h
  clang-tools-extra/test/clang-tidy/checkers/misc/include-cleaner.cpp
  clang-tools-extra/test/clang-tidy/checkers/misc/system/string.h
  clang-tools-extra/test/clang-tidy/checkers/misc/system/vector.h
  clang-tools-extra/unittests/clang-tidy/CMakeLists.txt
  clang-tools-extra/unittests/clang-tidy/IncludeCleanerTest.cpp

Index: clang-tools-extra/unittests/clang-tidy/IncludeCleanerTest.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/unittests/clang-tidy/IncludeCleanerTest.cpp
@@ -0,0 +1,116 @@
+#include "ClangTidyDiagnosticConsumer.h"
+#include "ClangTidyOptions.h"
+#include "ClangTidyTest.h"
+#include "misc/IncludeCleanerCheck.h"
+#include "gtest/gtest.h"
+
+#include <optional>
+#include <vector>
+
+using namespace clang::tidy::misc;
+
+namespace clang {
+namespace tidy {
+namespace test {
+
+TEST(IncludeCleanerCheckTest, BasicUnusedIncludes) {
+  const char *PreCode = R"(
+#include "bar.h"
+#include <vector>
+)";
+  const char *PostCode = "\n\n\n";
+
+  std::vector<ClangTidyError> Errors;
+  EXPECT_EQ(PostCode, runCheckOnCode<IncludeCleanerCheck>(
+                          PreCode, &Errors, "file.cpp", std::nullopt,
+                          ClangTidyOptions(), {{"bar.h", ""}, {"vector", ""}}));
+}
+
+TEST(IncludeCleanerCheckTest, BasicMissingIncludes) {
+  const char *PreCode = R"(
+#include "bar.h"
+
+int BarResult = bar();
+int BazResult = baz();
+)";
+  const char *PostCode = R"(
+#include "bar.h"
+#include "baz.h"
+
+int BarResult = bar();
+int BazResult = baz();
+)";
+
+  std::vector<ClangTidyError> Errors;
+  EXPECT_EQ(PostCode,
+            runCheckOnCode<IncludeCleanerCheck>(
+                PreCode, &Errors, "file.cpp", std::nullopt, ClangTidyOptions(),
+                {{"bar.h", R"(#pragma once
+                              #include "baz.h"
+                              int bar();
+                           )"},
+                 {"baz.h", R"(#pragma once
+                              int baz();
+                           )"}}));
+}
+
+TEST(IncludeCleanerCheckTest, SystemMissingIncludes) {
+  const char *PreCode = R"(
+#include <vector>
+
+std::string HelloString;
+std::vector Vec;
+)";
+  const char *PostCode = R"(
+#include <string>
+#include <vector>
+
+std::string HelloString;
+std::vector Vec;
+)";
+
+  std::vector<ClangTidyError> Errors;
+  EXPECT_EQ(PostCode,
+            runCheckOnCode<IncludeCleanerCheck>(
+                PreCode, &Errors, "file.cpp", std::nullopt, ClangTidyOptions(),
+                {{"string", R"(#pragma once
+                              namespace std { class string {}; }
+                            )"},
+                 {"vector", R"(#pragma once
+                              #include <string>
+                              namespace std { class vector {}; }
+                            )"}}));
+}
+
+TEST(IncludeCleanerCheckTest, PragmaMissingIncludes) {
+  const char *PreCode = R"(
+#include "bar.h"
+
+int BarResult = bar();
+int FooBarResult = foobar();
+)";
+  const char *PostCode = R"(
+#include "bar.h"
+#include "public.h"
+
+int BarResult = bar();
+int FooBarResult = foobar();
+)";
+
+  std::vector<ClangTidyError> Errors;
+  EXPECT_EQ(PostCode,
+            runCheckOnCode<IncludeCleanerCheck>(
+                PreCode, &Errors, "file.cpp", std::nullopt, ClangTidyOptions(),
+                {{"bar.h", R"(#pragma once
+                              #include "private.h"
+                              int bar();
+                           )"},
+                 {"private.h", R"(#pragma once
+                                // IWYU pragma: private, include "public.h"
+                                int foobar();
+                               )"}}));
+}
+
+} // namespace test
+} // namespace tidy
+} // namespace clang
Index: clang-tools-extra/unittests/clang-tidy/CMakeLists.txt
===================================================================
--- clang-tools-extra/unittests/clang-tidy/CMakeLists.txt
+++ clang-tools-extra/unittests/clang-tidy/CMakeLists.txt
@@ -15,12 +15,14 @@
 get_filename_component(CLANG_LINT_SOURCE_DIR
   ${CMAKE_CURRENT_SOURCE_DIR}/../../clang-tidy REALPATH)
 include_directories(${CLANG_LINT_SOURCE_DIR})
+include_directories(BEFORE "${CMAKE_CURRENT_SOURCE_DIR}/../../include-cleaner/include")
 
 add_extra_unittest(ClangTidyTests
   AddConstTest.cpp
   ClangTidyDiagnosticConsumerTest.cpp
   ClangTidyOptionsTest.cpp
   DeclRefExprUtilsTest.cpp
+  IncludeCleanerTest.cpp
   IncludeInserterTest.cpp
   GlobListTest.cpp
   GoogleModuleTest.cpp
@@ -46,12 +48,14 @@
   clangTooling
   clangToolingCore
   clangTransformer
+  clangIncludeCleaner
   )
 target_link_libraries(ClangTidyTests
   PRIVATE
   clangTidy
   clangTidyAndroidModule
   clangTidyGoogleModule
+  clangTidyMiscModule
   clangTidyLLVMModule
   clangTidyModernizeModule
   clangTidyObjCModule
Index: clang-tools-extra/test/clang-tidy/checkers/misc/system/vector.h
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/misc/system/vector.h
@@ -0,0 +1,4 @@
+#pragma once
+#include <string.h>
+
+namespace std { class vector {}; }
Index: clang-tools-extra/test/clang-tidy/checkers/misc/system/string.h
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/misc/system/string.h
@@ -0,0 +1,2 @@
+#pragma once
+namespace std { class string {}; }
Index: clang-tools-extra/test/clang-tidy/checkers/misc/include-cleaner.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/misc/include-cleaner.cpp
@@ -0,0 +1,17 @@
+// RUN: %check_clang_tidy %s misc-include-cleaner %t -- -- -I%S/Inputs -isystem%S/system
+#include "bar.h"
+// CHECK-FIXES: {{^}}#include "baz.h"{{$}}
+#include "foo.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: Unused include "foo.h" [misc-include-cleaner]
+// CHECK-FIXES: {{^}}
+// CHECK-FIXES: {{^}}#include <string>{{$}}
+#include <vector.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: Unused include <vector.h> [misc-include-cleaner]
+// CHECK-FIXES: {{^}}
+int BarResult = bar();
+int BazResult = baz();
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: Missing include "baz.h" [misc-include-cleaner]
+std::string HelloString;
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: Missing include <string> [misc-include-cleaner]
+int FooBarResult = foobar();
+// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: Missing include "public.h" [misc-include-cleaner]
Index: clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/private.h
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/private.h
@@ -0,0 +1,2 @@
+// IWYU pragma: private, include "public.h"
+int foobar();
Index: clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/foo.h
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/foo.h
@@ -0,0 +1,2 @@
+#pragma once
+void foo();
Index: clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/baz.h
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/baz.h
@@ -0,0 +1,2 @@
+#pragma once
+int baz();
Index: clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/bar.h
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/bar.h
@@ -0,0 +1,4 @@
+#pragma once
+#include "baz.h"
+#include "private.h"
+int bar();
Index: clang-tools-extra/include-cleaner/lib/Record.cpp
===================================================================
--- clang-tools-extra/include-cleaner/lib/Record.cpp
+++ clang-tools-extra/include-cleaner/lib/Record.cpp
@@ -19,6 +19,8 @@
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Tooling/Inclusions/HeaderAnalysis.h"
 #include "clang/Tooling/Inclusions/StandardLibrary.h"
+#include <memory>
+#include <utility>
 
 namespace clang::include_cleaner {
 namespace {
@@ -151,6 +153,10 @@
       : SM(CI.getSourceManager()),
         HeaderInfo(CI.getPreprocessor().getHeaderSearchInfo()), Out(Out),
         UniqueStrings(Arena) {}
+  RecordPragma(const SourceManager &SM, const Preprocessor &P, PragmaIncludes *Out)
+      : SM(SM),
+        HeaderInfo(P.getHeaderSearchInfo()), Out(Out),
+        UniqueStrings(Arena) {}
 
   void FileChanged(SourceLocation Loc, FileChangeReason Reason,
                    SrcMgr::CharacteristicKind FileType,
@@ -342,6 +348,12 @@
   CI.getPreprocessor().addPPCallbacks(std::move(Record));
 }
 
+void PragmaIncludes::record(const SourceManager &SM, Preprocessor &P) {
+  auto Record = std::make_unique<RecordPragma>(SM, P, this);
+  P.addCommentHandler(Record.get());
+  P.addPPCallbacks(std::move(Record));
+}
+
 llvm::StringRef PragmaIncludes::getPublic(const FileEntry *F) const {
   auto It = IWYUPublic.find(F->getUniqueID());
   if (It == IWYUPublic.end())
Index: clang-tools-extra/include-cleaner/include/clang-include-cleaner/Record.h
===================================================================
--- clang-tools-extra/include-cleaner/include/clang-include-cleaner/Record.h
+++ clang-tools-extra/include-cleaner/include/clang-include-cleaner/Record.h
@@ -18,6 +18,7 @@
 #define CLANG_INCLUDE_CLEANER_RECORD_H
 
 #include "clang-include-cleaner/Types.h"
+#include "clang/Basic/SourceLocation.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/SmallVector.h"
@@ -52,6 +53,10 @@
   /// to the structure.
   void record(const CompilerInstance &CI);
 
+  /// Installs an analysing PPCallback and CommentHandler and populates results
+  /// to the structure.
+  void record(const SourceManager &SM, Preprocessor &P);
+
   /// Returns true if the given #include of the main-file should never be
   /// removed.
   bool shouldKeep(unsigned HashLineNumber) const {
Index: clang-tools-extra/docs/clang-tidy/checks/misc/include-cleaner.rst
===================================================================
--- /dev/null
+++ clang-tools-extra/docs/clang-tidy/checks/misc/include-cleaner.rst
@@ -0,0 +1,7 @@
+.. title:: clang-tidy - misc-include-cleaner
+
+misc-include-cleaner
+======================
+
+Checks for unused and missing includes.
+Findings correspond to https://clangd.llvm.org/design/include-cleaner.
Index: clang-tools-extra/docs/clang-tidy/checks/list.rst
===================================================================
--- clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -252,6 +252,7 @@
    `misc-confusable-identifiers <misc/confusable-identifiers.html>`_,
    `misc-const-correctness <misc/const-correctness.html>`_, "Yes"
    `misc-definitions-in-headers <misc/definitions-in-headers.html>`_, "Yes"
+   `misc-include-cleaner <misc/include-cleaner.html>`_, "Yes"
    `misc-misleading-bidirectional <misc/misleading-bidirectional.html>`_,
    `misc-misleading-identifier <misc/misleading-identifier.html>`_,
    `misc-misplaced-const <misc/misplaced-const.html>`_,
@@ -289,7 +290,7 @@
    `modernize-unary-static-assert <modernize/unary-static-assert.html>`_, "Yes"
    `modernize-use-auto <modernize/use-auto.html>`_, "Yes"
    `modernize-use-bool-literals <modernize/use-bool-literals.html>`_, "Yes"
-   `modernize-use-default-member-init <modernize/use-default-member-init.html>`_, "Yes"
+   `modernize-use-default-member-init <modernize/use-default-member-init.html>`_
    `modernize-use-emplace <modernize/use-emplace.html>`_, "Yes"
    `modernize-use-equals-default <modernize/use-equals-default.html>`_, "Yes"
    `modernize-use-equals-delete <modernize/use-equals-delete.html>`_, "Yes"
@@ -477,7 +478,7 @@
    `cppcoreguidelines-explicit-virtual-functions <cppcoreguidelines/explicit-virtual-functions.html>`_, `modernize-use-override <modernize/use-override.html>`_, "Yes"
    `cppcoreguidelines-macro-to-enum <cppcoreguidelines/macro-to-enum.html>`_, `modernize-macro-to-enum <modernize/macro-to-enum.html>`_, "Yes"
    `cppcoreguidelines-non-private-member-variables-in-classes <cppcoreguidelines/non-private-member-variables-in-classes.html>`_, `misc-non-private-member-variables-in-classes <misc/non-private-member-variables-in-classes.html>`_,
-   `cppcoreguidelines-use-default-member-init <cppcoreguidelines/use-default-member-init.html>`_, `modernize-use-default-member-init <modernize/use-default-member-init.html>`_,
+   `cppcoreguidelines-use-default-member-init <cppcoreguidelines/use-default-member-init.html>`_, `modernize-use-default-member-init <modernize/use-default-member-init.html>`_, "Yes"
    `fuchsia-header-anon-namespaces <fuchsia/header-anon-namespaces.html>`_, `google-build-namespaces <google/build-namespaces.html>`_,
    `google-readability-braces-around-statements <google/readability-braces-around-statements.html>`_, `readability-braces-around-statements <readability/braces-around-statements.html>`_, "Yes"
    `google-readability-function-size <google/readability-function-size.html>`_, `readability-function-size <readability/function-size.html>`_,
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -163,6 +163,11 @@
   Enforces consistent token representation for invoked binary, unary and
   overloaded operators in C++ code.
 
+- New :doc:`misc-include-cleaner
+  <clang-tidy/checks/misc/include-cleaner>` check.
+
+  Checks for unused and missing includes.
+
 New check aliases
 ^^^^^^^^^^^^^^^^^
 
Index: clang-tools-extra/clangd/TidyProvider.cpp
===================================================================
--- clang-tools-extra/clangd/TidyProvider.cpp
+++ clang-tools-extra/clangd/TidyProvider.cpp
@@ -195,32 +195,34 @@
 
 TidyProvider disableUnusableChecks(llvm::ArrayRef<std::string> ExtraBadChecks) {
   constexpr llvm::StringLiteral Seperator(",");
-  static const std::string BadChecks =
-      llvm::join_items(Seperator,
-                       // We want this list to start with a seperator to
-                       // simplify appending in the lambda. So including an
-                       // empty string here will force that.
-                       "",
-                       // ----- False Positives -----
-
-                       // Check relies on seeing ifndef/define/endif directives,
-                       // clangd doesn't replay those when using a preamble.
-                       "-llvm-header-guard", "-modernize-macro-to-enum",
-
-                       // ----- Crashing Checks -----
-
-                       // Check can choke on invalid (intermediate) c++
-                       // code, which is often the case when clangd
-                       // tries to build an AST.
-                       "-bugprone-use-after-move",
-                       // Alias for bugprone-use-after-move.
-                       "-hicpp-invalid-access-moved",
-
-                       // ----- Performance problems -----
-
-                       // This check runs expensive analysis for each variable.
-                       // It has been observed to increase reparse time by 10x.
-                       "-misc-const-correctness");
+  static const std::string BadChecks = llvm::join_items(
+      Seperator,
+      // We want this list to start with a seperator to
+      // simplify appending in the lambda. So including an
+      // empty string here will force that.
+      "",
+      // ----- False Positives -----
+
+      // Check relies on seeing ifndef/define/endif directives,
+      // clangd doesn't replay those when using a preamble.
+      "-llvm-header-guard", "-modernize-macro-to-enum",
+
+      // ----- Crashing Checks -----
+
+      // Check can choke on invalid (intermediate) c++
+      // code, which is often the case when clangd
+      // tries to build an AST.
+      "-bugprone-use-after-move",
+      // Alias for bugprone-use-after-move.
+      "-hicpp-invalid-access-moved",
+      // include-cleaner is directly integrated in IncludeCleaner.cpp
+      "-misc-include-cleaner",
+
+      // ----- Performance problems -----
+
+      // This check runs expensive analysis for each variable.
+      // It has been observed to increase reparse time by 10x.
+      "-misc-const-correctness");
 
   size_t Size = BadChecks.size();
   for (const std::string &Str : ExtraBadChecks) {
Index: clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp
+++ clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp
@@ -12,6 +12,7 @@
 #include "ConfusableIdentifierCheck.h"
 #include "ConstCorrectnessCheck.h"
 #include "DefinitionsInHeadersCheck.h"
+#include "IncludeCleanerCheck.h"
 #include "MisleadingBidirectional.h"
 #include "MisleadingIdentifier.h"
 #include "MisplacedConstCheck.h"
@@ -41,6 +42,7 @@
         "misc-const-correctness");
     CheckFactories.registerCheck<DefinitionsInHeadersCheck>(
         "misc-definitions-in-headers");
+    CheckFactories.registerCheck<IncludeCleanerCheck>("misc-include-cleaner");
     CheckFactories.registerCheck<MisleadingBidirectionalCheck>(
         "misc-misleading-bidirectional");
     CheckFactories.registerCheck<MisleadingIdentifierCheck>(
Index: clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.h
@@ -0,0 +1,55 @@
+//===--- IncludeCleanerCheck.h - clang-tidy ---------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_INCLUDECLEANER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_INCLUDECLEANER_H
+
+#include "../ClangTidyCheck.h"
+#include "../ClangTidyDiagnosticConsumer.h"
+#include "clang-include-cleaner/Record.h"
+#include "clang-include-cleaner/Types.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Lex/HeaderSearch.h"
+#include "clang/Tooling/Syntax/Tokens.h"
+#include <string>
+
+namespace clang::tidy::misc {
+
+struct MissingIncludeInfo {
+  SourceLocation SymRefLocation;
+  std::string MissingHeaderSpelling;
+};
+
+/// Compute unused and missing includes and suggest fixes.
+/// Findings correspond to https://clangd.llvm.org/design/include-cleaner.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc/include-cleaner.html
+class IncludeCleanerCheck : public ClangTidyCheck {
+public:
+  IncludeCleanerCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
+                           Preprocessor *ModuleExpanderPP) override;
+  void onEndOfTranslationUnit() override;
+
+private:
+  include_cleaner::RecordedPP RecordedPreprocessor;
+  include_cleaner::PragmaIncludes RecordedPI;
+  HeaderSearch *HS;
+  Decl *TUDecl;
+  const SourceManager *SM;
+};
+
+} // namespace clang::tidy::misc
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_INCLUDECLEANER_H
Index: clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.cpp
@@ -0,0 +1,145 @@
+//===--- IncludeCleanerCheck.cpp - clang-tidy -----------------------------===//
+//
+// 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 "IncludeCleanerCheck.h"
+#include "clang-include-cleaner/Analysis.h"
+#include "clang-include-cleaner/Record.h"
+#include "clang-include-cleaner/Types.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/FileEntry.h"
+#include "clang/Format/Format.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "clang/Tooling/Inclusions/HeaderIncludes.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/StringRef.h"
+#include <optional>
+#include <string>
+#include <vector>
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::misc {
+
+void IncludeCleanerCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+  Finder->addMatcher(translationUnitDecl().bind("top"), this);
+}
+
+void IncludeCleanerCheck::registerPPCallbacks(const SourceManager &SM,
+                                              Preprocessor *PP,
+                                              Preprocessor *ModuleExpanderPP) {
+  PP->addPPCallbacks(RecordedPreprocessor.record(*PP));
+  HS = &PP->getHeaderSearchInfo();
+  RecordedPI.record(SM, *PP);
+}
+
+void IncludeCleanerCheck::check(const MatchFinder::MatchResult &Result) {
+  TUDecl = const_cast<Decl *>(Result.Nodes.getNodeAs<Decl>("top"));
+  SM = Result.SourceManager;
+}
+
+void IncludeCleanerCheck::onEndOfTranslationUnit() {
+  const FileEntry *MainFile = SM->getFileEntryForID(SM->getMainFileID());
+  llvm::DenseSet<const include_cleaner::Include *> Used;
+  std::vector<MissingIncludeInfo> Missing;
+  walkUsed(TUDecl, RecordedPreprocessor.MacroReferences, &RecordedPI, *SM,
+           [&](const include_cleaner::SymbolReference &Ref,
+               llvm::ArrayRef<include_cleaner::Header> Providers) {
+             bool Satisfied = false;
+             for (const include_cleaner::Header &H : Providers) {
+               if (H.kind() == include_cleaner::Header::Physical &&
+                   H.physical() == MainFile)
+                 Satisfied = true;
+
+               for (const include_cleaner::Include *I :
+                    RecordedPreprocessor.Includes.match(H)) {
+                 Used.insert(I);
+                 Satisfied = true;
+               }
+             }
+             if (!Satisfied && !Providers.empty() &&
+                 Ref.RT == include_cleaner::RefType::Explicit)
+               Missing.push_back(
+                   {Ref.RefLocation, include_cleaner::spellHeader(
+                                         Providers.front(), *HS, MainFile)});
+           });
+
+  std::vector<const include_cleaner::Include *> Unused;
+  for (const include_cleaner::Include &I :
+       RecordedPreprocessor.Includes.all()) {
+    if (Used.contains(&I) || !I.Resolved)
+      continue;
+    if (RecordedPI.shouldKeep(I.Line))
+      continue;
+    // Check if main file is the public interface for a private header. If so
+    // we shouldn't diagnose it as unused.
+    if (auto PHeader = RecordedPI.getPublic(I.Resolved); !PHeader.empty()) {
+      PHeader = PHeader.trim("<>\"");
+      // Since most private -> public mappings happen in a verbatim way, we
+      // check textually here. This might go wrong in presence of symlinks or
+      // header mappings. But that's not different than rest of the places.
+      if (MainFile->tryGetRealPathName().endswith(PHeader))
+        continue;
+    }
+
+    Unused.push_back(&I);
+  }
+
+  for (const auto *Inc : Unused) {
+    std::string Description("Unused include ");
+    Description += Inc->quote();
+
+    diag(Inc->HashLocation, Description) << FixItHint::CreateRemoval(
+        {Inc->HashLocation,
+         SM->getComposedLoc(SM->getMainFileID(),
+                            SM->getDecomposedLoc(Inc->HashLocation).second +
+                                std::string("#include").length() +
+                                Inc->quote().length() + 1)});
+  }
+
+  auto FileStyle = format::getStyle(
+      format::DefaultFormatStyle, MainFile->tryGetRealPathName(),
+      format::DefaultFallbackStyle, SM->getBufferData(SM->getMainFileID()),
+      &SM->getFileManager().getVirtualFileSystem());
+  if (!FileStyle) {
+    FileStyle = format::getLLVMStyle();
+  }
+  for (const auto &Inc : Missing) {
+    std::string Description("Missing include ");
+    Description += Inc.MissingHeaderSpelling;
+
+    if (MainFile->getName().empty())
+      continue;
+    tooling::HeaderIncludes HeaderIncludes(
+        MainFile->getName(), SM->getBufferData(SM->getMainFileID()),
+        FileStyle->IncludeStyle);
+    bool Angled = llvm::StringRef{Inc.MissingHeaderSpelling}.starts_with("<");
+    // We might suggest insertion of an existing include in edge cases, e.g.,
+    // include is present in a PP-disabled region, or spelling of the header
+    // turns out to be the same as one of the unresolved includes in the
+    // main file.
+    std::optional<tooling::Replacement> Replacement = HeaderIncludes.insert(
+        llvm::StringRef{Inc.MissingHeaderSpelling}.trim("\"<>"), Angled,
+        tooling::IncludeDirective::Include);
+    if (!Replacement.has_value())
+      continue;
+
+    diag(Inc.SymRefLocation, Description) << FixItHint::CreateInsertion(
+        SM->getComposedLoc(SM->getMainFileID(), Replacement->getOffset()),
+        Replacement->getReplacementText());
+  }
+}
+
+} // namespace clang::tidy::misc
Index: clang-tools-extra/clang-tidy/misc/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/misc/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/misc/CMakeLists.txt
@@ -19,6 +19,7 @@
   ConstCorrectnessCheck.cpp
   DefinitionsInHeadersCheck.cpp
   ConfusableIdentifierCheck.cpp
+  IncludeCleanerCheck.cpp
   MiscTidyModule.cpp
   MisleadingBidirectional.cpp
   MisleadingIdentifier.cpp
Index: clang-tools-extra/clang-tidy/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/CMakeLists.txt
@@ -7,6 +7,7 @@
   ${CMAKE_CURRENT_SOURCE_DIR}/clang-tidy-config.h.cmake
   ${CMAKE_CURRENT_BINARY_DIR}/clang-tidy-config.h)
 include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR})
+include_directories(BEFORE "${CMAKE_CURRENT_SOURCE_DIR}/../include-cleaner/include")
 
 add_clang_library(clangTidy
   ClangTidy.cpp
@@ -38,6 +39,7 @@
   clangSerialization
   clangTooling
   clangToolingCore
+  clangIncludeCleaner
   )
 
 if(CLANG_TIDY_ENABLE_STATIC_ANALYZER)
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to