njames93 created this revision.
njames93 added reviewers: carlosgalvezp, PiotrZSL.
Herald added a subscriber: xazax.hun.
Herald added a project: All.
njames93 updated this revision to Diff 520620.
njames93 added a comment.
njames93 updated this revision to Diff 520918.
njames93 retitled this revision from "[clang-tidy] Set traversal scope to 
prevent analysis in headers" to "[clang-tidy][WIP] Set traversal scope to 
prevent analysis in headers".
njames93 published this revision for review.
Herald added a project: clang-tools-extra.
Herald added a subscriber: cfe-commits.

Fix failing unittests


njames93 added a comment.

Fix infrastructure test failures



================
Comment at: clang-tools-extra/clang-tidy/ClangTidy.cpp:511
+
+    Consumers.push_back(std::make_unique<TraversalScopeConsumer>(
+        !Context.getOptions().SystemHeaders.value_or(false),
----------------
Here comes a stupid question :) If I understand correctly we are adding one 
more ASTConsumer to the list of Consumers, i.e. we are not replacing the 
"normal" consumer with the "optimized" consumer. So I fail to understand how 
adding this new consumer fixes the problems of the other consumer? I'd be happy 
to read more about the implementation if there are resources you could point me 
to.


================
Comment at: clang-tools-extra/clang-tidy/ClangTidy.cpp:511
+
+    Consumers.push_back(std::make_unique<TraversalScopeConsumer>(
+        !Context.getOptions().SystemHeaders.value_or(false),
----------------
carlosgalvezp wrote:
> Here comes a stupid question :) If I understand correctly we are adding one 
> more ASTConsumer to the list of Consumers, i.e. we are not replacing the 
> "normal" consumer with the "optimized" consumer. So I fail to understand how 
> adding this new consumer fixes the problems of the other consumer? I'd be 
> happy to read more about the implementation if there are resources you could 
> point me to.
The `MultiplexConsumer` runs each consumers callbacks in the order they are 
added.  So this consumers `HandleTranslationUnit` callback will be executed 
before the `FinderASTConsumer `runs its `HandleTranslationUnit` callback(The 
function that actually does the AST matching. So by modifying the `ASTContext` 
traversal scope in this `HandleTranslationUnit` callback, the results are 
visible when the Finder comes to do its matching.

Also part of this implementation was taken from clangd which uses a somewhat 
similar approach to only run tidy checks that originate in the main file 
https://github.com/llvm/llvm-project/blob/main/clang-tools-extra/clangd/ParsedAST.cpp#L73


See 
https://discourse.llvm.org/t/rfc-exclude-issues-from-system-headers-as-early-as-posible/68483


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D150126

Files:
  clang-tools-extra/clang-tidy/ClangTidy.cpp
  clang-tools-extra/clang-tidy/abseil/NoInternalDependenciesCheck.cpp
  clang-tools-extra/clang-tidy/abseil/UpgradeDurationConversionsCheck.cpp
  clang-tools-extra/clang-tidy/cert/DontModifyStdNamespaceCheck.cpp
  clang-tools-extra/test/clang-tidy/infrastructure/file-filter-symlinks.cpp
  clang-tools-extra/test/clang-tidy/infrastructure/file-filter.cpp
  clang-tools-extra/test/clang-tidy/infrastructure/line-filter.cpp
  clang-tools-extra/test/clang-tidy/infrastructure/system-headers.cpp

Index: clang-tools-extra/test/clang-tidy/infrastructure/system-headers.cpp
===================================================================
--- clang-tools-extra/test/clang-tidy/infrastructure/system-headers.cpp
+++ clang-tools-extra/test/clang-tidy/infrastructure/system-headers.cpp
@@ -11,9 +11,9 @@
 // RUN: clang-tidy -help | FileCheck -check-prefix=CHECK-OPT-PRESENT %s
 
 // RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -system-headers=true %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-SYSTEM-HEADERS %s
-// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -system-headers=false %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-NO-SYSTEM-HEADERS %s
+// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -system-headers=false %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-NO-SYSTEM-HEADERS %s --allow-empty
 // RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -config='SystemHeaders: true' %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-SYSTEM-HEADERS %s
-// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -config='SystemHeaders: false' %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-NO-SYSTEM-HEADERS %s
+// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -config='SystemHeaders: false' %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-NO-SYSTEM-HEADERS %s --allow-empty
 
 #include <system_header.h>
 // CHECK-SYSTEM-HEADERS: system_header.h:1:13: warning: single-argument constructors must be marked explicit
Index: clang-tools-extra/test/clang-tidy/infrastructure/line-filter.cpp
===================================================================
--- clang-tools-extra/test/clang-tidy/infrastructure/line-filter.cpp
+++ clang-tools-extra/test/clang-tidy/infrastructure/line-filter.cpp
@@ -24,4 +24,4 @@
 
 // CHECK-NOT: warning:
 
-// CHECK: Suppressed 3 warnings (1 in non-user code, 2 due to line filter)
+// CHECK: Suppressed 2 warnings (2 due to line filter)
Index: clang-tools-extra/test/clang-tidy/infrastructure/file-filter.cpp
===================================================================
--- clang-tools-extra/test/clang-tidy/infrastructure/file-filter.cpp
+++ clang-tools-extra/test/clang-tidy/infrastructure/file-filter.cpp
@@ -61,14 +61,8 @@
 // CHECK4-NOT: warning:
 // CHECK4-QUIET-NOT: warning:
 
-// CHECK: Suppressed 3 warnings (3 in non-user code)
-// CHECK: Use -header-filter=.* to display errors from all non-system headers.
 // CHECK-QUIET-NOT: Suppressed
-// CHECK2: Suppressed 1 warnings (1 in non-user code)
-// CHECK2: Use -header-filter=.* {{.*}}
 // CHECK2-QUIET-NOT: Suppressed
-// CHECK3: Suppressed 2 warnings (2 in non-user code)
-// CHECK3: Use -header-filter=.* {{.*}}
 // CHECK3-QUIET-NOT: Suppressed
 // CHECK4-NOT: Suppressed {{.*}} warnings
 // CHECK4-NOT: Use -header-filter=.* {{.*}}
Index: clang-tools-extra/test/clang-tidy/infrastructure/file-filter-symlinks.cpp
===================================================================
--- clang-tools-extra/test/clang-tidy/infrastructure/file-filter-symlinks.cpp
+++ clang-tools-extra/test/clang-tidy/infrastructure/file-filter-symlinks.cpp
@@ -9,8 +9,8 @@
 // RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='dir1/dir2/\.\./header_alias\.h' -quiet %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER_ALIAS %s
 // RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header_alias\.h' %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER_ALIAS %s
 // RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header_alias\.h' -quiet %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER_ALIAS %s
-// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header\.h' %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER %s
-// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header\.h' -quiet %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER %s
+// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header\.h' %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER %s --allow-empty
+// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header\.h' -quiet %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER %s --allow-empty
 
 // Check that `-header-filter` operates on the same file paths as paths in
 // diagnostics printed by ClangTidy.
Index: clang-tools-extra/clang-tidy/cert/DontModifyStdNamespaceCheck.cpp
===================================================================
--- clang-tools-extra/clang-tidy/cert/DontModifyStdNamespaceCheck.cpp
+++ clang-tools-extra/clang-tidy/cert/DontModifyStdNamespaceCheck.cpp
@@ -35,6 +35,30 @@
                              Builder) != Args.end();
 }
 
+static bool isStdOrPosixImpl(const DeclContext *Ctx) {
+  if (!Ctx->isNamespace())
+    return false;
+
+  const auto *ND = cast<NamespaceDecl>(Ctx);
+  if (ND->isInline()) {
+    return isStdOrPosixImpl(ND->getParent());
+  }
+
+  if (!ND->getParent()->getRedeclContext()->isTranslationUnit())
+    return false;
+
+  const IdentifierInfo *II = ND->getIdentifier();
+  return II && (II->isStr("std") || II->isStr("posix"));
+}
+
+AST_MATCHER(Decl, isInStdOrPosixNS) {
+  for (const auto *Ctx = Node.getDeclContext(); Ctx; Ctx = Ctx->getParent()) {
+    if (isStdOrPosixImpl(Ctx))
+      return true;
+  }
+  return false;
+}
+
 } // namespace
 
 namespace clang::tidy::cert {
@@ -42,12 +66,10 @@
 void DontModifyStdNamespaceCheck::registerMatchers(MatchFinder *Finder) {
   auto HasStdParent =
       hasDeclContext(namespaceDecl(hasAnyName("std", "posix"),
-                                   unless(hasParent(namespaceDecl())))
+                                   unless(hasDeclContext(namespaceDecl())))
                          .bind("nmspc"));
-  auto UserDefinedType = qualType(
-      hasUnqualifiedDesugaredType(tagType(unless(hasDeclaration(tagDecl(
-          hasAncestor(namespaceDecl(hasAnyName("std", "posix"),
-                                    unless(hasParent(namespaceDecl()))))))))));
+  auto UserDefinedType = qualType(hasUnqualifiedDesugaredType(
+      tagType(unless(hasDeclaration(tagDecl(isInStdOrPosixNS()))))));
   auto HasNoProgramDefinedTemplateArgument = unless(
       hasAnyTemplateArgumentIncludingPack(refersToType(UserDefinedType)));
   auto InsideStdClassOrClassTemplateSpecialization = hasDeclContext(
Index: clang-tools-extra/clang-tidy/abseil/UpgradeDurationConversionsCheck.cpp
===================================================================
--- clang-tools-extra/clang-tidy/abseil/UpgradeDurationConversionsCheck.cpp
+++ clang-tools-extra/clang-tidy/abseil/UpgradeDurationConversionsCheck.cpp
@@ -34,7 +34,7 @@
               0, expr(hasType(cxxRecordDecl(hasName("::absl::Duration"))))),
           hasArgument(1, expr().bind("arg")),
           callee(functionDecl(
-              hasParent(functionTemplateDecl()),
+              ast_matchers::isTemplateInstantiation(),
               unless(hasTemplateArgument(0, refersToType(builtinType()))),
               hasAnyName("operator*=", "operator/="))))
           .bind("OuterExpr"),
