diff --git a/clang-tools-extra/clang-tidy/ClangTidyCheck.h b/clang-tools-extra/clang-tidy/ClangTidyCheck.h --- a/clang-tools-extra/clang-tidy/ClangTidyCheck.h +++ b/clang-tools-extra/clang-tidy/ClangTidyCheck.h @@ -422,6 +422,12 @@ bool areDiagsSelfContained() const { return Context->areDiagsSelfContained(); } + const FileExtensionsSet &getHeaderFileExtensions() const { + return Context->getHeaderFileExtensions(); + } + const FileExtensionsSet &getImplementationFileExtensions() const { + return Context->getImplementationFileExtensions(); + } }; /// Read a named option from the ``Context`` and parse it as a bool. diff --git a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h --- a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h +++ b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h @@ -11,6 +11,7 @@ #include "ClangTidyOptions.h" #include "ClangTidyProfiling.h" +#include "FileExtensionsSet.h" #include "NoLintDirectiveHandler.h" #include "clang/Basic/Diagnostic.h" #include "clang/Tooling/Core/Diagnostic.h" @@ -160,6 +161,14 @@ /// \c CurrentFile. ClangTidyOptions getOptionsForFile(StringRef File) const; + const FileExtensionsSet &getHeaderFileExtensions() const { + return HeaderFileExtensions; + } + + const FileExtensionsSet &getImplementationFileExtensions() const { + return ImplementationFileExtensions; + } + /// Returns \c ClangTidyStats containing issued and ignored diagnostic /// counters. const ClangTidyStats &getStats() const { return Stats; } @@ -221,6 +230,9 @@ std::unique_ptr CheckFilter; std::unique_ptr WarningAsErrorFilter; + FileExtensionsSet HeaderFileExtensions; + FileExtensionsSet ImplementationFileExtensions; + LangOptions LangOpts; ClangTidyStats Stats; diff --git a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp --- a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp @@ -22,6 +22,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/ASTDiagnostic.h" #include "clang/AST/Attr.h" +#include "clang/Basic/CharInfo.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/FileManager.h" @@ -222,12 +223,35 @@ DiagEngine->setSourceManager(SourceMgr); } +static bool +parseFileExtensions(const std::vector &AllFileExtensions, + FileExtensionsSet &FileExtensions) { + FileExtensions.clear(); + for (StringRef Suffix : AllFileExtensions) { + StringRef Extension = Suffix.trim(); + if (!llvm::all_of(Extension, isAlphanumeric)) + return false; + FileExtensions.insert(Extension); + } + return true; +} + void ClangTidyContext::setCurrentFile(StringRef File) { CurrentFile = std::string(File); CurrentOptions = getOptionsForFile(CurrentFile); CheckFilter = std::make_unique(*getOptions().Checks); WarningAsErrorFilter = std::make_unique(*getOptions().WarningsAsErrors); + + if (!parseFileExtensions(*getOptions().HeaderFileExtensions, + HeaderFileExtensions)) { + this->configurationDiag("Invalid header file extensions"); + } + + if (!parseFileExtensions(*getOptions().ImplementationFileExtensions, + ImplementationFileExtensions)) { + this->configurationDiag("Invalid implementation file extensions"); + } } void ClangTidyContext::setASTContext(ASTContext *Context) { diff --git a/clang-tools-extra/clang-tidy/ClangTidyOptions.h b/clang-tools-extra/clang-tidy/ClangTidyOptions.h --- a/clang-tools-extra/clang-tidy/ClangTidyOptions.h +++ b/clang-tools-extra/clang-tidy/ClangTidyOptions.h @@ -71,6 +71,14 @@ /// WarningsAsErrors filter. std::optional WarningsAsErrors; + /// File extensions to consider to determine if a given diagnostic is located + /// in a header file. + std::optional> HeaderFileExtensions; + + /// File extensions to consider to determine if a given diagnostic is located + /// is located in an implementation file. + std::optional> ImplementationFileExtensions; + /// Output warnings from headers matching this filter. Warnings from /// main files will always be displayed. std::optional HeaderFilterRegex; diff --git a/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp b/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp --- a/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp @@ -122,6 +122,9 @@ bool Ignored = false; IO.mapOptional("Checks", Options.Checks); IO.mapOptional("WarningsAsErrors", Options.WarningsAsErrors); + IO.mapOptional("HeaderFileExtensions", Options.HeaderFileExtensions); + IO.mapOptional("ImplementationFileExtensions", + Options.ImplementationFileExtensions); IO.mapOptional("HeaderFilterRegex", Options.HeaderFilterRegex); IO.mapOptional("AnalyzeTemporaryDtors", Ignored); // deprecated IO.mapOptional("FormatStyle", Options.FormatStyle); @@ -142,6 +145,8 @@ ClangTidyOptions Options; Options.Checks = ""; Options.WarningsAsErrors = ""; + Options.HeaderFileExtensions = {"", "h", "hh", "hpp", "hxx"}; + Options.ImplementationFileExtensions = {"c", "cc", "cpp", "cxx"}; Options.HeaderFilterRegex = ""; Options.SystemHeaders = false; Options.FormatStyle = "none"; @@ -178,6 +183,9 @@ unsigned Order) { mergeCommaSeparatedLists(Checks, Other.Checks); mergeCommaSeparatedLists(WarningsAsErrors, Other.WarningsAsErrors); + overrideValue(HeaderFileExtensions, Other.HeaderFileExtensions); + overrideValue(ImplementationFileExtensions, + Other.ImplementationFileExtensions); overrideValue(HeaderFilterRegex, Other.HeaderFilterRegex); overrideValue(SystemHeaders, Other.SystemHeaders); overrideValue(FormatStyle, Other.FormatStyle); diff --git a/clang-tools-extra/clang-tidy/FileExtensionsSet.h b/clang-tools-extra/clang-tidy/FileExtensionsSet.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/FileExtensionsSet.h @@ -0,0 +1,19 @@ +//===--- FileExtensionsSet.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_FILE_EXTENSIONS_SET_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_FILE_EXTENSIONS_SET_H + +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/StringRef.h" + +namespace clang::tidy { +typedef llvm::SmallSet FileExtensionsSet; +} // namespace clang::tidy + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_FILE_EXTENSIONS_SET_H diff --git a/clang-tools-extra/clang-tidy/bugprone/DynamicStaticInitializersCheck.h b/clang-tools-extra/clang-tidy/bugprone/DynamicStaticInitializersCheck.h --- a/clang-tools-extra/clang-tidy/bugprone/DynamicStaticInitializersCheck.h +++ b/clang-tools-extra/clang-tidy/bugprone/DynamicStaticInitializersCheck.h @@ -10,7 +10,7 @@ #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_DYNAMIC_STATIC_INITIALIZERS_CHECK_H #include "../ClangTidyCheck.h" -#include "../utils/FileExtensionsUtils.h" +#include "../FileExtensionsSet.h" namespace clang::tidy::bugprone { @@ -34,8 +34,8 @@ void check(const ast_matchers::MatchFinder::MatchResult &Result) override; private: - const StringRef RawStringHeaderFileExtensions; - utils::FileExtensionsSet HeaderFileExtensions; + StringRef RawStringHeaderFileExtensions; + FileExtensionsSet HeaderFileExtensions; }; } // namespace clang::tidy::bugprone diff --git a/clang-tools-extra/clang-tidy/bugprone/DynamicStaticInitializersCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/DynamicStaticInitializersCheck.cpp --- a/clang-tools-extra/clang-tidy/bugprone/DynamicStaticInitializersCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/DynamicStaticInitializersCheck.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "DynamicStaticInitializersCheck.h" +#include "../utils/FileExtensionsUtils.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" @@ -24,17 +25,22 @@ return false; } -DynamicStaticInitializersCheck::DynamicStaticInitializersCheck(StringRef Name, - ClangTidyContext *Context) - : ClangTidyCheck(Name, Context), - RawStringHeaderFileExtensions(Options.getLocalOrGlobal( - "HeaderFileExtensions", utils::defaultHeaderFileExtensions())) { - if (!utils::parseFileExtensions(RawStringHeaderFileExtensions, - HeaderFileExtensions, - utils::defaultFileExtensionDelimiters())) { - this->configurationDiag("Invalid header file extension: '%0'") - << RawStringHeaderFileExtensions; - } +DynamicStaticInitializersCheck::DynamicStaticInitializersCheck( + StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) { + std::optional HeaderFileExtensionsOption = + Options.get("HeaderFileExtensions"); + RawStringHeaderFileExtensions = + HeaderFileExtensionsOption.value_or(utils::defaultHeaderFileExtensions()); + if (HeaderFileExtensionsOption) { + if (!utils::parseFileExtensions(RawStringHeaderFileExtensions, + HeaderFileExtensions, + utils::defaultFileExtensionDelimiters())) { + this->configurationDiag("Invalid header file extension: '%0'") + << RawStringHeaderFileExtensions; + } + } else + HeaderFileExtensions = getHeaderFileExtensions(); } void DynamicStaticInitializersCheck::storeOptions( diff --git a/clang-tools-extra/clang-tidy/bugprone/SuspiciousIncludeCheck.h b/clang-tools-extra/clang-tidy/bugprone/SuspiciousIncludeCheck.h --- a/clang-tools-extra/clang-tidy/bugprone/SuspiciousIncludeCheck.h +++ b/clang-tools-extra/clang-tidy/bugprone/SuspiciousIncludeCheck.h @@ -40,12 +40,12 @@ Preprocessor *ModuleExpanderPP) override; void storeOptions(ClangTidyOptions::OptionMap &Opts) override; - utils::FileExtensionsSet HeaderFileExtensions; - utils::FileExtensionsSet ImplementationFileExtensions; + FileExtensionsSet HeaderFileExtensions; + FileExtensionsSet ImplementationFileExtensions; private: - const StringRef RawStringHeaderFileExtensions; - const StringRef RawStringImplementationFileExtensions; + StringRef RawStringHeaderFileExtensions; + StringRef RawStringImplementationFileExtensions; }; } // namespace clang::tidy::bugprone diff --git a/clang-tools-extra/clang-tidy/bugprone/SuspiciousIncludeCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/SuspiciousIncludeCheck.cpp --- a/clang-tools-extra/clang-tidy/bugprone/SuspiciousIncludeCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/SuspiciousIncludeCheck.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "SuspiciousIncludeCheck.h" +#include "../utils/FileExtensionsUtils.h" #include "clang/AST/ASTContext.h" #include "clang/Lex/Preprocessor.h" #include @@ -36,25 +37,35 @@ SuspiciousIncludeCheck::SuspiciousIncludeCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context), - RawStringHeaderFileExtensions(Options.getLocalOrGlobal( - "HeaderFileExtensions", utils::defaultHeaderFileExtensions())), - RawStringImplementationFileExtensions(Options.getLocalOrGlobal( - "ImplementationFileExtensions", - utils::defaultImplementationFileExtensions())) { - if (!utils::parseFileExtensions(RawStringImplementationFileExtensions, - ImplementationFileExtensions, - utils::defaultFileExtensionDelimiters())) { - this->configurationDiag("Invalid implementation file extension: '%0'") - << RawStringImplementationFileExtensions; - } - - if (!utils::parseFileExtensions(RawStringHeaderFileExtensions, - HeaderFileExtensions, - utils::defaultFileExtensionDelimiters())) { - this->configurationDiag("Invalid header file extension: '%0'") - << RawStringHeaderFileExtensions; - } + : ClangTidyCheck(Name, Context) { + std::optional ImplementationFileExtensionsOption = + Options.get("ImplementationFileExtensions"); + RawStringImplementationFileExtensions = + ImplementationFileExtensionsOption.value_or( + utils::defaultImplementationFileExtensions()); + if (ImplementationFileExtensionsOption) { + if (!utils::parseFileExtensions(RawStringImplementationFileExtensions, + ImplementationFileExtensions, + utils::defaultFileExtensionDelimiters())) { + this->configurationDiag("Invalid implementation file extension: '%0'") + << RawStringImplementationFileExtensions; + } + } else + ImplementationFileExtensions = getImplementationFileExtensions(); + + std::optional HeaderFileExtensionsOption = + Options.get("HeaderFileExtensions"); + RawStringHeaderFileExtensions = + HeaderFileExtensionsOption.value_or(utils::defaultHeaderFileExtensions()); + if (HeaderFileExtensionsOption) { + if (!utils::parseFileExtensions(RawStringHeaderFileExtensions, + HeaderFileExtensions, + utils::defaultFileExtensionDelimiters())) { + this->configurationDiag("Invalid header file extension: '%0'") + << RawStringHeaderFileExtensions; + } + } else + HeaderFileExtensions = getHeaderFileExtensions(); } void SuspiciousIncludeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { diff --git a/clang-tools-extra/clang-tidy/google/GlobalNamesInHeadersCheck.h b/clang-tools-extra/clang-tidy/google/GlobalNamesInHeadersCheck.h --- a/clang-tools-extra/clang-tidy/google/GlobalNamesInHeadersCheck.h +++ b/clang-tools-extra/clang-tidy/google/GlobalNamesInHeadersCheck.h @@ -35,8 +35,8 @@ void check(const ast_matchers::MatchFinder::MatchResult &Result) override; private: - const StringRef RawStringHeaderFileExtensions; - utils::FileExtensionsSet HeaderFileExtensions; + StringRef RawStringHeaderFileExtensions; + FileExtensionsSet HeaderFileExtensions; }; } // namespace clang::tidy::google::readability diff --git a/clang-tools-extra/clang-tidy/google/GlobalNamesInHeadersCheck.cpp b/clang-tools-extra/clang-tidy/google/GlobalNamesInHeadersCheck.cpp --- a/clang-tools-extra/clang-tidy/google/GlobalNamesInHeadersCheck.cpp +++ b/clang-tools-extra/clang-tidy/google/GlobalNamesInHeadersCheck.cpp @@ -18,15 +18,20 @@ GlobalNamesInHeadersCheck::GlobalNamesInHeadersCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context), - RawStringHeaderFileExtensions(Options.getLocalOrGlobal( - "HeaderFileExtensions", utils::defaultHeaderFileExtensions())) { - if (!utils::parseFileExtensions(RawStringHeaderFileExtensions, - HeaderFileExtensions, - utils::defaultFileExtensionDelimiters())) { - this->configurationDiag("Invalid header file extension: '%0'") - << RawStringHeaderFileExtensions; - } + : ClangTidyCheck(Name, Context) { + std::optional HeaderFileExtensionsOption = + Options.get("HeaderFileExtensions"); + RawStringHeaderFileExtensions = + HeaderFileExtensionsOption.value_or(utils::defaultHeaderFileExtensions()); + if (HeaderFileExtensionsOption) { + if (!utils::parseFileExtensions(RawStringHeaderFileExtensions, + HeaderFileExtensions, + utils::defaultFileExtensionDelimiters())) { + this->configurationDiag("Invalid header file extension: '%0'") + << RawStringHeaderFileExtensions; + } + } else + HeaderFileExtensions = getHeaderFileExtensions(); } void GlobalNamesInHeadersCheck::storeOptions( diff --git a/clang-tools-extra/clang-tidy/google/UnnamedNamespaceInHeaderCheck.h b/clang-tools-extra/clang-tidy/google/UnnamedNamespaceInHeaderCheck.h --- a/clang-tools-extra/clang-tidy/google/UnnamedNamespaceInHeaderCheck.h +++ b/clang-tools-extra/clang-tidy/google/UnnamedNamespaceInHeaderCheck.h @@ -41,8 +41,8 @@ void check(const ast_matchers::MatchFinder::MatchResult &Result) override; private: - const StringRef RawStringHeaderFileExtensions; - utils::FileExtensionsSet HeaderFileExtensions; + StringRef RawStringHeaderFileExtensions; + FileExtensionsSet HeaderFileExtensions; }; } // namespace clang::tidy::google::build diff --git a/clang-tools-extra/clang-tidy/google/UnnamedNamespaceInHeaderCheck.cpp b/clang-tools-extra/clang-tidy/google/UnnamedNamespaceInHeaderCheck.cpp --- a/clang-tools-extra/clang-tidy/google/UnnamedNamespaceInHeaderCheck.cpp +++ b/clang-tools-extra/clang-tidy/google/UnnamedNamespaceInHeaderCheck.cpp @@ -17,15 +17,20 @@ UnnamedNamespaceInHeaderCheck::UnnamedNamespaceInHeaderCheck( StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context), - RawStringHeaderFileExtensions(Options.getLocalOrGlobal( - "HeaderFileExtensions", utils::defaultHeaderFileExtensions())) { - if (!utils::parseFileExtensions(RawStringHeaderFileExtensions, - HeaderFileExtensions, - utils::defaultFileExtensionDelimiters())) { - this->configurationDiag("Invalid header file extension: '%0'") - << RawStringHeaderFileExtensions; - } + : ClangTidyCheck(Name, Context) { + std::optional HeaderFileExtensionsOption = + Options.get("HeaderFileExtensions"); + RawStringHeaderFileExtensions = + HeaderFileExtensionsOption.value_or(utils::defaultHeaderFileExtensions()); + if (HeaderFileExtensionsOption) { + if (!utils::parseFileExtensions(RawStringHeaderFileExtensions, + HeaderFileExtensions, + utils::defaultFileExtensionDelimiters())) { + this->configurationDiag("Invalid header file extension: '%0'") + << RawStringHeaderFileExtensions; + } + } else + HeaderFileExtensions = getHeaderFileExtensions(); } void UnnamedNamespaceInHeaderCheck::storeOptions( diff --git a/clang-tools-extra/clang-tidy/misc/DefinitionsInHeadersCheck.h b/clang-tools-extra/clang-tidy/misc/DefinitionsInHeadersCheck.h --- a/clang-tools-extra/clang-tidy/misc/DefinitionsInHeadersCheck.h +++ b/clang-tools-extra/clang-tidy/misc/DefinitionsInHeadersCheck.h @@ -41,8 +41,8 @@ private: const bool UseHeaderFileExtension; - const StringRef RawStringHeaderFileExtensions; - utils::FileExtensionsSet HeaderFileExtensions; + StringRef RawStringHeaderFileExtensions; + FileExtensionsSet HeaderFileExtensions; }; } // namespace clang::tidy::misc diff --git a/clang-tools-extra/clang-tidy/misc/DefinitionsInHeadersCheck.cpp b/clang-tools-extra/clang-tidy/misc/DefinitionsInHeadersCheck.cpp --- a/clang-tools-extra/clang-tidy/misc/DefinitionsInHeadersCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/DefinitionsInHeadersCheck.cpp @@ -16,7 +16,7 @@ namespace { -AST_MATCHER_P(NamedDecl, usesHeaderFileExtension, utils::FileExtensionsSet, +AST_MATCHER_P(NamedDecl, usesHeaderFileExtension, FileExtensionsSet, HeaderFileExtensions) { return utils::isExpansionLocInHeaderFile( Node.getBeginLoc(), Finder->getASTContext().getSourceManager(), @@ -28,15 +28,20 @@ DefinitionsInHeadersCheck::DefinitionsInHeadersCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), - UseHeaderFileExtension(Options.get("UseHeaderFileExtension", true)), - RawStringHeaderFileExtensions(Options.getLocalOrGlobal( - "HeaderFileExtensions", utils::defaultHeaderFileExtensions())) { - if (!utils::parseFileExtensions(RawStringHeaderFileExtensions, - HeaderFileExtensions, - utils::defaultFileExtensionDelimiters())) { - this->configurationDiag("Invalid header file extension: '%0'") - << RawStringHeaderFileExtensions; - } + UseHeaderFileExtension(Options.get("UseHeaderFileExtension", true)) { + std::optional HeaderFileExtensionsOption = + Options.get("HeaderFileExtensions"); + RawStringHeaderFileExtensions = + HeaderFileExtensionsOption.value_or(utils::defaultHeaderFileExtensions()); + if (HeaderFileExtensionsOption) { + if (!utils::parseFileExtensions(RawStringHeaderFileExtensions, + HeaderFileExtensions, + utils::defaultFileExtensionDelimiters())) { + this->configurationDiag("Invalid header file extension: '%0'") + << RawStringHeaderFileExtensions; + } + } else + HeaderFileExtensions = getHeaderFileExtensions(); } void DefinitionsInHeadersCheck::storeOptions( diff --git a/clang-tools-extra/clang-tidy/misc/UnusedUsingDeclsCheck.h b/clang-tools-extra/clang-tidy/misc/UnusedUsingDeclsCheck.h --- a/clang-tools-extra/clang-tidy/misc/UnusedUsingDeclsCheck.h +++ b/clang-tools-extra/clang-tidy/misc/UnusedUsingDeclsCheck.h @@ -47,8 +47,8 @@ std::vector Contexts; - const StringRef RawStringHeaderFileExtensions; - utils::FileExtensionsSet HeaderFileExtensions; + StringRef RawStringHeaderFileExtensions; + FileExtensionsSet HeaderFileExtensions; }; } // namespace clang::tidy::misc diff --git a/clang-tools-extra/clang-tidy/misc/UnusedUsingDeclsCheck.cpp b/clang-tools-extra/clang-tidy/misc/UnusedUsingDeclsCheck.cpp --- a/clang-tools-extra/clang-tidy/misc/UnusedUsingDeclsCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/UnusedUsingDeclsCheck.cpp @@ -38,14 +38,20 @@ UnusedUsingDeclsCheck::UnusedUsingDeclsCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context), - RawStringHeaderFileExtensions(Options.getLocalOrGlobal( - "HeaderFileExtensions", utils::defaultHeaderFileExtensions())) { - if (!utils::parseFileExtensions(RawStringHeaderFileExtensions, - HeaderFileExtensions, - utils::defaultFileExtensionDelimiters())) - this->configurationDiag("Invalid header file extension: '%0'") - << RawStringHeaderFileExtensions; + : ClangTidyCheck(Name, Context) { + std::optional HeaderFileExtensionsOption = + Options.get("HeaderFileExtensions"); + RawStringHeaderFileExtensions = + HeaderFileExtensionsOption.value_or(utils::defaultHeaderFileExtensions()); + if (HeaderFileExtensionsOption) { + if (!utils::parseFileExtensions(RawStringHeaderFileExtensions, + HeaderFileExtensions, + utils::defaultFileExtensionDelimiters())) { + this->configurationDiag("Invalid header file extension: '%0'") + << RawStringHeaderFileExtensions; + } + } else + HeaderFileExtensions = getHeaderFileExtensions(); } void UnusedUsingDeclsCheck::registerMatchers(MatchFinder *Finder) { diff --git a/clang-tools-extra/clang-tidy/misc/UseAnonymousNamespaceCheck.h b/clang-tools-extra/clang-tidy/misc/UseAnonymousNamespaceCheck.h --- a/clang-tools-extra/clang-tidy/misc/UseAnonymousNamespaceCheck.h +++ b/clang-tools-extra/clang-tidy/misc/UseAnonymousNamespaceCheck.h @@ -38,8 +38,8 @@ void check(const ast_matchers::MatchFinder::MatchResult &Result) override; private: - const StringRef RawStringHeaderFileExtensions; - utils::FileExtensionsSet HeaderFileExtensions; + StringRef RawStringHeaderFileExtensions; + FileExtensionsSet HeaderFileExtensions; }; } // namespace clang::tidy::misc diff --git a/clang-tools-extra/clang-tidy/misc/UseAnonymousNamespaceCheck.cpp b/clang-tools-extra/clang-tidy/misc/UseAnonymousNamespaceCheck.cpp --- a/clang-tools-extra/clang-tidy/misc/UseAnonymousNamespaceCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/UseAnonymousNamespaceCheck.cpp @@ -17,7 +17,7 @@ AST_POLYMORPHIC_MATCHER_P(isInHeaderFile, AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl, VarDecl), - utils::FileExtensionsSet, HeaderFileExtensions) { + FileExtensionsSet, HeaderFileExtensions) { return utils::isExpansionLocInHeaderFile( Node.getBeginLoc(), Finder->getASTContext().getSourceManager(), HeaderFileExtensions); @@ -31,15 +31,20 @@ UseAnonymousNamespaceCheck::UseAnonymousNamespaceCheck( StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context), - RawStringHeaderFileExtensions(Options.getLocalOrGlobal( - "HeaderFileExtensions", utils::defaultHeaderFileExtensions())) { - if (!utils::parseFileExtensions(RawStringHeaderFileExtensions, - HeaderFileExtensions, - utils::defaultFileExtensionDelimiters())) { - this->configurationDiag("Invalid header file extension: '%0'") - << RawStringHeaderFileExtensions; - } + : ClangTidyCheck(Name, Context) { + std::optional HeaderFileExtensionsOption = + Options.get("HeaderFileExtensions"); + RawStringHeaderFileExtensions = + HeaderFileExtensionsOption.value_or(utils::defaultHeaderFileExtensions()); + if (HeaderFileExtensionsOption) { + if (!utils::parseFileExtensions(RawStringHeaderFileExtensions, + HeaderFileExtensions, + utils::defaultFileExtensionDelimiters())) { + this->configurationDiag("Invalid header file extension: '%0'") + << RawStringHeaderFileExtensions; + } + } else + HeaderFileExtensions = getHeaderFileExtensions(); } void UseAnonymousNamespaceCheck::storeOptions( diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp --- a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp +++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp @@ -46,12 +46,14 @@ $ clang-tidy -dump-config --- - Checks: '-*,some-check' - WarningsAsErrors: '' - HeaderFilterRegex: '' - FormatStyle: none - InheritParentConfig: true - User: user + Checks: '-*,some-check' + WarningsAsErrors: '' + HeaderFileExtensions: ['', 'h','hh','hpp','hxx'] + ImplementationFileExtensions: ['c','cc','cpp','cxx'] + HeaderFilterRegex: '' + FormatStyle: none + InheritParentConfig: true + User: user CheckOptions: some-check.SomeOption: 'some value' ... @@ -130,10 +132,10 @@ cl::init(false), cl::cat(ClangTidyCategory)); static cl::opt FixNotes("fix-notes", cl::desc(R"( -If a warning has no fix, but a single fix can -be found through an associated diagnostic note, -apply the fix. -Specifying this flag will implicitly enable the +If a warning has no fix, but a single fix can +be found through an associated diagnostic note, +apply the fix. +Specifying this flag will implicitly enable the '--fix' flag. )"), cl::init(false), cl::cat(ClangTidyCategory)); @@ -458,6 +460,26 @@ return AnyInvalid; } +static bool verifyFileExtensions( + const std::vector &HeaderFileExtensions, + const std::vector &ImplementationFileExtensions, + StringRef Source) { + bool AnyInvalid = false; + for (const auto &HeaderExtension : HeaderFileExtensions) { + for (const auto &ImplementationExtension : ImplementationFileExtensions) { + if (HeaderExtension == ImplementationExtension) { + AnyInvalid = true; + auto &Output = llvm::WithColor::warning(llvm::errs(), Source) + << "HeaderFileExtension '" << HeaderExtension << '\'' + << " is the same as ImplementationFileExtension '" + << ImplementationExtension << '\''; + Output << VerifyConfigWarningEnd; + } + } + } + return AnyInvalid; +} + int clangTidyMain(int argc, const char **argv) { llvm::InitLLVM X(argc, argv); @@ -561,6 +583,11 @@ if (Opts.Checks) AnyInvalid |= verifyChecks(Valid.Names, *Opts.Checks, Source); + if (Opts.HeaderFileExtensions && Opts.ImplementationFileExtensions) + AnyInvalid |= + verifyFileExtensions(*Opts.HeaderFileExtensions, + *Opts.ImplementationFileExtensions, Source); + for (auto Key : Opts.CheckOptions.keys()) { if (Valid.Options.contains(Key)) continue; diff --git a/clang-tools-extra/clang-tidy/utils/FileExtensionsUtils.h b/clang-tools-extra/clang-tidy/utils/FileExtensionsUtils.h --- a/clang-tools-extra/clang-tidy/utils/FileExtensionsUtils.h +++ b/clang-tools-extra/clang-tidy/utils/FileExtensionsUtils.h @@ -9,6 +9,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_FILE_EXTENSIONS_UTILS_H #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_FILE_EXTENSIONS_UTILS_H +#include "../FileExtensionsSet.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "llvm/ADT/SmallSet.h" @@ -17,8 +18,6 @@ namespace clang::tidy::utils { -typedef llvm::SmallSet FileExtensionsSet; - /// Checks whether expansion location of \p Loc is in header file. bool isExpansionLocInHeaderFile(SourceLocation Loc, const SourceManager &SM, const FileExtensionsSet &HeaderFileExtensions); diff --git a/clang-tools-extra/clang-tidy/utils/HeaderGuard.h b/clang-tools-extra/clang-tidy/utils/HeaderGuard.h --- a/clang-tools-extra/clang-tidy/utils/HeaderGuard.h +++ b/clang-tools-extra/clang-tidy/utils/HeaderGuard.h @@ -25,12 +25,20 @@ class HeaderGuardCheck : public ClangTidyCheck { public: HeaderGuardCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context), - RawStringHeaderFileExtensions(Options.getLocalOrGlobal( - "HeaderFileExtensions", utils::defaultHeaderFileExtensions())) { - utils::parseFileExtensions(RawStringHeaderFileExtensions, - HeaderFileExtensions, - utils::defaultFileExtensionDelimiters()); + : ClangTidyCheck(Name, Context) { + std::optional HeaderFileExtensionsOption = + Options.get("HeaderFileExtensions"); + RawStringHeaderFileExtensions = HeaderFileExtensionsOption.value_or( + utils::defaultHeaderFileExtensions()); + if (HeaderFileExtensionsOption) { + if (!utils::parseFileExtensions( + RawStringHeaderFileExtensions, HeaderFileExtensions, + utils::defaultFileExtensionDelimiters())) { + this->configurationDiag("Invalid header file extension: '%0'") + << RawStringHeaderFileExtensions; + } + } else + HeaderFileExtensions = getHeaderFileExtensions(); } void storeOptions(ClangTidyOptions::OptionMap &Opts) override; void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, @@ -58,7 +66,7 @@ private: std::string RawStringHeaderFileExtensions; - utils::FileExtensionsSet HeaderFileExtensions; + FileExtensionsSet HeaderFileExtensions; }; } // namespace clang::tidy::utils diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -94,6 +94,10 @@ Improvements to clang-tidy -------------------------- +- New global configuration file options `HeaderFileExtensions` and + `ImplementationFileExtensions`, replacing the check-local options of the + same name. + New checks ^^^^^^^^^^ @@ -124,6 +128,41 @@ Changes in existing checks ^^^^^^^^^^^^^^^^^^^^^^^^^^ +- Deprecated check-local options `HeaderFileExtensions` and `ImplementationFileExtensions` + in :doc:`bugprone-dynamic-static-initializers + ` check. + Global options of the same name should be used instead. + +- Deprecated check-local options `HeaderFileExtensions` and `ImplementationFileExtensions` + in :doc:`bugprone-suspicious-include + ` check. + Global options of the same name should be used instead. + +- Deprecated check-local options `HeaderFileExtensions` and `ImplementationFileExtensions` + in :doc:`google-build-namespaces + ` check. + Global options of the same name should be used instead. + +- Deprecated check-local options `HeaderFileExtensions` and `ImplementationFileExtensions` + in :doc:`google-global-names-in-headers + ` check. + Global options of the same name should be used instead. + +- Deprecated check-local options `HeaderFileExtensions` and `ImplementationFileExtensions` + in :doc:`llvm-header-guard + ` check. + Global options of the same name should be used instead. + +- Deprecated check-local options `HeaderFileExtensions` and `ImplementationFileExtensions` + in :doc:`misc-definitions-in-headers + ` check. + Global options of the same name should be used instead. + +- Deprecated check-local options `HeaderFileExtensions` and `ImplementationFileExtensions` + in :doc:`misc-unused-using-decls + ` check. + Global options of the same name should be used instead. + Removed checks ^^^^^^^^^^^^^^ diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/suspicious-include.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/suspicious-include.rst --- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/suspicious-include.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/suspicious-include.rst @@ -19,6 +19,10 @@ ------- .. option:: HeaderFileExtensions + Note: this option is deprecated, it will be removed in :program:`clang-tidy` + version 19. Please use the global configuration option + `HeaderFileExtensions`. + Default value: ``";h;hh;hpp;hxx"`` A semicolon-separated list of filename extensions of header files (the filename extensions should not contain a "." prefix). For extension-less @@ -27,6 +31,10 @@ .. option:: ImplementationFileExtensions + Note: this option is deprecated, it will be removed in :program:`clang-tidy` + version 19. Please use the global configuration option + `ImplementationFileExtensions`. + Default value: ``"c;cc;cpp;cxx"`` Likewise, a semicolon-separated list of filename extensions of implementation files. diff --git a/clang-tools-extra/docs/clang-tidy/checks/google/build-namespaces.rst b/clang-tools-extra/docs/clang-tidy/checks/google/build-namespaces.rst --- a/clang-tools-extra/docs/clang-tidy/checks/google/build-namespaces.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/google/build-namespaces.rst @@ -17,6 +17,10 @@ .. option:: HeaderFileExtensions + Note: this option is deprecated, it will be removed in :program:`clang-tidy` + version 19. Please use the global configuration option + `HeaderFileExtensions`. + A comma-separated list of filename extensions of header files (the filename extensions should not include "." prefix). Default is "h,hh,hpp,hxx". For header files without an extension, use an empty string (if there are no diff --git a/clang-tools-extra/docs/clang-tidy/checks/google/global-names-in-headers.rst b/clang-tools-extra/docs/clang-tidy/checks/google/global-names-in-headers.rst --- a/clang-tools-extra/docs/clang-tidy/checks/google/global-names-in-headers.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/google/global-names-in-headers.rst @@ -14,6 +14,10 @@ .. option:: HeaderFileExtensions + Note: this option is deprecated, it will be removed in :program:`clang-tidy` + version 19. Please use the global configuration option + `HeaderFileExtensions`. + A comma-separated list of filename extensions of header files (the filename extensions should not contain "." prefix). Default is "h". For header files without an extension, use an empty string (if there are no diff --git a/clang-tools-extra/docs/clang-tidy/checks/llvm/header-guard.rst b/clang-tools-extra/docs/clang-tidy/checks/llvm/header-guard.rst --- a/clang-tools-extra/docs/clang-tidy/checks/llvm/header-guard.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/llvm/header-guard.rst @@ -10,6 +10,10 @@ .. option:: HeaderFileExtensions + Note: this option is deprecated, it will be removed in :program:`clang-tidy` + version 19. Please use the global configuration option + `HeaderFileExtensions`. + A comma-separated list of filename extensions of header files (the filename extensions should not include "." prefix). Default is "h,hh,hpp,hxx". For header files without an extension, use an empty string (if there are no diff --git a/clang-tools-extra/docs/clang-tidy/checks/misc/definitions-in-headers.rst b/clang-tools-extra/docs/clang-tidy/checks/misc/definitions-in-headers.rst --- a/clang-tools-extra/docs/clang-tidy/checks/misc/definitions-in-headers.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/misc/definitions-in-headers.rst @@ -92,6 +92,10 @@ .. option:: HeaderFileExtensions + Note: this option is deprecated, it will be removed in :program:`clang-tidy` + version 19. Please use the global configuration option + `HeaderFileExtensions`. + A comma-separated list of filename extensions of header files (the filename extensions should not include "." prefix). Default is "h,hh,hpp,hxx". For header files without an extension, use an empty string (if there are no @@ -100,5 +104,9 @@ .. option:: UseHeaderFileExtension + Note: this option is deprecated, it will be removed in :program:`clang-tidy` + version 19. The check will unconditionally use the global option + `HeaderFileExtensions`. + When `true`, the check will use the file extension to distinguish header files. Default is `true`. diff --git a/clang-tools-extra/docs/clang-tidy/checks/misc/unused-using-decls.rst b/clang-tools-extra/docs/clang-tidy/checks/misc/unused-using-decls.rst --- a/clang-tools-extra/docs/clang-tidy/checks/misc/unused-using-decls.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/misc/unused-using-decls.rst @@ -22,6 +22,10 @@ .. option:: HeaderFileExtensions + Note: this option is deprecated, it will be removed in :program:`clang-tidy` + version 19. Please use the global configuration option + `HeaderFileExtensions`. + A semicolon-separated list of filename extensions of header files (the filename extensions should not include "." prefix). Default is "h,hh,hpp,hxx". For extension-less header files, use an empty string or leave an diff --git a/clang-tools-extra/docs/clang-tidy/checks/misc/use-anonymous-namespace.rst b/clang-tools-extra/docs/clang-tidy/checks/misc/use-anonymous-namespace.rst --- a/clang-tools-extra/docs/clang-tidy/checks/misc/use-anonymous-namespace.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/misc/use-anonymous-namespace.rst @@ -38,6 +38,10 @@ .. option:: HeaderFileExtensions + Note: this option is deprecated, it will be removed in :program:`clang-tidy` + version 19. Please use the global configuration option + `HeaderFileExtensions`. + A semicolon-separated list of filename extensions of header files (the filename extensions should not include "." prefix). Default is ";h;hh;hpp;hxx". For extension-less header files, using an empty string or leaving an diff --git a/clang-tools-extra/docs/clang-tidy/index.rst b/clang-tools-extra/docs/clang-tidy/index.rst --- a/clang-tools-extra/docs/clang-tidy/index.rst +++ b/clang-tools-extra/docs/clang-tidy/index.rst @@ -139,7 +139,7 @@ When the value is empty, clang-tidy will attempt to find a file named .clang-tidy for each source file in its parent directories. - --config-file= - + --config-file= - Specify the path of .clang-tidy or custom config file: e.g. --config-file=/some/path/myTidyConfigFile This option internally works exactly the same way as @@ -237,7 +237,7 @@ format to stderr. When this option is passed, these per-TU profiles are instead stored as JSON. --system-headers - Display the errors from system headers. - --use-color - + --use-color - Use colors in diagnostics. If not set, colors will be used if the terminal connected to standard output supports colors. @@ -289,6 +289,8 @@ --- Checks: '-*,some-check' WarningsAsErrors: '' + HeaderFileExtensions: ['', 'h','hh','hpp','hxx'] + ImplementationFileExtensions: ['c','cc','cpp','cxx'] HeaderFilterRegex: '' FormatStyle: none InheritParentConfig: true diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/verify-config.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/verify-config.cpp --- a/clang-tools-extra/test/clang-tidy/infrastructure/verify-config.cpp +++ b/clang-tools-extra/test/clang-tidy/infrastructure/verify-config.cpp @@ -3,6 +3,8 @@ // RUN: not clang-tidy -verify-config \ // RUN: --checks='-*,bad*glob,llvm*,llvm-includeorder,my-made-up-check' --config='{Checks: "readability-else-after-ret", \ +// RUN: HeaderFileExtensions: ["h", "hh", "hpp"], \ +// RUN: ImplementationFileExtensions: ["c", "cc", "hpp"], \ // RUN: CheckOptions: [{key: "IgnoreMacros", value: "true"}, \ // RUN: {key: "StriceMode", value: "true"}, \ // RUN: {key: modernize-lop-convert.UseCxx20ReverseRanges, value: true} \ @@ -12,6 +14,7 @@ // CHECK-VERIFY-DAG: command-line option '-config': warning: unknown check 'readability-else-after-ret'; did you mean 'readability-else-after-return' [-verify-config] // CHECK-VERIFY-DAG: command-line option '-config': warning: unknown check option 'modernize-lop-convert.UseCxx20ReverseRanges'; did you mean 'modernize-loop-convert.UseCxx20ReverseRanges' [-verify-config] // CHECK-VERIFY-DAG: command-line option '-config': warning: unknown check option 'StriceMode'; did you mean 'StrictMode' [-verify-config] +// CHECK-VERIFY-DAG: command-line option '-config': warning: HeaderFileExtension 'hpp' is the same as ImplementationFileExtension 'hpp' [-verify-config] // CHECK-VERIFY: command-line option '-checks': warning: check glob 'bad*glob' doesn't match any known check [-verify-config] // CHECK-VERIFY: command-line option '-checks': warning: unknown check 'llvm-includeorder'; did you mean 'llvm-include-order' [-verify-config] // CHECK-VERIFY: command-line option '-checks': warning: unknown check 'my-made-up-check' [-verify-config] diff --git a/clang-tools-extra/unittests/clang-tidy/ClangTidyOptionsTest.cpp b/clang-tools-extra/unittests/clang-tidy/ClangTidyOptionsTest.cpp --- a/clang-tools-extra/unittests/clang-tidy/ClangTidyOptionsTest.cpp +++ b/clang-tools-extra/unittests/clang-tidy/ClangTidyOptionsTest.cpp @@ -76,13 +76,20 @@ TEST(ParseConfiguration, ValidConfiguration) { llvm::ErrorOr Options = - parseConfiguration(llvm::MemoryBufferRef("Checks: \"-*,misc-*\"\n" - "HeaderFilterRegex: \".*\"\n" - "AnalyzeTemporaryDtors: true\n" - "User: some.user", - "Options")); + parseConfiguration(llvm::MemoryBufferRef( + "Checks: \"-*,misc-*\"\n" + "HeaderFileExtensions: [\"\",\"h\",\"hh\",\"hpp\",\"hxx\"]\n" + "ImplementationFileExtensions: [\"c\",\"cc\",\"cpp\",\"cxx\"]\n" + "HeaderFilterRegex: \".*\"\n" + "AnalyzeTemporaryDtors: true\n" + "User: some.user", + "Options")); EXPECT_TRUE(!!Options); EXPECT_EQ("-*,misc-*", *Options->Checks); + EXPECT_EQ(std::vector({"", "h", "hh", "hpp", "hxx"}), + *Options->HeaderFileExtensions); + EXPECT_EQ(std::vector({"c", "cc", "cpp", "cxx"}), + *Options->ImplementationFileExtensions); EXPECT_EQ(".*", *Options->HeaderFilterRegex); EXPECT_EQ("some.user", *Options->User); } @@ -105,6 +112,8 @@ llvm::ErrorOr Options1 = parseConfiguration(llvm::MemoryBufferRef(R"( Checks: "check1,check2" + HeaderFileExtensions: ["h","hh"] + ImplementationFileExtensions: ["c","cc"] HeaderFilterRegex: "filter1" AnalyzeTemporaryDtors: true User: user1 @@ -117,6 +126,8 @@ llvm::ErrorOr Options2 = parseConfiguration(llvm::MemoryBufferRef(R"( Checks: "check3,check4" + HeaderFileExtensions: ["hpp","hxx"] + ImplementationFileExtensions: ["cpp","cxx"] HeaderFilterRegex: "filter2" AnalyzeTemporaryDtors: false User: user2 @@ -128,6 +139,10 @@ ASSERT_TRUE(!!Options2); ClangTidyOptions Options = Options1->merge(*Options2, 0); EXPECT_EQ("check1,check2,check3,check4", *Options.Checks); + EXPECT_EQ(std::vector({"hpp", "hxx"}), + *Options.HeaderFileExtensions); + EXPECT_EQ(std::vector({"cpp", "cxx"}), + *Options.ImplementationFileExtensions); EXPECT_EQ("filter2", *Options.HeaderFilterRegex); EXPECT_EQ("user2", *Options.User); ASSERT_TRUE(Options.ExtraArgs.has_value());