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) { + // 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(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()); diff --git a/clang-tools-extra/clang-tidy/abseil/NoInternalDependenciesCheck.cpp b/clang-tools-extra/clang-tidy/abseil/NoInternalDependenciesCheck.cpp --- a/clang-tools-extra/clang-tidy/abseil/NoInternalDependenciesCheck.cpp +++ b/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) { diff --git a/clang-tools-extra/clang-tidy/abseil/UpgradeDurationConversionsCheck.cpp b/clang-tools-extra/clang-tidy/abseil/UpgradeDurationConversionsCheck.cpp --- a/clang-tools-extra/clang-tidy/abseil/UpgradeDurationConversionsCheck.cpp +++ b/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); } diff --git a/clang-tools-extra/clang-tidy/cert/DontModifyStdNamespaceCheck.cpp b/clang-tools-extra/clang-tidy/cert/DontModifyStdNamespaceCheck.cpp --- a/clang-tools-extra/clang-tidy/cert/DontModifyStdNamespaceCheck.cpp +++ b/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(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(