From 998caead7041603154c67f23e65744bdfc88c479 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Fri, 6 May 2011 16:33:08 +0000 Subject: [PATCH] Introduce a new libclang parsing flag, CXTranslationUnit_NestedMacroInstantiations, which indicates whether we want to see "nested" macro instantiations (e.g., those that occur inside other macro instantiations) within the detailed preprocessing record. Many clients (e.g., those that only care about visible tokens) don't care about this information, and in code that uses preprocessor metaprogramming, this information can have a very high cost. Addresses . llvm-svn: 130990 --- clang/include/clang-c/Index.h | 13 ++++++++++- clang/include/clang/Frontend/ASTUnit.h | 10 ++++++-- .../clang/Frontend/PreprocessorOptions.h | 5 ++++ clang/include/clang/Lex/PreprocessingRecord.h | 7 +++++- clang/include/clang/Lex/Preprocessor.h | 2 +- clang/lib/Frontend/ASTUnit.cpp | 14 +++++++++-- clang/lib/Frontend/CompilerInstance.cpp | 3 ++- clang/lib/Lex/PreprocessingRecord.cpp | 8 +++++-- clang/lib/Lex/Preprocessor.cpp | 5 ++-- clang/lib/Serialization/ASTReader.cpp | 4 ++-- .../Index/nested-macro-instantiations.cpp | 20 ++++++++++++++++ clang/tools/c-index-test/c-index-test.c | 23 +++++++++++-------- clang/tools/libclang/CIndex.cpp | 10 ++++++-- 13 files changed, 98 insertions(+), 26 deletions(-) create mode 100644 clang/test/Index/nested-macro-instantiations.cpp diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h index 011999bfe921..97c0c7c0daa1 100644 --- a/clang/include/clang-c/Index.h +++ b/clang/include/clang-c/Index.h @@ -829,7 +829,18 @@ enum CXTranslationUnit_Flags { * Note: this is a *temporary* option that is available only while * we are testing C++ precompiled preamble support. */ - CXTranslationUnit_CXXChainedPCH = 0x20 + CXTranslationUnit_CXXChainedPCH = 0x20, + + /** + * \brief Used to indicate that the "detailed" preprocessing record, + * if requested, should also contain nested macro instantiations. + * + * Nested macro instantiations (i.e., macro instantiations that occur + * inside another macro instantiation) can, in some code bases, require + * a large amount of storage to due preprocessor metaprogramming. Moreover, + * its fairly rare that this information is useful for libclang clients. + */ + CXTranslationUnit_NestedMacroInstantiations = 0x40 }; /** diff --git a/clang/include/clang/Frontend/ASTUnit.h b/clang/include/clang/Frontend/ASTUnit.h index 57d9f712aa18..339297eb9362 100644 --- a/clang/include/clang/Frontend/ASTUnit.h +++ b/clang/include/clang/Frontend/ASTUnit.h @@ -249,6 +249,10 @@ private: /// \brief Whether we should be caching code-completion results. bool ShouldCacheCodeCompletionResults; + /// \brief Whether we want to include nested macro instantiations in the + /// detailed preprocessing record. + bool NestedMacroInstantiations; + static void ConfigureDiags(llvm::IntrusiveRefCntPtr &Diags, const char **ArgBegin, const char **ArgEnd, ASTUnit &AST, bool CaptureDiagnostics); @@ -607,7 +611,8 @@ public: bool CaptureDiagnostics = false, bool PrecompilePreamble = false, bool CompleteTranslationUnit = true, - bool CacheCodeCompletionResults = false); + bool CacheCodeCompletionResults = false, + bool NestedMacroInstantiations = true); /// LoadFromCommandLine - Create an ASTUnit from a vector of command line /// arguments, which must specify exactly one source file. @@ -636,7 +641,8 @@ public: bool CompleteTranslationUnit = true, bool CacheCodeCompletionResults = false, bool CXXPrecompilePreamble = false, - bool CXXChainedPCH = false); + bool CXXChainedPCH = false, + bool NestedMacroInstantiations = true); /// \brief Reparse the source files using the same command-line options that /// were originally used to produce this translation unit. diff --git a/clang/include/clang/Frontend/PreprocessorOptions.h b/clang/include/clang/Frontend/PreprocessorOptions.h index e875ec1fefd2..e471c5cf1d82 100644 --- a/clang/include/clang/Frontend/PreprocessorOptions.h +++ b/clang/include/clang/Frontend/PreprocessorOptions.h @@ -41,6 +41,10 @@ public: /// record of all macro definitions and /// instantiations. + /// \brief Whether the detailed preprocessing record includes nested macro + /// instantiations. + unsigned DetailedRecordIncludesNestedMacroInstantiations : 1; + /// The implicit PCH included at the start of the translation unit, or empty. std::string ImplicitPCHInclude; @@ -136,6 +140,7 @@ public: public: PreprocessorOptions() : UsePredefines(true), DetailedRecord(false), + DetailedRecordIncludesNestedMacroInstantiations(true), DisablePCHValidation(false), DisableStatCache(false), DumpDeserializedPCHDecls(false), PrecompiledPreambleBytes(0, true), diff --git a/clang/include/clang/Lex/PreprocessingRecord.h b/clang/include/clang/Lex/PreprocessingRecord.h index d1534496b3d3..e498e9d0a020 100644 --- a/clang/include/clang/Lex/PreprocessingRecord.h +++ b/clang/include/clang/Lex/PreprocessingRecord.h @@ -258,6 +258,10 @@ namespace clang { /// including the various preprocessing directives processed, macros /// instantiated, etc. class PreprocessingRecord : public PPCallbacks { + /// \brief Whether we should include nested macro instantiations in + /// the preprocessing record. + bool IncludeNestedMacroInstantiations; + /// \brief Allocator used to store preprocessing objects. llvm::BumpPtrAllocator BumpAlloc; @@ -281,7 +285,8 @@ namespace clang { void MaybeLoadPreallocatedEntities() const ; public: - PreprocessingRecord(); + /// \brief Construct + explicit PreprocessingRecord(bool IncludeNestedMacroInstantiations); /// \brief Allocate memory in the preprocessing record. void *Allocate(unsigned Size, unsigned Align = 8) { diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index 616507a914a8..817fab57d339 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -441,7 +441,7 @@ public: /// \brief Create a new preprocessing record, which will keep track of /// all macro expansions, macro definitions, etc. - void createPreprocessingRecord(); + void createPreprocessingRecord(bool IncludeNestedMacroInstantiations); /// EnterMainSourceFile - Enter the specified FileID as the main source file, /// which implicitly adds the builtin defines etc. diff --git a/clang/lib/Frontend/ASTUnit.cpp b/clang/lib/Frontend/ASTUnit.cpp index 0d0fecdcf447..8827116f2e40 100644 --- a/clang/lib/Frontend/ASTUnit.cpp +++ b/clang/lib/Frontend/ASTUnit.cpp @@ -102,6 +102,7 @@ ASTUnit::ASTUnit(bool _MainFileIsAST) ConcurrencyCheckValue(CheckUnlocked), PreambleRebuildCounter(0), SavedMainFileBuffer(0), PreambleBuffer(0), ShouldCacheCodeCompletionResults(false), + NestedMacroInstantiations(true), CompletionCacheTopLevelHashValue(0), PreambleTopLevelHashValue(0), CurrentTopLevelHashValue(0), @@ -927,6 +928,8 @@ bool ASTUnit::Parse(llvm::MemoryBuffer *OverrideMainBuffer) { // If the main file has been overridden due to the use of a preamble, // make that override happen and introduce the preamble. PreprocessorOptions &PreprocessorOpts = Clang->getPreprocessorOpts(); + PreprocessorOpts.DetailedRecordIncludesNestedMacroInstantiations + = NestedMacroInstantiations; std::string PriorImplicitPCHInclude; if (OverrideMainBuffer) { PreprocessorOpts.addRemappedFile(OriginalSourceFile, OverrideMainBuffer); @@ -1712,7 +1715,8 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI, bool CaptureDiagnostics, bool PrecompilePreamble, bool CompleteTranslationUnit, - bool CacheCodeCompletionResults) { + bool CacheCodeCompletionResults, + bool NestedMacroInstantiations) { // Create the AST unit. llvm::OwningPtr AST; AST.reset(new ASTUnit(false)); @@ -1723,6 +1727,7 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI, AST->CompleteTranslationUnit = CompleteTranslationUnit; AST->ShouldCacheCodeCompletionResults = CacheCodeCompletionResults; AST->Invocation = CI; + AST->NestedMacroInstantiations = NestedMacroInstantiations; // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar @@ -1747,7 +1752,8 @@ ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin, bool CompleteTranslationUnit, bool CacheCodeCompletionResults, bool CXXPrecompilePreamble, - bool CXXChainedPCH) { + bool CXXChainedPCH, + bool NestedMacroInstantiations) { if (!Diags.getPtr()) { // No diagnostics engine was provided, so create our own diagnostics object // with the default options. @@ -1814,6 +1820,7 @@ ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin, AST->NumStoredDiagnosticsInPreamble = StoredDiagnostics.size(); AST->StoredDiagnostics.swap(StoredDiagnostics); AST->Invocation = CI; + AST->NestedMacroInstantiations = NestedMacroInstantiations; // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar @@ -2265,6 +2272,9 @@ void ASTUnit::CodeComplete(llvm::StringRef File, unsigned Line, unsigned Column, PreprocessorOpts.PrecompiledPreambleBytes.second = false; } + // Disable the preprocessing record + PreprocessorOpts.DetailedRecord = false; + llvm::OwningPtr Act; Act.reset(new SyntaxOnlyAction); if (Act->BeginSourceFile(*Clang.get(), Clang->getFrontendOpts().Inputs[0].second, diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index ace3c5adb100..38fcfe3c4711 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -227,7 +227,8 @@ CompilerInstance::createPreprocessor(Diagnostic &Diags, } if (PPOpts.DetailedRecord) - PP->createPreprocessingRecord(); + PP->createPreprocessingRecord( + PPOpts.DetailedRecordIncludesNestedMacroInstantiations); InitializePreprocessor(*PP, PPOpts, HSOpts, FEOpts); diff --git a/clang/lib/Lex/PreprocessingRecord.cpp b/clang/lib/Lex/PreprocessingRecord.cpp index 9555611dc59d..0c8d948ce0a7 100644 --- a/clang/lib/Lex/PreprocessingRecord.cpp +++ b/clang/lib/Lex/PreprocessingRecord.cpp @@ -45,8 +45,9 @@ void PreprocessingRecord::MaybeLoadPreallocatedEntities() const { ExternalSource->ReadPreprocessedEntities(); } -PreprocessingRecord::PreprocessingRecord() - : ExternalSource(0), NumPreallocatedEntities(0), +PreprocessingRecord::PreprocessingRecord(bool IncludeNestedMacroInstantiations) + : IncludeNestedMacroInstantiations(IncludeNestedMacroInstantiations), + ExternalSource(0), NumPreallocatedEntities(0), LoadedPreallocatedEntities(false) { } @@ -120,6 +121,9 @@ MacroDefinition *PreprocessingRecord::findMacroDefinition(const MacroInfo *MI) { } void PreprocessingRecord::MacroExpands(const Token &Id, const MacroInfo* MI) { + if (!IncludeNestedMacroInstantiations && Id.getLocation().isMacroID()) + return; + if (MacroDefinition *Def = findMacroDefinition(MI)) PreprocessedEntities.push_back( new (*this) MacroInstantiation(Id.getIdentifierInfo(), diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp index 31fd667a651d..fdc18f878180 100644 --- a/clang/lib/Lex/Preprocessor.cpp +++ b/clang/lib/Lex/Preprocessor.cpp @@ -527,10 +527,11 @@ CommentHandler::~CommentHandler() { } CodeCompletionHandler::~CodeCompletionHandler() { } -void Preprocessor::createPreprocessingRecord() { +void Preprocessor::createPreprocessingRecord( + bool IncludeNestedMacroInstantiations) { if (Record) return; - Record = new PreprocessingRecord; + Record = new PreprocessingRecord(IncludeNestedMacroInstantiations); addPPCallbacks(Record); } diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index bad18ff02e2e..bdc46f590fa1 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -2382,7 +2382,7 @@ ASTReader::ASTReadResult ASTReader::ReadAST(const std::string &FileName, PP->getHeaderSearchInfo().SetExternalLookup(this); if (TotalNumPreallocatedPreprocessingEntities > 0) { if (!PP->getPreprocessingRecord()) - PP->createPreprocessingRecord(); + PP->createPreprocessingRecord(true); PP->getPreprocessingRecord()->SetExternalSource(*this, TotalNumPreallocatedPreprocessingEntities); } @@ -2581,7 +2581,7 @@ void ASTReader::setPreprocessor(Preprocessor &pp) { TotalNum += Chain[I]->NumPreallocatedPreprocessingEntities; if (TotalNum) { if (!PP->getPreprocessingRecord()) - PP->createPreprocessingRecord(); + PP->createPreprocessingRecord(true); PP->getPreprocessingRecord()->SetExternalSource(*this, TotalNum); } } diff --git a/clang/test/Index/nested-macro-instantiations.cpp b/clang/test/Index/nested-macro-instantiations.cpp new file mode 100644 index 000000000000..d0c9bbb1e763 --- /dev/null +++ b/clang/test/Index/nested-macro-instantiations.cpp @@ -0,0 +1,20 @@ +#define FOO(x) x +#define BAR(x) FOO(x) +#define WIBBLE(x) BAR(x) + +WIBBLE(int x); + +// RUN: env CINDEXTEST_NESTED_MACROS=1 c-index-test -test-load-source all %s | FileCheck -check-prefix CHECK-WITH-NESTED %s +// RUN: env CINDEXTEST_EDITING=1 CINDEXTEST_NESTED_MACROS=1 c-index-test -test-load-source all %s | FileCheck -check-prefix CHECK-WITH-NESTED %s +// RUN: env CINDEXTEST_EDITING=1 CINDEXTEST_NESTED_MACROS=1 c-index-test -test-load-source-reparse 5 all %s | FileCheck -check-prefix CHECK-WITH-NESTED %s +// CHECK-WITH-NESTED: nested-macro-instantiations.cpp:5:1: macro instantiation=WIBBLE:3:9 Extent=[5:1 - 5:7] +// CHECK-WITH-NESTED: nested-macro-instantiations.cpp:3:19: macro instantiation=BAR:2:9 Extent=[3:19 - 5:14] +// CHECK-WITH-NESTED: nested-macro-instantiations.cpp:2:16: macro instantiation=FOO:1:9 Extent=[2:16 - 5:14] +// CHECK-WITH-NESTED: nested-macro-instantiations.cpp:5:1: VarDecl=x:5:1 (Definition) Extent=[5:1 - 5:14] + +// RUN: c-index-test -test-load-source all %s | FileCheck -check-prefix CHECK-WITHOUT-NESTED %s +// RUN: env CINDEXTEST_EDITING=1 c-index-test -test-load-source all %s | FileCheck -check-prefix CHECK-WITHOUT-NESTED %s +// RUN: env CINDEXTEST_EDITING=1 c-index-test -test-load-source-reparse 5 all %s | FileCheck -check-prefix CHECK-WITHOUT-NESTED %s +// CHECK-WITHOUT-NESTED: nested-macro-instantiations.cpp:5:1: macro instantiation=WIBBLE:3:9 Extent=[5:1 - 5:7] +// CHECK-WITHOUT-NESTED-NOT: nested-macro-instantiations.cpp:3:19: macro instantiation=BAR:2:9 Extent=[3:19 - 5:14] +// CHECK-WITHOUT-NESTED: nested-macro-instantiations.cpp:5:1: VarDecl=x:5:1 (Definition) Extent=[5:1 - 5:14] diff --git a/clang/tools/c-index-test/c-index-test.c b/clang/tools/c-index-test/c-index-test.c index e43ac0699f2e..a5d4909e63c8 100644 --- a/clang/tools/c-index-test/c-index-test.c +++ b/clang/tools/c-index-test/c-index-test.c @@ -37,6 +37,8 @@ static unsigned getDefaultParsingOptions() { options |= clang_defaultEditingTranslationUnitOptions(); if (getenv("CINDEXTEST_COMPLETION_CACHING")) options |= CXTranslationUnit_CacheCompletionResults; + if (getenv("CINDEXTEST_NESTED_MACROS")) + options |= CXTranslationUnit_NestedMacroInstantiations; return options; } @@ -713,11 +715,11 @@ int perform_test_load_source(int argc, const char **argv, return -1; } - TU = clang_createTranslationUnitFromSourceFile(Idx, 0, - argc - num_unsaved_files, - argv + num_unsaved_files, - num_unsaved_files, - unsaved_files); + TU = clang_parseTranslationUnit(Idx, 0, + argv + num_unsaved_files, + argc - num_unsaved_files, + unsaved_files, num_unsaved_files, + getDefaultParsingOptions()); if (!TU) { fprintf(stderr, "Unable to load translation unit!\n"); free_remapped_files(unsaved_files, num_unsaved_files); @@ -1231,11 +1233,12 @@ int perform_token_annotation(int argc, const char **argv) { return -1; CIdx = clang_createIndex(0, 1); - TU = clang_createTranslationUnitFromSourceFile(CIdx, argv[argc - 1], - argc - num_unsaved_files - 3, - argv + num_unsaved_files + 2, - num_unsaved_files, - unsaved_files); + TU = clang_parseTranslationUnit(CIdx, argv[argc - 1], + argv + num_unsaved_files + 2, + argc - num_unsaved_files - 3, + unsaved_files, + num_unsaved_files, + getDefaultParsingOptions()); if (!TU) { fprintf(stderr, "unable to parse input\n"); clang_disposeIndex(CIdx); diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 7234da3f6383..f17a415f44b2 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -2376,10 +2376,12 @@ clang_createTranslationUnitFromSourceFile(CXIndex CIdx, const char * const *command_line_args, unsigned num_unsaved_files, struct CXUnsavedFile *unsaved_files) { + unsigned Options = CXTranslationUnit_DetailedPreprocessingRecord | + CXTranslationUnit_NestedMacroInstantiations; return clang_parseTranslationUnit(CIdx, source_filename, command_line_args, num_command_line_args, unsaved_files, num_unsaved_files, - CXTranslationUnit_DetailedPreprocessingRecord); + Options); } struct ParseTranslationUnitInfo { @@ -2480,9 +2482,12 @@ static void clang_parseTranslationUnit_Impl(void *UserData) { Args->push_back(source_filename); // Do we need the detailed preprocessing record? + bool NestedMacroInstantiations = false; if (options & CXTranslationUnit_DetailedPreprocessingRecord) { Args->push_back("-Xclang"); Args->push_back("-detailed-preprocessing-record"); + NestedMacroInstantiations + = (options & CXTranslationUnit_NestedMacroInstantiations); } unsigned NumErrors = Diags->getClient()->getNumErrors(); @@ -2501,7 +2506,8 @@ static void clang_parseTranslationUnit_Impl(void *UserData) { CompleteTranslationUnit, CacheCodeCompetionResults, CXXPrecompilePreamble, - CXXChainedPCH)); + CXXChainedPCH, + NestedMacroInstantiations)); if (NumErrors != Diags->getClient()->getNumErrors()) { // Make sure to check that 'Unit' is non-NULL.