diff --git a/clang-tools-extra/clang-tidy/CMakeLists.txt b/clang-tools-extra/clang-tidy/CMakeLists.txt --- a/clang-tools-extra/clang-tidy/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/CMakeLists.txt @@ -8,6 +8,7 @@ ClangTidyDiagnosticConsumer.cpp ClangTidyOptions.cpp ClangTidyProfiling.cpp + ExpandModularHeadersPPCallbacks.cpp DEPENDS ClangSACheckers diff --git a/clang-tools-extra/clang-tidy/ClangTidy.h b/clang-tools-extra/clang-tidy/ClangTidy.h --- a/clang-tools-extra/clang-tidy/ClangTidy.h +++ b/clang-tools-extra/clang-tidy/ClangTidy.h @@ -143,6 +143,18 @@ /// dependent properties, e.g. the order of include directives. virtual void registerPPCallbacks(CompilerInstance &Compiler) {} + /// \brief Override this to register ``PPCallbacks`` in the preprocessor. + /// + /// There are two Preprocessors to choose frome: the real one and the one that + /// generates callbacks for all transitively included headers even when + /// compiling with modules. When compiling without modules, both pointers + /// point to the real preprocessor. + /// + /// This should be used for clang-tidy checks that analyze preprocessor- + /// dependent properties, e.g. include directives and macro definitions. + 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 +202,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 +218,7 @@ private: ClangTidyContext &Context; + IntrusiveRefCntPtr OverlayFS; std::unique_ptr CheckFactories; }; @@ -233,7 +248,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()); 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 @@ -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(); } diff --git a/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.h b/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.h @@ -0,0 +1,135 @@ +//===- 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 +// +//===----------------------------------------------------------------------===// +// +// Defines the ExpandModularHeadersPPCallbacks class that handles #includes of +// modular headers, traverses all transitively included headers in non-modular +// mode and generates PPCallbacks for their contents. +// This allows existing tools based on PPCallbacks to retain their functionality +// when running with C++ modules enabled. +//===----------------------------------------------------------------------===// + +#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 tooling { + +/// \brief Handles PPCallbacks and replays preprocessing with modules disabled. +/// +/// This way it's possible to get PPCallbacks from the contents of the modular +/// headers and all their transitive includes. +class ExpandModularHeadersPPCallbacks : public PPCallbacks { +public: + ExpandModularHeadersPPCallbacks( + CompilerInstance *compiler, + IntrusiveRefCntPtr OverlayFS); + ~ExpandModularHeadersPPCallbacks(); + + /// \brief Users can get expanded PPCallbacks by registering their callback + /// handlers in the preprocessor instance returned by this method. + 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 directive_loc, + const Token &include_token, + StringRef included_filename, bool is_angled, + CharSourceRange filename_range, + const FileEntry *included_file, StringRef search_path, + StringRef relative_path, const Module *imported, + SrcMgr::CharacteristicKind FileType) override; + + // FIXME: This crashes: + // void EndOfMainFile() override { target_callbacks_->EndOfMainFile(); } + + // 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 file_recorder_; + // Set of all the modules visited. Avoids processing a module more than once. + llvm::DenseSet visited_modules_; + + CompilerInstance &compiler_; + // Additional filesystem for replay. Provides all input files from modules. + llvm::IntrusiveRefCntPtr in_memory_fs_; + + SourceManager &sources_; + DiagnosticsEngine diags_; + LangOptions lang_opts_; + TrivialModuleLoader module_loader_; + + std::unique_ptr header_info_; + std::unique_ptr preprocessor_; + bool entered_main_file_ = false; + bool started_lexing_ = false; + Token current_token_; +}; + +} // namespace tooling +} // namespace clang + +#endif // LLVM_CLANG_TOOLING_EXPANDMODULARHEADERSPPCALLBACKS_H_ diff --git a/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.cpp b/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.cpp @@ -0,0 +1,302 @@ +//===- 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: + // Stores FileEntry for which contents are to be recorded later. + void AddFileToRecord(const FileEntry *file) { files_to_record_.insert(file); } + + // Records content for a file and adds it to the FileSystem. + void RecordContent(const FileEntry *file, + const SrcMgr::ContentCache *content_cache, + llvm::vfs::InMemoryFileSystem &in_memory_fs_) { + // Return if we are not interested in the contents of this file. + if (!files_to_record_.count(file)) + return; + + //assert(content_cache->getRawBuffer() && "No raw buffer"); + // FIXME: Why is this happening? We might be losing contents here. + if (!content_cache->getRawBuffer()) + return; + + in_memory_fs_.addFile(file->getName(), /*ModificationTime=*/0, + llvm::MemoryBuffer::getMemBufferCopy( + content_cache->getRawBuffer()->getBuffer())); + // Remove file since we have successfully recorded its contents. + files_to_record_.erase(file); + } + + // Make sure we have contents for all the files we were interested in. Ideally + // `files_to_record_` should be empty. + void CheckAllFilesRecorded() { + for (auto file_entry : files_to_record_) + llvm::errs() << "Did not record contents for input file: " + << file_entry->getName() << "\n"; + } + +private: + // Set of files whose contents are to be recorded. + llvm::DenseSet files_to_record_; +}; + +ExpandModularHeadersPPCallbacks::ExpandModularHeadersPPCallbacks( + CompilerInstance *compiler, + IntrusiveRefCntPtr OverlayFS) + : file_recorder_(llvm::make_unique()), compiler_(*compiler), + in_memory_fs_(new llvm::vfs::InMemoryFileSystem), + sources_(compiler_.getSourceManager()), + // Forward the new diagnostics to the original DiagnosticConsumer. + diags_(new DiagnosticIDs, new DiagnosticOptions, + new ForwardingDiagnosticConsumer(compiler_.getDiagnosticClient())), + lang_opts_(compiler_.getLangOpts()) { + // Add FileSystem containing the extra files needed in place of modular + // headers. + OverlayFS->pushOverlay(in_memory_fs_); + + diags_.setSourceManager(&sources_); + + // Switch of header modules in the new preprocessor. + lang_opts_.Modules = false; + + auto header_search_options = std::make_shared(); + *header_search_options = compiler_.getHeaderSearchOpts(); + + header_info_ = + llvm::make_unique(header_search_options, sources_, diags_, + lang_opts_, &compiler_.getTarget()); + + auto preprocessor_options = std::make_shared(); + *preprocessor_options = compiler_.getPreprocessorOpts(); + + preprocessor_ = + llvm::make_unique(preprocessor_options, diags_, lang_opts_, + sources_, *header_info_, module_loader_, + /*IILookup=*/nullptr, + /*OwnsHeaderSearch=*/false); + preprocessor_->Initialize(compiler_.getTarget(), compiler_.getAuxTarget()); + InitializePreprocessor(*preprocessor_, *preprocessor_options, + compiler_.getPCHContainerReader(), + compiler_.getFrontendOpts()); + ApplyHeaderSearchOptions(*header_info_, *header_search_options, lang_opts_, + 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 (visited_modules_.count(MF)) + return; + visited_modules_.insert(MF); + + // Visit all the input files of this module and mark them to be record their + // contents later. + compiler_.getModuleManager()->visitInputFiles( + *MF, true, false, + [this](const serialization::InputFile &IF, bool isSystem) { + auto *F = IF.getFile(); + file_recorder_->AddFileToRecord(F); + }); + // 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) { + file_recorder_->RecordContent(it->getFirst(), it->getSecond(), + *in_memory_fs_); + } + file_recorder_->CheckAllFilesRecorded(); + + if (!started_lexing_) { + started_lexing_ = true; + preprocessor_->Lex(current_token_); + } + while ( + !current_token_.is(tok::eof) && + sources_.isBeforeInTranslationUnit(current_token_.getLocation(), loc)) { + preprocessor_->Lex(current_token_); + } +} + +void ExpandModularHeadersPPCallbacks::FileChanged( + SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, FileID PrevFID = FileID()) { + if (!entered_main_file_) { + entered_main_file_ = true; + preprocessor_->EnterMainSourceFile(); + } +} + +void ExpandModularHeadersPPCallbacks::InclusionDirective( + SourceLocation directive_loc, const Token &include_token, + StringRef included_filename, bool is_angled, CharSourceRange filename_range, + const FileEntry *included_file, StringRef search_path, + StringRef relative_path, const Module *imported, + SrcMgr::CharacteristicKind FileType) { + if (imported) { + serialization::ModuleFile *MF = + compiler_.getModuleManager()->getModuleManager().lookup( + imported->getASTFile()); + handleModuleFile(MF); + } + ParseToLocation(directive_loc); +} + +// FIXME: This crashes: void EndOfMainFile() override { +// target_callbacks_->EndOfMainFile(); } + +// Handle all other callbacks. +// Just parse to the corresponding location to generate the same callback for +// the target_callbacks_. +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 diff --git a/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.h b/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.h --- a/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.h +++ b/clang-tools-extra/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 { diff --git a/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp b/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp --- a/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp +++ b/clang-tools-extra/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, 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 @@ -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(); diff --git a/clang-tools-extra/test/CMakeLists.txt b/clang-tools-extra/test/CMakeLists.txt --- a/clang-tools-extra/test/CMakeLists.txt +++ b/clang-tools-extra/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) diff --git a/clang-tools-extra/test/clang-tidy/Inputs/expand-modular-headers-ppcallbacks/a.h b/clang-tools-extra/test/clang-tidy/Inputs/expand-modular-headers-ppcallbacks/a.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/Inputs/expand-modular-headers-ppcallbacks/a.h @@ -0,0 +1 @@ +#define a diff --git a/clang-tools-extra/test/clang-tidy/Inputs/expand-modular-headers-ppcallbacks/b.h b/clang-tools-extra/test/clang-tidy/Inputs/expand-modular-headers-ppcallbacks/b.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/Inputs/expand-modular-headers-ppcallbacks/b.h @@ -0,0 +1,2 @@ +#include "a.h" +#define b diff --git a/clang-tools-extra/test/clang-tidy/Inputs/expand-modular-headers-ppcallbacks/c.h b/clang-tools-extra/test/clang-tidy/Inputs/expand-modular-headers-ppcallbacks/c.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/Inputs/expand-modular-headers-ppcallbacks/c.h @@ -0,0 +1,2 @@ +#include "b.h" +#define c diff --git a/clang-tools-extra/test/clang-tidy/Inputs/expand-modular-headers-ppcallbacks/module.modulemap b/clang-tools-extra/test/clang-tidy/Inputs/expand-modular-headers-ppcallbacks/module.modulemap new file mode 100644 --- /dev/null +++ b/clang-tools-extra/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 } diff --git a/clang-tools-extra/test/clang-tidy/expand-modular-headers-ppcallbacks.cpp b/clang-tools-extra/test/clang-tidy/expand-modular-headers-ppcallbacks.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/expand-modular-headers-ppcallbacks.cpp @@ -0,0 +1,31 @@ +// 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' [readability-identifier-naming] +// 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' [readability-identifier-naming] +// CHECK-MESSAGES: c.h:2:9: note: FIX-IT applied suggested code changes