@@ -46,7 +46,7 @@
       cxxMemberCallExpr(
           callee(cxxMethodDecl(
               ofClass(cxxRecordDecl(hasName("::absl::Duration"))),
-              hasParent(functionTemplateDecl()),
+              ast_matchers::isTemplateInstantiation(),
               unless(hasTemplateArgument(0, refersToType(builtinType()))),
               hasAnyName("operator*=", "operator/="))),
           argumentCountIs(1), hasArgument(0, expr().bind("arg")))
@@ -58,7 +58,7 @@
   // built-in type.
   Finder->addMatcher(
       callExpr(callee(functionDecl(
-                   hasParent(functionTemplateDecl()),
+                   ast_matchers::isTemplateInstantiation(),
                    unless(hasTemplateArgument(0, refersToType(builtinType()))),
                    hasAnyName("::absl::operator*", "::absl::operator/"))),
                argumentCountIs(2),
@@ -72,7 +72,7 @@
   // built-in type and `b` has type `absl::Duration`.
   Finder->addMatcher(
       callExpr(callee(functionDecl(
-                   hasParent(functionTemplateDecl()),
+                   ast_matchers::isTemplateInstantiation(),
                    unless(hasTemplateArgument(0, refersToType(builtinType()))),
                    hasName("::absl::operator*"))),
                argumentCountIs(2), hasArgument(0, expr().bind("arg")),
@@ -98,16 +98,17 @@
   //   `absl::Hours(x)`
   // where `x` is not of a built-in type.
   Finder->addMatcher(
-      traverse(TK_AsIs, implicitCastExpr(
-                            anyOf(hasCastKind(CK_UserDefinedConversion),
-                                  has(implicitCastExpr(
-                                      hasCastKind(CK_UserDefinedConversion)))),
-                            hasParent(callExpr(
-                                callee(functionDecl(
-                                    DurationFactoryFunction(),
-                                    unless(hasParent(functionTemplateDecl())))),
-                                hasArgument(0, expr().bind("arg")))))
-                            .bind("OuterExpr")),
+      traverse(TK_AsIs,
+               implicitCastExpr(
+                   anyOf(hasCastKind(CK_UserDefinedConversion),
+                         has(implicitCastExpr(
+                             hasCastKind(CK_UserDefinedConversion)))),
+                   hasParent(callExpr(
+                       callee(functionDecl(
+                           DurationFactoryFunction(),
+                           unless(ast_matchers::isTemplateInstantiation()))),
+                       hasArgument(0, expr().bind("arg")))))
+                   .bind("OuterExpr")),
       this);
 }
 
Index: clang-tools-extra/clang-tidy/abseil/NoInternalDependenciesCheck.cpp
===================================================================
--- clang-tools-extra/clang-tidy/abseil/NoInternalDependenciesCheck.cpp
+++ clang-tools-extra/clang-tidy/abseil/NoInternalDependenciesCheck.cpp
@@ -19,13 +19,13 @@
   // TODO: refactor matcher to be configurable or just match on any internal
   // access from outside the enclosing namespace.
 
-  Finder->addMatcher(
-      nestedNameSpecifierLoc(loc(specifiesNamespace(namespaceDecl(
-                                 matchesName("internal"),
-                                 hasParent(namespaceDecl(hasName("absl")))))),
-                             unless(isInAbseilFile()))
-          .bind("InternalDep"),
-      this);
+  Finder->addMatcher(nestedNameSpecifierLoc(
+                         loc(specifiesNamespace(namespaceDecl(
+                             matchesName("internal"),
+                             hasDeclContext(namespaceDecl(hasName("absl")))))),
+                         unless(isInAbseilFile()))
+                         .bind("InternalDep"),
+                     this);
 }
 
 void NoInternalDependenciesCheck::check(const MatchFinder::MatchResult &Result) {
Index: clang-tools-extra/clang-tidy/ClangTidy.cpp
===================================================================
--- clang-tools-extra/clang-tidy/ClangTidy.cpp
+++ clang-tools-extra/clang-tidy/ClangTidy.cpp
@@ -431,6 +431,87 @@
   }
 
   std::vector<std::unique_ptr<ASTConsumer>> Consumers;
+
+  if (!Context.getOptions().SystemHeaders.value_or(false) ||
+      Context.getOptions().HeaderFilterRegex.value_or("") != ".*") {
+    class TraversalScopeConsumer : public ASTConsumer {
+      void Initialize(ASTContext &Context) override {
+        // Make sure the main file ID always gets included.
+        Cache.insert(
+            std::make_pair(Context.getSourceManager().getMainFileID(), true));
+      }
+
+      bool shouldKeepDeclsFromFile(SourceManager &SM, FileID FID) {
+        // Tricky cases, just assume we should include
+        if (FID.isInvalid() || FID == FileID::getSentinel())
+          return true;
+
+        auto [Item, Inserted] = Cache.try_emplace(FID, true);
+
+        // Already in cache, return cached value
+        if (!Inserted)
+          return Item->getSecond();
+
+        // Hash value isn't strictly what we need, but it currently just returns
+        // the opaque id which is what we need. Any other way to get the ID
+        // would require modifying the FID class or type-punning.
+        auto Value = static_cast<int>(FID.getHashValue());
+        SrcMgr::SLocEntry Entry;
+        if (Value > 0) {
+          // Regular file
+          Entry = SM.getLocalSLocEntry(static_cast<unsigned>(Value));
+        } else {
+          // Module
+          bool Invalid = false;
+          Entry = SM.getLoadedSLocEntry(static_cast<unsigned>(-Value - 2),
+                                        &Invalid);
+          if (Invalid)
+            return true;
+        }
+
+        if (FilterSystem &&
+            SrcMgr::isSystem(Entry.getFile().getFileCharacteristic()))
+          return Item->second = false;
+        if (HeaderFilter)
+          return Item->second = HeaderFilter->match(Entry.getFile().getName());
+        return true;
+      }
+
+      // Gather all top level decls
+      bool HandleTopLevelDecl(DeclGroupRef DG) override {
+        for (auto *D : DG) {
+          auto &SM = D->getASTContext().getSourceManager();
+          if (!shouldKeepDeclsFromFile(
+                  SM, SM.getDecomposedExpansionLoc(D->getLocation()).first))
+            continue;
+          Decls.push_back(D);
+        }
+        return true;
+      }
+
+      // Set the contexts traversal scope to only include top-level decls.
+      void HandleTranslationUnit(ASTContext &Ctx) override {
+        Ctx.setTraversalScope(Decls);
+      }
+
+      std::optional<llvm::Regex> HeaderFilter;
+      llvm::DenseMap<clang::FileID, bool> Cache;
+      bool FilterSystem;
+      std::vector<Decl *> Decls;
+
+    public:
+      TraversalScopeConsumer(bool FilterSystem,
+                             const std::optional<std::string> &HeaderFilter)
+          : FilterSystem(FilterSystem) {
+        if (HeaderFilter && *HeaderFilter != ".*")
+          this->HeaderFilter.emplace(*HeaderFilter);
+      }
+    };
+
+    Consumers.push_back(std::make_unique<TraversalScopeConsumer>(
+        !Context.getOptions().SystemHeaders.value_or(false),
+        Context.getOptions().HeaderFilterRegex));
+  }
   if (!Checks.empty())
     Consumers.push_back(Finder->newASTConsumer());
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D150126: [clang-tidy]... Nathan James via Phabricator via cfe-commits

Reply via email to