kbobyrev created this revision.
kbobyrev added a reviewer: sammccall.
Herald added subscribers: usaxena95, arphaman.
kbobyrev requested review of this revision.
Herald added subscribers: cfe-commits, MaskRay, ilya-biryukov.
Herald added a project: clang-tools-extra.

Headers without include guards might have side effects or can be the files we
don't want to consider (e.g. tablegen ".inc" files). Skip them when translating
headers to the HeaderIDs that we will consider as unused.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D112695

Files:
  clang-tools-extra/clangd/IncludeCleaner.cpp
  clang-tools-extra/clangd/IncludeCleaner.h
  clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
  clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp

Index: clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
+++ clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
@@ -9,6 +9,7 @@
 #include "Annotations.h"
 #include "IncludeCleaner.h"
 #include "TestTU.h"
+#include "llvm/ADT/StringRef.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
@@ -16,6 +17,10 @@
 namespace clangd {
 namespace {
 
+std::string guard(llvm::StringRef Code) {
+  return "#pragma once\n" + Code.str();
+}
+
 using ::testing::ElementsAre;
 using ::testing::UnorderedElementsAre;
 
@@ -216,13 +221,13 @@
   // Build expected ast with symbols coming from headers.
   TestTU TU;
   TU.Filename = "foo.cpp";
-  TU.AdditionalFiles["foo.h"] = "void foo();";
-  TU.AdditionalFiles["a.h"] = "void a();";
-  TU.AdditionalFiles["b.h"] = "void b();";
-  TU.AdditionalFiles["dir/c.h"] = "void c();";
-  TU.AdditionalFiles["unused.h"] = "void unused();";
-  TU.AdditionalFiles["dir/unused.h"] = "void dirUnused();";
-  TU.AdditionalFiles["system/system_header.h"] = "";
+  TU.AdditionalFiles["foo.h"] = guard("void foo();");
+  TU.AdditionalFiles["a.h"] = guard("void a();");
+  TU.AdditionalFiles["b.h"] = guard("void b();");
+  TU.AdditionalFiles["dir/c.h"] = guard("void c();");
+  TU.AdditionalFiles["unused.h"] = guard("void unused();");
+  TU.AdditionalFiles["dir/unused.h"] = guard("void dirUnused();");
+  TU.AdditionalFiles["system/system_header.h"] = guard("");
   TU.ExtraArgs.push_back("-I" + testPath("dir"));
   TU.ExtraArgs.push_back("-isystem" + testPath("system"));
   TU.Code = MainFile.str();
@@ -252,6 +257,9 @@
   // ScratchBuffer with `flags::FLAGS_FOO` that will have FileID but not
   // FileEntry.
   TU.AdditionalFiles["macros.h"] = R"cpp(
+    #ifndef MACROS_H
+    #define MACROS_H
+
     #define DEFINE_FLAG(X) \
     namespace flags { \
     int FLAGS_##X; \
@@ -261,6 +269,8 @@
 
     #define ab x
     #define concat(x, y) x##y
+
+    #endif // MACROS_H
     )cpp";
   TU.ExtraArgs = {"-DCLI=42"};
   ParsedAST AST = TU.build();
@@ -278,7 +288,9 @@
                                    testPath("macros.h")));
 
   // Should not crash due to FileIDs that are not headers.
-  auto ReferencedHeaders = translateToHeaderIDs(ReferencedFiles, Includes, SM);
+  auto ReferencedHeaders =
+      translateToHeaderIDs(ReferencedFiles, Includes, SM,
+                           AST.getPreprocessor().getHeaderSearchInfo());
   std::vector<llvm::StringRef> ReferencedHeaderNames;
   for (IncludeStructure::HeaderID HID : ReferencedHeaders)
     ReferencedHeaderNames.push_back(Includes.getRealPath(HID));
Index: clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
+++ clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
@@ -1494,9 +1494,11 @@
   TestTU TU;
   TU.Code = Test.code().str();
   TU.AdditionalFiles["unused.h"] = R"cpp(
+    #pragma once
     void unused() {}
   )cpp";
   TU.AdditionalFiles["used.h"] = R"cpp(
+    #pragma once
     void used() {}
   )cpp";
   TU.AdditionalFiles["system/system_header.h"] = "";
Index: clang-tools-extra/clangd/IncludeCleaner.h
===================================================================
--- clang-tools-extra/clangd/IncludeCleaner.h
+++ clang-tools-extra/clangd/IncludeCleaner.h
@@ -24,6 +24,7 @@
 #include "Headers.h"
 #include "ParsedAST.h"
 #include "clang/Basic/SourceLocation.h"
+#include "clang/Lex/HeaderSearch.h"
 #include "llvm/ADT/DenseSet.h"
 #include <vector>
 
@@ -58,7 +59,8 @@
 /// FileIDs that are not true files (<built-in> etc) are dropped.
 llvm::DenseSet<IncludeStructure::HeaderID>
 translateToHeaderIDs(const llvm::DenseSet<FileID> &Files,
-                     const IncludeStructure &Includes, const SourceManager &SM);
+                     const IncludeStructure &Includes, const SourceManager &SM,
+                     HeaderSearch &HeaderSearch);
 
 /// Retrieves headers that are referenced from the main file but not used.
 std::vector<const Inclusion *>
Index: clang-tools-extra/clangd/IncludeCleaner.cpp
===================================================================
--- clang-tools-extra/clangd/IncludeCleaner.cpp
+++ clang-tools-extra/clangd/IncludeCleaner.cpp
@@ -14,6 +14,8 @@
 #include "support/Trace.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/Basic/SourceLocation.h"
+#include "clang/Lex/HeaderSearch.h"
+#include "clang/Lex/Preprocessor.h"
 #include "clang/Tooling/Syntax/Tokens.h"
 #include "llvm/Support/FormatVariadic.h"
 #include "llvm/Support/Path.h"
@@ -251,8 +253,8 @@
 
 llvm::DenseSet<IncludeStructure::HeaderID>
 translateToHeaderIDs(const llvm::DenseSet<FileID> &Files,
-                     const IncludeStructure &Includes,
-                     const SourceManager &SM) {
+                     const IncludeStructure &Includes, const SourceManager &SM,
+                     HeaderSearch &HeaderInfo) {
   llvm::DenseSet<IncludeStructure::HeaderID> TranslatedHeaderIDs;
   TranslatedHeaderIDs.reserve(Files.size());
   for (FileID FID : Files) {
@@ -261,6 +263,13 @@
       assert(isSpecialBuffer(FID, SM));
       continue;
     }
+    // Headers without include guards have side effects and are not
+    // self-contained, skip them.
+    if (!HeaderInfo.isFileMultipleIncludeGuarded(FE)) {
+      dlog("{0} doesn't have header guard and will not be considered unused",
+           FE->getName());
+      continue;
+    }
     const auto File = Includes.getID(FE);
     assert(File);
     TranslatedHeaderIDs.insert(*File);
@@ -272,8 +281,9 @@
   const auto &SM = AST.getSourceManager();
 
   auto Refs = findReferencedLocations(AST);
-  auto ReferencedFiles = translateToHeaderIDs(findReferencedFiles(Refs, SM),
-                                              AST.getIncludeStructure(), SM);
+  auto ReferencedFiles = translateToHeaderIDs(
+      findReferencedFiles(Refs, SM), AST.getIncludeStructure(), SM,
+      AST.getPreprocessor().getHeaderSearchInfo());
   return getUnused(AST.getIncludeStructure(), ReferencedFiles);
 }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to