diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp --- a/clang-tools-extra/clang-tidy/ClangTidy.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp @@ -431,6 +431,87 @@ } std::vector> 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) { + auto [Item, Inserted] = Cache.try_emplace(FID, true); + + // Already in cache, return cached value + if (!Inserted) + return Item->getSecond(); + + // Tricky cases, just asume we should include + if (FID.isInvalid() || FID == FileID::getSentinel()) + return true; + + // 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(FID.getHashValue()); + SrcMgr::SLocEntry Entry; + if (Value > 0) { + // Regular file + Entry = SM.getLocalSLocEntry(static_cast(Value)); + } else { + // Module + bool Invalid = false; + Entry = SM.getLoadedSLocEntry(static_cast(-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 HeaderFilter; + llvm::DenseMap Cache; + bool FilterSystem; + std::vector Decls; + + public: + TraversalScopeConsumer(bool FilterSystem, + const std::optional &HeaderFilter) + : FilterSystem(FilterSystem) { + if (HeaderFilter && *HeaderFilter != ".*") + this->HeaderFilter.emplace(*HeaderFilter); + } + }; + + Consumers.push_back(std::make_unique( + !Context.getOptions().SystemHeaders.value_or(false), + Context.getOptions().HeaderFilterRegex)); + } if (!Checks.empty()) Consumers.push_back(Finder->newASTConsumer());