Index: clang-tools-extra/trunk/clang-tidy/CMakeLists.txt =================================================================== --- clang-tools-extra/trunk/clang-tidy/CMakeLists.txt +++ clang-tools-extra/trunk/clang-tidy/CMakeLists.txt @@ -8,6 +8,7 @@ ClangTidyDiagnosticConsumer.cpp ClangTidyOptions.cpp ClangTidyProfiling.cpp + ExpandModularHeadersPPCallbacks.cpp DEPENDS ClangSACheckers Index: clang-tools-extra/trunk/clang-tidy/ClangTidy.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/ClangTidy.h +++ clang-tools-extra/trunk/clang-tidy/ClangTidy.h @@ -143,6 +143,24 @@ /// dependent properties, e.g. the order of include directives. virtual void registerPPCallbacks(CompilerInstance &Compiler) {} + /// \brief Override this to register ``PPCallbacks`` in the preprocessor. + /// + /// This should be used for clang-tidy checks that analyze preprocessor- + /// dependent properties, e.g. include directives and macro definitions. + /// + /// There are two Preprocessors to choose from that differ in how they handle + /// modular #includes: + /// - PP is the real Preprocessor. It doesn't walk into modular #includes and + /// thus doesn't generate PPCallbacks for their contents. + /// - ModuleExpanderPP preprocesses the whole translation unit in the + /// non-modular mode, which allows it to generate PPCallbacks not only for + /// the main file and textual headers, but also for all transitively + /// included modular headers when the analysis runs with modules enabled. + /// When modules are not enabled ModuleExpanderPP just points to the real + /// preprocessor. + virtual void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, + Preprocessor *ModuleExpanderPP) {} + /// \brief Override this to register AST matchers with \p Finder. /// /// This should be used by clang-tidy checks that analyze code properties that @@ -190,7 +208,9 @@ class ClangTidyASTConsumerFactory { public: - ClangTidyASTConsumerFactory(ClangTidyContext &Context); + ClangTidyASTConsumerFactory( + ClangTidyContext &Context, + IntrusiveRefCntPtr OverlayFS = nullptr); /// \brief Returns an ASTConsumer that runs the specified clang-tidy checks. std::unique_ptr @@ -204,6 +224,7 @@ private: ClangTidyContext &Context; + IntrusiveRefCntPtr OverlayFS; std::unique_ptr CheckFactories; }; @@ -233,7 +254,7 @@ runClangTidy(clang::tidy::ClangTidyContext &Context, const tooling::CompilationDatabase &Compilations, ArrayRef InputFiles, - llvm::IntrusiveRefCntPtr BaseFS, + llvm::IntrusiveRefCntPtr BaseFS, bool EnableCheckProfile = false, llvm::StringRef StoreCheckProfile = StringRef()); Index: clang-tools-extra/trunk/clang-tidy/ClangTidy.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/ClangTidy.cpp +++ clang-tools-extra/trunk/clang-tidy/ClangTidy.cpp @@ -18,6 +18,7 @@ #include "ClangTidyDiagnosticConsumer.h" #include "ClangTidyModuleRegistry.h" #include "ClangTidyProfiling.h" +#include "ExpandModularHeadersPPCallbacks.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" @@ -290,8 +291,10 @@ } // namespace ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory( - ClangTidyContext &Context) - : Context(Context), CheckFactories(new ClangTidyCheckFactories) { + ClangTidyContext &Context, + IntrusiveRefCntPtr OverlayFS) + : Context(Context), OverlayFS(OverlayFS), + CheckFactories(new ClangTidyCheckFactories) { for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(), E = ClangTidyModuleRegistry::end(); I != E; ++I) { @@ -351,7 +354,8 @@ clang::CompilerInstance &Compiler, StringRef File) { // FIXME: Move this to a separate method, so that CreateASTConsumer doesn't // modify Compiler. - Context.setSourceManager(&Compiler.getSourceManager()); + SourceManager *SM = &Compiler.getSourceManager(); + Context.setSourceManager(SM); Context.setCurrentFile(File); Context.setASTContext(&Compiler.getASTContext()); @@ -377,9 +381,20 @@ std::unique_ptr Finder( new ast_matchers::MatchFinder(std::move(FinderOptions))); + Preprocessor *PP = &Compiler.getPreprocessor(); + Preprocessor *ModuleExpanderPP = PP; + + if (Context.getLangOpts().Modules && OverlayFS != nullptr) { + auto ModuleExpander = llvm::make_unique( + &Compiler, OverlayFS); + ModuleExpanderPP = ModuleExpander->getPreprocessor(); + PP->addPPCallbacks(std::move(ModuleExpander)); + } + for (auto &Check : Checks) { Check->registerMatchers(&*Finder); Check->registerPPCallbacks(Compiler); + Check->registerPPCallbacks(*SM, PP, ModuleExpanderPP); } std::vector> Consumers; @@ -505,7 +520,7 @@ runClangTidy(clang::tidy::ClangTidyContext &Context, const CompilationDatabase &Compilations, ArrayRef InputFiles, - llvm::IntrusiveRefCntPtr BaseFS, + llvm::IntrusiveRefCntPtr BaseFS, bool EnableCheckProfile, llvm::StringRef StoreCheckProfile) { ClangTool Tool(Compilations, InputFiles, std::make_shared(), BaseFS); @@ -541,7 +556,9 @@ class ActionFactory : public FrontendActionFactory { public: - ActionFactory(ClangTidyContext &Context) : ConsumerFactory(Context) {} + ActionFactory(ClangTidyContext &Context, + IntrusiveRefCntPtr BaseFS) + : ConsumerFactory(Context, BaseFS) {} FrontendAction *create() override { return new Action(&ConsumerFactory); } bool runInvocation(std::shared_ptr Invocation, @@ -572,7 +589,7 @@ ClangTidyASTConsumerFactory ConsumerFactory; }; - ActionFactory Factory(Context); + ActionFactory Factory(Context, BaseFS); Tool.run(&Factory); return DiagConsumer.take(); } Index: clang-tools-extra/trunk/clang-tidy/ExpandModularHeadersPPCallbacks.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/ExpandModularHeadersPPCallbacks.h +++ clang-tools-extra/trunk/clang-tidy/ExpandModularHeadersPPCallbacks.h @@ -0,0 +1,137 @@ +//===- ExpandModularHeadersPPCallbacks.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_TOOLING_EXPANDMODULARHEADERSPPCALLBACKS_H_ +#define LLVM_CLANG_TOOLING_EXPANDMODULARHEADERSPPCALLBACKS_H_ + +#include "clang/Lex/PPCallbacks.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/DenseSet.h" + +namespace clang { +class CompilerInstance; + +namespace serialization { +class ModuleFile; +} // namespace serialization + +namespace tooling { + +/// \brief Handles PPCallbacks and re-runs preprocessing of the whole +/// translation unit with modules disabled. +/// +/// This way it's possible to get PPCallbacks for the whole translation unit +/// including the contents of the modular headers and all their transitive +/// includes. +/// +/// This allows existing tools based on PPCallbacks to retain their functionality +/// when running with C++ modules enabled. This only works in the backwards +/// compatible modules mode, i.e. when code can still be parsed in non-modular +/// way. +class ExpandModularHeadersPPCallbacks : public PPCallbacks { +public: + ExpandModularHeadersPPCallbacks( + CompilerInstance *Compiler, + IntrusiveRefCntPtr OverlayFS); + ~ExpandModularHeadersPPCallbacks(); + + /// \brief Returns the preprocessor that provides callbacks for the whole + /// translation unit, including the main file, textual headers, and modular + /// headers. + /// + /// This preprocessor is separate from the one used by the rest of the + /// compiler. + Preprocessor *getPreprocessor() const; + +private: + class FileRecorder; + + void handleModuleFile(serialization::ModuleFile *MF); + void parseToLocation(SourceLocation Loc); + + // Handle PPCallbacks. + void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID) override; + + void InclusionDirective(SourceLocation DirectiveLoc, + const Token &IncludeToken, StringRef IncludedFilename, + bool IsAngled, CharSourceRange FilenameRange, + const FileEntry *IncludedFile, StringRef SearchPath, + StringRef RelativePath, const Module *Imported, + SrcMgr::CharacteristicKind FileType) override; + + void EndOfMainFile() override; + + // Handle all other callbacks. + // Just parse to the corresponding location to generate PPCallbacks for the + // corresponding range + void Ident(SourceLocation Loc, StringRef) override; + void PragmaDirective(SourceLocation Loc, PragmaIntroducerKind) override; + void PragmaComment(SourceLocation Loc, const IdentifierInfo *, + StringRef) override; + void PragmaDetectMismatch(SourceLocation Loc, StringRef, StringRef) override; + void PragmaDebug(SourceLocation Loc, StringRef) override; + void PragmaMessage(SourceLocation Loc, StringRef, PragmaMessageKind, + StringRef) override; + void PragmaDiagnosticPush(SourceLocation Loc, StringRef) override; + void PragmaDiagnosticPop(SourceLocation Loc, StringRef) override; + void PragmaDiagnostic(SourceLocation Loc, StringRef, diag::Severity, + StringRef) override; + void HasInclude(SourceLocation Loc, StringRef, bool, const FileEntry *, + SrcMgr::CharacteristicKind) override; + void PragmaOpenCLExtension(SourceLocation NameLoc, const IdentifierInfo *, + SourceLocation StateLoc, unsigned) override; + void PragmaWarning(SourceLocation Loc, StringRef, ArrayRef) override; + void PragmaWarningPush(SourceLocation Loc, int) override; + void PragmaWarningPop(SourceLocation Loc) override; + void PragmaAssumeNonNullBegin(SourceLocation Loc) override; + void PragmaAssumeNonNullEnd(SourceLocation Loc) override; + void MacroExpands(const Token &MacroNameTok, const MacroDefinition &, + SourceRange Range, const MacroArgs *) override; + void MacroDefined(const Token &MacroNameTok, + const MacroDirective *MD) override; + void MacroUndefined(const Token &, const MacroDefinition &, + const MacroDirective *Undef) override; + void Defined(const Token &MacroNameTok, const MacroDefinition &, + SourceRange Range) override; + void SourceRangeSkipped(SourceRange Range, SourceLocation EndifLoc) override; + void If(SourceLocation Loc, SourceRange, ConditionValueKind) override; + void Elif(SourceLocation Loc, SourceRange, ConditionValueKind, + SourceLocation) override; + void Ifdef(SourceLocation Loc, const Token &, + const MacroDefinition &) override; + void Ifndef(SourceLocation Loc, const Token &, + const MacroDefinition &) override; + void Else(SourceLocation Loc, SourceLocation) override; + void Endif(SourceLocation Loc, SourceLocation) override; + + std::unique_ptr Recorder; + // Set of all the modules visited. Avoids processing a module more than once. + llvm::DenseSet VisitedModules; + + CompilerInstance &Compiler; + // Additional filesystem for replay. Provides all input files from modules. + llvm::IntrusiveRefCntPtr InMemoryFs; + + SourceManager &Sources; + DiagnosticsEngine Diags; + LangOptions LangOpts; + TrivialModuleLoader ModuleLoader; + + std::unique_ptr HeaderInfo; + std::unique_ptr Preprocessor; + bool EnteredMainFile = false; + bool StartedLexing = false; + Token CurrentToken; +}; + +} // namespace tooling +} // namespace clang + +#endif // LLVM_CLANG_TOOLING_EXPANDMODULARHEADERSPPCALLBACKS_H_ Index: clang-tools-extra/trunk/clang-tidy/ExpandModularHeadersPPCallbacks.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/ExpandModularHeadersPPCallbacks.cpp +++ clang-tools-extra/trunk/clang-tidy/ExpandModularHeadersPPCallbacks.cpp @@ -0,0 +1,294 @@ +//===- ExpandModularHeadersPPCallbacks.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 +// +//===----------------------------------------------------------------------===// + +#include "ExpandModularHeadersPPCallbacks.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "clang/Serialization/ASTReader.h" + +namespace clang { +namespace tooling { + +class ExpandModularHeadersPPCallbacks::FileRecorder { +public: + /// Records that a given file entry is needed for replaying callbacks. + void addNecessaryFile(const FileEntry *File) { FilesToRecord.insert(File); } + + /// Records content for a file and adds it to the FileSystem. + void recordFileContent(const FileEntry *File, + const SrcMgr::ContentCache &ContentCache, + llvm::vfs::InMemoryFileSystem &InMemoryFs) { + // Return if we are not interested in the contents of this file. + if (!FilesToRecord.count(File)) + return; + + // FIXME: Why is this happening? We might be losing contents here. + if (!ContentCache.getRawBuffer()) + return; + + InMemoryFs.addFile(File->getName(), /*ModificationTime=*/0, + llvm::MemoryBuffer::getMemBufferCopy( + ContentCache.getRawBuffer()->getBuffer())); + // Remove the file from the set of necessary files. + FilesToRecord.erase(File); + } + + /// Makes sure we have contents for all the files we were interested in. Ideally + /// `FilesToRecord` should be empty. + void checkAllFilesRecorded() { + for (auto FileEntry : FilesToRecord) + llvm::errs() << "Did not record contents for input file: " + << FileEntry->getName() << "\n"; + } + +private: + /// A set of files whose contents are to be recorded. + llvm::DenseSet FilesToRecord; +}; + +ExpandModularHeadersPPCallbacks::ExpandModularHeadersPPCallbacks( + CompilerInstance *CI, + IntrusiveRefCntPtr OverlayFS) + : Recorder(llvm::make_unique()), Compiler(*CI), + InMemoryFs(new llvm::vfs::InMemoryFileSystem), + Sources(Compiler.getSourceManager()), + // Forward the new diagnostics to the original DiagnosticConsumer. + Diags(new DiagnosticIDs, new DiagnosticOptions, + new ForwardingDiagnosticConsumer(Compiler.getDiagnosticClient())), + LangOpts(Compiler.getLangOpts()) { + // Add a FileSystem containing the extra files needed in place of modular + // headers. + OverlayFS->pushOverlay(InMemoryFs); + + Diags.setSourceManager(&Sources); + + LangOpts.Modules = false; + + auto HSO = std::make_shared(); + *HSO = Compiler.getHeaderSearchOpts(); + + HeaderInfo = llvm::make_unique(HSO, Sources, Diags, LangOpts, + &Compiler.getTarget()); + + auto PO = std::make_shared(); + *PO = Compiler.getPreprocessorOpts(); + + Preprocessor = llvm::make_unique( + PO, Diags, LangOpts, Sources, *HeaderInfo, ModuleLoader, + /*IILookup=*/nullptr, + /*OwnsHeaderSearch=*/false); + Preprocessor->Initialize(Compiler.getTarget(), Compiler.getAuxTarget()); + InitializePreprocessor(*Preprocessor, *PO, Compiler.getPCHContainerReader(), + Compiler.getFrontendOpts()); + ApplyHeaderSearchOptions(*HeaderInfo, *HSO, LangOpts, + Compiler.getTarget().getTriple()); +} + +ExpandModularHeadersPPCallbacks::~ExpandModularHeadersPPCallbacks() = default; + +Preprocessor *ExpandModularHeadersPPCallbacks::getPreprocessor() const { + return Preprocessor.get(); +} + +void ExpandModularHeadersPPCallbacks::handleModuleFile( + serialization::ModuleFile *MF) { + if (!MF) + return; + // Avoid processing a ModuleFile more than once. + if (VisitedModules.count(MF)) + return; + VisitedModules.insert(MF); + + // Visit all the input files of this module and mark them to record their + // contents later. + Compiler.getModuleManager()->visitInputFiles( + *MF, true, false, + [this](const serialization::InputFile &IF, bool /*IsSystem*/) { + Recorder->addNecessaryFile(IF.getFile()); + }); + // Recursively handle all transitively imported modules. + for (auto Import : MF->Imports) + handleModuleFile(Import); +} + +void ExpandModularHeadersPPCallbacks::parseToLocation(SourceLocation Loc) { + // Load all source locations present in the external sources. + for (unsigned I = 0, N = Sources.loaded_sloc_entry_size(); I != N; ++I) { + Sources.getLoadedSLocEntry(I, nullptr); + } + // Record contents of files we are interested in and add to the FileSystem. + for (auto It = Sources.fileinfo_begin(); It != Sources.fileinfo_end(); ++It) { + Recorder->recordFileContent(It->getFirst(), *It->getSecond(), *InMemoryFs); + } + Recorder->checkAllFilesRecorded(); + + if (!StartedLexing) { + StartedLexing = true; + Preprocessor->Lex(CurrentToken); + } + while (!CurrentToken.is(tok::eof) && + Sources.isBeforeInTranslationUnit(CurrentToken.getLocation(), Loc)) { + Preprocessor->Lex(CurrentToken); + } +} + +void ExpandModularHeadersPPCallbacks::FileChanged( + SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, FileID PrevFID = FileID()) { + if (!EnteredMainFile) { + EnteredMainFile = true; + Preprocessor->EnterMainSourceFile(); + } +} + +void ExpandModularHeadersPPCallbacks::InclusionDirective( + SourceLocation DirectiveLoc, const Token &IncludeToken, + StringRef IncludedFilename, bool IsAngled, CharSourceRange FilenameRange, + const FileEntry *IncludedFile, StringRef SearchPath, StringRef RelativePath, + const Module *Imported, SrcMgr::CharacteristicKind FileType) { + if (Imported) { + serialization::ModuleFile *MF = + Compiler.getModuleManager()->getModuleManager().lookup( + Imported->getASTFile()); + handleModuleFile(MF); + } + parseToLocation(DirectiveLoc); +} + +void ExpandModularHeadersPPCallbacks::EndOfMainFile() { + while (!CurrentToken.is(tok::eof)) + Preprocessor->Lex(CurrentToken); +} + +// Handle all other callbacks. +// Just parse to the corresponding location to generate the same callback for +// the PPCallbacks registered in our custom preprocessor. +void ExpandModularHeadersPPCallbacks::Ident(SourceLocation Loc, StringRef) { + parseToLocation(Loc); +} +void ExpandModularHeadersPPCallbacks::PragmaDirective(SourceLocation Loc, + PragmaIntroducerKind) { + parseToLocation(Loc); +} +void ExpandModularHeadersPPCallbacks::PragmaComment(SourceLocation Loc, + const IdentifierInfo *, + StringRef) { + parseToLocation(Loc); +} +void ExpandModularHeadersPPCallbacks::PragmaDetectMismatch(SourceLocation Loc, + StringRef, + StringRef) { + parseToLocation(Loc); +} +void ExpandModularHeadersPPCallbacks::PragmaDebug(SourceLocation Loc, + StringRef) { + parseToLocation(Loc); +} +void ExpandModularHeadersPPCallbacks::PragmaMessage(SourceLocation Loc, + StringRef, + PragmaMessageKind, + StringRef) { + parseToLocation(Loc); +} +void ExpandModularHeadersPPCallbacks::PragmaDiagnosticPush(SourceLocation Loc, + StringRef) { + parseToLocation(Loc); +} +void ExpandModularHeadersPPCallbacks::PragmaDiagnosticPop(SourceLocation Loc, + StringRef) { + parseToLocation(Loc); +} +void ExpandModularHeadersPPCallbacks::PragmaDiagnostic(SourceLocation Loc, + StringRef, + diag::Severity, + StringRef) { + parseToLocation(Loc); +} +void ExpandModularHeadersPPCallbacks::HasInclude(SourceLocation Loc, StringRef, + bool, const FileEntry *, + SrcMgr::CharacteristicKind) { + parseToLocation(Loc); +} +void ExpandModularHeadersPPCallbacks::PragmaOpenCLExtension( + SourceLocation NameLoc, const IdentifierInfo *, SourceLocation StateLoc, + unsigned) { + // FIME: Figure out whether it's the right location to parse to. + parseToLocation(NameLoc); +} +void ExpandModularHeadersPPCallbacks::PragmaWarning(SourceLocation Loc, + StringRef, ArrayRef) { + parseToLocation(Loc); +} +void ExpandModularHeadersPPCallbacks::PragmaWarningPush(SourceLocation Loc, + int) { + parseToLocation(Loc); +} +void ExpandModularHeadersPPCallbacks::PragmaWarningPop(SourceLocation Loc) { + parseToLocation(Loc); +} +void ExpandModularHeadersPPCallbacks::PragmaAssumeNonNullBegin( + SourceLocation Loc) { + parseToLocation(Loc); +} +void ExpandModularHeadersPPCallbacks::PragmaAssumeNonNullEnd( + SourceLocation Loc) { + parseToLocation(Loc); +} +void ExpandModularHeadersPPCallbacks::MacroExpands(const Token &MacroNameTok, + const MacroDefinition &, + SourceRange Range, + const MacroArgs *) { + // FIME: Figure out whether it's the right location to parse to. + parseToLocation(Range.getBegin()); +} +void ExpandModularHeadersPPCallbacks::MacroDefined(const Token &MacroNameTok, + const MacroDirective *MD) { + parseToLocation(MD->getLocation()); +} +void ExpandModularHeadersPPCallbacks::MacroUndefined( + const Token &, const MacroDefinition &, const MacroDirective *Undef) { + if (Undef) + parseToLocation(Undef->getLocation()); +} +void ExpandModularHeadersPPCallbacks::Defined(const Token &MacroNameTok, + const MacroDefinition &, + SourceRange Range) { + // FIME: Figure out whether it's the right location to parse to. + parseToLocation(Range.getBegin()); +} +void ExpandModularHeadersPPCallbacks::SourceRangeSkipped( + SourceRange Range, SourceLocation EndifLoc) { + // FIME: Figure out whether it's the right location to parse to. + parseToLocation(EndifLoc); +} +void ExpandModularHeadersPPCallbacks::If(SourceLocation Loc, SourceRange, + ConditionValueKind) { + parseToLocation(Loc); +} +void ExpandModularHeadersPPCallbacks::Elif(SourceLocation Loc, SourceRange, + ConditionValueKind, SourceLocation) { + parseToLocation(Loc); +} +void ExpandModularHeadersPPCallbacks::Ifdef(SourceLocation Loc, const Token &, + const MacroDefinition &) { + parseToLocation(Loc); +} +void ExpandModularHeadersPPCallbacks::Ifndef(SourceLocation Loc, const Token &, + const MacroDefinition &) { + parseToLocation(Loc); +} +void ExpandModularHeadersPPCallbacks::Else(SourceLocation Loc, SourceLocation) { + parseToLocation(Loc); +} +void ExpandModularHeadersPPCallbacks::Endif(SourceLocation Loc, + SourceLocation) { + parseToLocation(Loc); +} + +} // namespace tooling +} // namespace clang Index: clang-tools-extra/trunk/clang-tidy/readability/IdentifierNamingCheck.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/readability/IdentifierNamingCheck.h +++ clang-tools-extra/trunk/clang-tidy/readability/IdentifierNamingCheck.h @@ -38,7 +38,8 @@ void storeOptions(ClangTidyOptions::OptionMap &Opts) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; - void registerPPCallbacks(CompilerInstance &Compiler) override; + void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, + Preprocessor *ModuleExpanderPP) override; void onEndOfTranslationUnit() override; enum CaseType { Index: clang-tools-extra/trunk/clang-tidy/readability/IdentifierNamingCheck.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/readability/IdentifierNamingCheck.cpp +++ clang-tools-extra/trunk/clang-tidy/readability/IdentifierNamingCheck.cpp @@ -240,10 +240,11 @@ Finder->addMatcher(nestedNameSpecifierLoc().bind("nestedNameLoc"), this); } -void IdentifierNamingCheck::registerPPCallbacks(CompilerInstance &Compiler) { - Compiler.getPreprocessor().addPPCallbacks( - llvm::make_unique( - &Compiler.getPreprocessor(), this)); +void IdentifierNamingCheck::registerPPCallbacks( + const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) { + ModuleExpanderPP->addPPCallbacks( + llvm::make_unique(ModuleExpanderPP, + this)); } static bool matchesStyle(StringRef Name, Index: clang-tools-extra/trunk/clang-tidy/tool/ClangTidyMain.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/tool/ClangTidyMain.cpp +++ clang-tools-extra/trunk/clang-tidy/tool/ClangTidyMain.cpp @@ -304,11 +304,10 @@ } llvm::IntrusiveRefCntPtr -getVfsOverlayFromFile(const std::string &OverlayFile) { - llvm::IntrusiveRefCntPtr OverlayFS( - new vfs::OverlayFileSystem(vfs::getRealFileSystem())); +getVfsFromFile(const std::string &OverlayFile, + llvm::IntrusiveRefCntPtr BaseFS) { llvm::ErrorOr> Buffer = - OverlayFS->getBufferForFile(OverlayFile); + BaseFS->getBufferForFile(OverlayFile); if (!Buffer) { llvm::errs() << "Can't load virtual filesystem overlay file '" << OverlayFile << "': " << Buffer.getError().message() @@ -323,19 +322,23 @@ << OverlayFile << "'.\n"; return nullptr; } - OverlayFS->pushOverlay(FS); - return OverlayFS; + return FS; } static int clangTidyMain(int argc, const char **argv) { llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); CommonOptionsParser OptionsParser(argc, argv, ClangTidyCategory, cl::ZeroOrMore); - llvm::IntrusiveRefCntPtr BaseFS( - VfsOverlay.empty() ? vfs::getRealFileSystem() - : getVfsOverlayFromFile(VfsOverlay)); - if (!BaseFS) - return 1; + llvm::IntrusiveRefCntPtr BaseFS( + new vfs::OverlayFileSystem(vfs::getRealFileSystem())); + + if (!VfsOverlay.empty()) { + IntrusiveRefCntPtr VfsFromFile = + getVfsFromFile(VfsOverlay, BaseFS); + if (!VfsFromFile) + return 1; + BaseFS->pushOverlay(VfsFromFile); + } auto OwningOptionsProvider = createOptionsProvider(BaseFS); auto *OptionsProvider = OwningOptionsProvider.get(); Index: clang-tools-extra/trunk/test/CMakeLists.txt =================================================================== --- clang-tools-extra/trunk/test/CMakeLists.txt +++ clang-tools-extra/trunk/test/CMakeLists.txt @@ -62,6 +62,8 @@ clang-resource-headers clang-tidy + # Clang-tidy tests need clang for building modules. + clang ) if(CLANGD_BUILD_XPC_SUPPORT) Index: clang-tools-extra/trunk/test/clang-tidy/Inputs/expand-modular-headers-ppcallbacks/a.h =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/Inputs/expand-modular-headers-ppcallbacks/a.h +++ clang-tools-extra/trunk/test/clang-tidy/Inputs/expand-modular-headers-ppcallbacks/a.h @@ -0,0 +1 @@ +#define a Index: clang-tools-extra/trunk/test/clang-tidy/Inputs/expand-modular-headers-ppcallbacks/b.h =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/Inputs/expand-modular-headers-ppcallbacks/b.h +++ clang-tools-extra/trunk/test/clang-tidy/Inputs/expand-modular-headers-ppcallbacks/b.h @@ -0,0 +1,2 @@ +#include "a.h" +#define b Index: clang-tools-extra/trunk/test/clang-tidy/Inputs/expand-modular-headers-ppcallbacks/c.h =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/Inputs/expand-modular-headers-ppcallbacks/c.h +++ clang-tools-extra/trunk/test/clang-tidy/Inputs/expand-modular-headers-ppcallbacks/c.h @@ -0,0 +1,2 @@ +#include "b.h" +#define c Index: clang-tools-extra/trunk/test/clang-tidy/Inputs/expand-modular-headers-ppcallbacks/module.modulemap =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/Inputs/expand-modular-headers-ppcallbacks/module.modulemap +++ clang-tools-extra/trunk/test/clang-tidy/Inputs/expand-modular-headers-ppcallbacks/module.modulemap @@ -0,0 +1,3 @@ +module a { header "a.h" export * } +module b { header "b.h" export * use a } +module c { header "c.h" export * use b } Index: clang-tools-extra/trunk/test/clang-tidy/expand-modular-headers-ppcallbacks.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/expand-modular-headers-ppcallbacks.cpp +++ clang-tools-extra/trunk/test/clang-tidy/expand-modular-headers-ppcallbacks.cpp @@ -0,0 +1,35 @@ +// Sanity-check. Run without modules: +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: cp %S/Inputs/expand-modular-headers-ppcallbacks/* %t/ +// RUN: %check_clang_tidy %s readability-identifier-naming %t/without-modules -- \ +// RUN: -config="CheckOptions: [{ \ +// RUN: key: readability-identifier-naming.MacroDefinitionCase, value: UPPER_CASE }]" \ +// RUN: -header-filter=.* \ +// RUN: -- -x c++ -std=c++11 -I%t/ +// +// Run clang-tidy on a file with modular includes: +// +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: cp %S/Inputs/expand-modular-headers-ppcallbacks/* %t/ +// RUN: %check_clang_tidy %s readability-identifier-naming %t/with-modules -- \ +// RUN: -config="CheckOptions: [{ \ +// RUN: key: readability-identifier-naming.MacroDefinitionCase, value: UPPER_CASE }]" \ +// RUN: -header-filter=.* \ +// RUN: -- -x c++ -std=c++11 -I%t/ \ +// RUN: -fmodules -fimplicit-modules -fno-implicit-module-maps \ +// RUN: -fmodule-map-file=%t/module.modulemap \ +// RUN: -fmodules-cache-path=%t/module-cache/ +#include "c.h" + +// CHECK-MESSAGES: a.h:1:9: warning: invalid case style for macro definition 'a' [readability-identifier-naming] +// CHECK-MESSAGES: a.h:1:9: note: FIX-IT applied suggested code changes +// CHECK-MESSAGES: b.h:2:9: warning: invalid case style for macro definition 'b' +// CHECK-MESSAGES: b.h:2:9: note: FIX-IT applied suggested code changes +// CHECK-MESSAGES: c.h:2:9: warning: invalid case style for macro definition 'c' +// CHECK-MESSAGES: c.h:2:9: note: FIX-IT applied suggested code changes + +#define m +// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: invalid case style for macro definition 'm' +// CHECK-MESSAGES: :[[@LINE-2]]:9: note: FIX-IT applied suggested code changes