diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h index 74badac740b64..bdce233ce8b8c 100644 --- a/clang/include/clang-c/Index.h +++ b/clang/include/clang-c/Index.h @@ -1356,7 +1356,12 @@ enum CXTranslationUnit_Flags { * the case where these warnings are not of interest, as for an IDE for * example, which typically shows only the diagnostics in the main file. */ - CXTranslationUnit_IgnoreNonErrorsFromIncludedFiles = 0x4000 + CXTranslationUnit_IgnoreNonErrorsFromIncludedFiles = 0x4000, + + /** + * Tells the preprocessor not to skip excluded conditional blocks. + */ + CXTranslationUnit_RetainExcludedConditionalBlocks = 0x8000, }; /** diff --git a/clang/include/clang/Frontend/ASTUnit.h b/clang/include/clang/Frontend/ASTUnit.h index 08cc91946eb74..a36655150d4ec 100644 --- a/clang/include/clang/Frontend/ASTUnit.h +++ b/clang/include/clang/Frontend/ASTUnit.h @@ -832,6 +832,7 @@ class ASTUnit { SkipFunctionBodiesScope::None, bool SingleFileParse = false, bool UserFilesAreVolatile = false, bool ForSerialization = false, + bool RetainExcludedConditionalBlocks = false, llvm::Optional ModuleFormat = llvm::None, std::unique_ptr *ErrAST = nullptr, IntrusiveRefCntPtr VFS = nullptr); diff --git a/clang/include/clang/Lex/PreprocessorOptions.h b/clang/include/clang/Lex/PreprocessorOptions.h index 1480548c7fbe4..8824f0a51d01d 100644 --- a/clang/include/clang/Lex/PreprocessorOptions.h +++ b/clang/include/clang/Lex/PreprocessorOptions.h @@ -142,6 +142,9 @@ class PreprocessorOptions { /// compiler invocation and its buffers will be reused. bool RetainRemappedFileBuffers = false; + /// When enabled, excluded conditional blocks retain in the main file. + bool RetainExcludedConditionalBlocks = false; + /// The Objective-C++ ARC standard library that we should support, /// by providing appropriate definitions to retrofit the standard library /// with support for lifetime-qualified pointers. @@ -201,6 +204,7 @@ class PreprocessorOptions { RetainRemappedFileBuffers = true; PrecompiledPreambleBytes.first = 0; PrecompiledPreambleBytes.second = false; + RetainExcludedConditionalBlocks = false; } }; diff --git a/clang/lib/Frontend/ASTUnit.cpp b/clang/lib/Frontend/ASTUnit.cpp index ac6e6b6048e73..35348ffe13b19 100644 --- a/clang/lib/Frontend/ASTUnit.cpp +++ b/clang/lib/Frontend/ASTUnit.cpp @@ -1735,6 +1735,7 @@ ASTUnit *ASTUnit::LoadFromCommandLine( bool CacheCodeCompletionResults, bool IncludeBriefCommentsInCodeCompletion, bool AllowPCHWithCompilerErrors, SkipFunctionBodiesScope SkipFunctionBodies, bool SingleFileParse, bool UserFilesAreVolatile, bool ForSerialization, + bool RetainExcludedConditionalBlocks, llvm::Optional ModuleFormat, std::unique_ptr *ErrAST, IntrusiveRefCntPtr VFS) { assert(Diags.get() && "no DiagnosticsEngine was provided"); @@ -1762,6 +1763,7 @@ ASTUnit *ASTUnit::LoadFromCommandLine( PPOpts.RemappedFilesKeepOriginalName = RemappedFilesKeepOriginalName; PPOpts.AllowPCHWithCompilerErrors = AllowPCHWithCompilerErrors; PPOpts.SingleFileParseMode = SingleFileParse; + PPOpts.RetainExcludedConditionalBlocks = RetainExcludedConditionalBlocks; // Override the resources path. CI->getHeaderSearchOpts().ResourceDir = ResourceFilesPath; diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp index 5eac1da115c0f..525f95b96e030 100644 --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -2855,6 +2855,9 @@ void Preprocessor::HandleIfdefDirective(Token &Result, Callbacks->Ifdef(DirectiveTok.getLocation(), MacroNameTok, MD); } + bool RetainExcludedCB = PPOpts->RetainExcludedConditionalBlocks && + getSourceManager().isInMainFile(DirectiveTok.getLocation()); + // Should we include the stuff contained by this directive? if (PPOpts->SingleFileParseMode && !MI) { // In 'single-file-parse mode' undefined identifiers trigger parsing of all @@ -2862,7 +2865,7 @@ void Preprocessor::HandleIfdefDirective(Token &Result, CurPPLexer->pushConditionalLevel(DirectiveTok.getLocation(), /*wasskip*/false, /*foundnonskip*/false, /*foundelse*/false); - } else if (!MI == isIfndef) { + } else if (!MI == isIfndef || RetainExcludedCB) { // Yes, remember that we are inside a conditional, then lex the next token. CurPPLexer->pushConditionalLevel(DirectiveTok.getLocation(), /*wasskip*/false, /*foundnonskip*/true, @@ -2903,13 +2906,16 @@ void Preprocessor::HandleIfDirective(Token &IfToken, IfToken.getLocation(), DER.ExprRange, (ConditionalTrue ? PPCallbacks::CVK_True : PPCallbacks::CVK_False)); + bool RetainExcludedCB = PPOpts->RetainExcludedConditionalBlocks && + getSourceManager().isInMainFile(IfToken.getLocation()); + // Should we include the stuff contained by this directive? if (PPOpts->SingleFileParseMode && DER.IncludedUndefinedIds) { // In 'single-file-parse mode' undefined identifiers trigger parsing of all // the directive blocks. CurPPLexer->pushConditionalLevel(IfToken.getLocation(), /*wasskip*/false, /*foundnonskip*/false, /*foundelse*/false); - } else if (ConditionalTrue) { + } else if (ConditionalTrue || RetainExcludedCB) { // Yes, remember that we are inside a conditional, then lex the next token. CurPPLexer->pushConditionalLevel(IfToken.getLocation(), /*wasskip*/false, /*foundnonskip*/true, /*foundelse*/false); @@ -2971,7 +2977,10 @@ void Preprocessor::HandleElseDirective(Token &Result, const Token &HashToken) { if (Callbacks) Callbacks->Else(Result.getLocation(), CI.IfLoc); - if (PPOpts->SingleFileParseMode && !CI.FoundNonSkip) { + bool RetainExcludedCB = PPOpts->RetainExcludedConditionalBlocks && + getSourceManager().isInMainFile(Result.getLocation()); + + if ((PPOpts->SingleFileParseMode && !CI.FoundNonSkip) || RetainExcludedCB) { // In 'single-file-parse mode' undefined identifiers trigger parsing of all // the directive blocks. CurPPLexer->pushConditionalLevel(CI.IfLoc, /*wasskip*/false, @@ -3013,7 +3022,10 @@ void Preprocessor::HandleElifDirective(Token &ElifToken, Callbacks->Elif(ElifToken.getLocation(), ConditionRange, PPCallbacks::CVK_NotEvaluated, CI.IfLoc); - if (PPOpts->SingleFileParseMode && !CI.FoundNonSkip) { + bool RetainExcludedCB = PPOpts->RetainExcludedConditionalBlocks && + getSourceManager().isInMainFile(ElifToken.getLocation()); + + if ((PPOpts->SingleFileParseMode && !CI.FoundNonSkip) || RetainExcludedCB) { // In 'single-file-parse mode' undefined identifiers trigger parsing of all // the directive blocks. CurPPLexer->pushConditionalLevel(ElifToken.getLocation(), /*wasskip*/false, diff --git a/clang/test/Index/retain-excluded-conditional-blocks.m b/clang/test/Index/retain-excluded-conditional-blocks.m new file mode 100644 index 0000000000000..ebfc7c5723d5a --- /dev/null +++ b/clang/test/Index/retain-excluded-conditional-blocks.m @@ -0,0 +1,132 @@ +// RUN: c-index-test -retain-excluded-conditional-blocks %s | FileCheck %s + +#include + +// CHECK: TypedefDecl=intptr_t + +// CHECK: [[@LINE+1]]:12: ObjCInterfaceDecl=MyCls +@interface MyCls +// CHECK: [[@LINE+1]]:8: ObjCInstanceMethodDecl=some_meth +-(void)some_meth; +@end + +#if 1 +// CHECK: [[@LINE+1]]:12: ObjCInterfaceDecl=Test1 +@interface Test1 @end +#else +// CHECK: [[@LINE+1]]:12: +@interface Test2 @end +#endif + +#if 0 +// CHECK: [[@LINE+1]]:12: +@interface Test3 @end +#else +// CHECK: [[@LINE+1]]:12: ObjCInterfaceDecl=Test4 +@interface Test4 @end +#endif + +#if SOMETHING_NOT_DEFINED +// CHECK: [[@LINE+1]]:12: ObjCInterfaceDecl=Test5 +@interface Test5 @end +#else +// CHECK: [[@LINE+1]]:12: ObjCInterfaceDecl=Test6 +@interface Test6 @end +#endif + +#define SOMETHING_DEFINED 1 +#if SOMETHING_DEFINED +// CHECK: [[@LINE+1]]:12: ObjCInterfaceDecl=Test7 +@interface Test7 @end +#else +// CHECK: [[@LINE+1]]:12: +@interface Test8 @end +#endif + +#if defined(SOMETHING_NOT_DEFINED) +// CHECK: [[@LINE+1]]:12: ObjCInterfaceDecl=Test9 +@interface Test9 @end +#else +// CHECK: [[@LINE+1]]:12: ObjCInterfaceDecl=Test10 +@interface Test10 @end +#endif + +#if defined(SOMETHING_DEFINED) +// CHECK: [[@LINE+1]]:12: ObjCInterfaceDecl=Test11 +@interface Test11 @end +#else +// CHECK: [[@LINE+1]]:12: +@interface Test12 @end +#endif + +#if SOMETHING_NOT_DEFINED1 +// CHECK: [[@LINE+1]]:12: ObjCInterfaceDecl=Test13 +@interface Test13 @end +#elif SOMETHING_NOT_DEFINED2 +// CHECK: [[@LINE+1]]:12: ObjCInterfaceDecl=Test14 +@interface Test14 @end +#else +// CHECK: [[@LINE+1]]:12: ObjCInterfaceDecl=Test15 +@interface Test15 @end +#endif + +#ifdef SOMETHING_NOT_DEFINED +// CHECK: [[@LINE+1]]:12: ObjCInterfaceDecl=Test19 +@interface Test19 @end +#else +// CHECK: [[@LINE+1]]:12: ObjCInterfaceDecl=Test20 +@interface Test20 @end +#endif + +#ifdef SOMETHING_DEFINED +// CHECK: [[@LINE+1]]:12: ObjCInterfaceDecl=Test21 +@interface Test21 @end +#else +// CHECK: [[@LINE+1]]:12: +@interface Test22 @end +#endif + +#ifndef SOMETHING_NOT_DEFINED +// CHECK: [[@LINE+1]]:12: ObjCInterfaceDecl=Test23 +@interface Test23 @end +#else +// CHECK: [[@LINE+1]]:12: ObjCInterfaceDecl=Test24 +@interface Test24 @end +#endif + +#ifndef SOMETHING_DEFINED +// CHECK: [[@LINE+1]]:12: +@interface Test25 @end +#else +// CHECK: [[@LINE+1]]:12: ObjCInterfaceDecl=Test26 +@interface Test26 @end +#endif + +#if 1 < SOMETHING_NOT_DEFINED +// CHECK: [[@LINE+1]]:12: ObjCInterfaceDecl=Test27 +@interface Test27 @end +#else +// CHECK: [[@LINE+1]]:12: ObjCInterfaceDecl=Test28 +@interface Test28 @end +#endif + +#if SOMETHING_NOT_DEFINED +// CHECK: [[@LINE+1]]:12: ObjCInterfaceDecl=Test29 +@interface Test29 @end +#endif + +#ifdef SOMETHING_NOT_DEFINED +// CHECK: [[@LINE+1]]:12: ObjCInterfaceDecl=Test30 +@interface Test30 @end +#endif + +#ifdef SOMETHING_DEFINED +// CHECK: [[@LINE+1]]:12: ObjCInterfaceDecl=Test31 +@interface Test31 @end +#elif !defined(SOMETHING_NOT_DEFINED) +// CHECK: [[@LINE+1]]:12: ObjCInterfaceDecl=Test32 +@interface Test32 @end +#else +// CHECK: [[@LINE+1]]:12: ObjCInterfaceDecl=Test33 +@interface Test33 @end +#endif diff --git a/clang/tools/c-index-test/c-index-test.c b/clang/tools/c-index-test/c-index-test.c index 33751714b8458..dc7e449a20712 100644 --- a/clang/tools/c-index-test/c-index-test.c +++ b/clang/tools/c-index-test/c-index-test.c @@ -2181,6 +2181,34 @@ static int perform_single_file_parse(const char *filename) { return result; } +static int perform_file_retain_excluded_cb(const char *filename) { + CXIndex Idx; + CXTranslationUnit TU; + enum CXErrorCode Err; + int result; + + Idx = clang_createIndex(/* excludeDeclsFromPCH */1, + /* displayDiagnostics=*/1); + + Err = clang_parseTranslationUnit2(Idx, filename, + /*command_line_args=*/NULL, + /*num_command_line_args=*/0, + /*unsaved_files=*/NULL, + /*num_unsaved_files=*/0, + CXTranslationUnit_RetainExcludedConditionalBlocks, &TU); + if (Err != CXError_Success) { + fprintf(stderr, "Unable to load translation unit!\n"); + describeLibclangFailure(Err); + clang_disposeIndex(Idx); + return 1; + } + + result = perform_test_load(Idx, TU, /*filter=*/"all", /*prefix=*/NULL, FilteredPrintingVisitor, /*PostVisit=*/NULL, + /*CommentSchemaFile=*/NULL); + clang_disposeIndex(Idx); + return result; +} + /******************************************************************************/ /* Logic for testing clang_getCursor(). */ /******************************************************************************/ @@ -4849,6 +4877,8 @@ int cindextest_main(int argc, const char **argv) { } else if (argc >= 3 && strcmp(argv[1], "-single-file-parse") == 0) return perform_single_file_parse(argv[2]); + else if (argc >= 3 && strcmp(argv[1], "-retain-excluded-conditional-blocks") == 0) + return perform_file_retain_excluded_cb(argv[2]); else if (argc >= 4 && strcmp(argv[1], "-test-file-scan") == 0) return perform_file_scan(argv[2], argv[3], argc >= 5 ? argv[4] : 0); diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 344d6f36d1f5c..61b60e2bf881f 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -3417,6 +3417,8 @@ clang_parseTranslationUnit_Impl(CXIndex CIdx, const char *source_filename, = options & CXTranslationUnit_IncludeBriefCommentsInCodeCompletion; bool SingleFileParse = options & CXTranslationUnit_SingleFileParse; bool ForSerialization = options & CXTranslationUnit_ForSerialization; + bool RetainExcludedCB = options & + CXTranslationUnit_RetainExcludedConditionalBlocks; SkipFunctionBodiesScope SkipFunctionBodies = SkipFunctionBodiesScope::None; if (options & CXTranslationUnit_SkipFunctionBodies) { SkipFunctionBodies = @@ -3517,7 +3519,7 @@ clang_parseTranslationUnit_Impl(CXIndex CIdx, const char *source_filename, /*RemappedFilesKeepOriginalName=*/true, PrecompilePreambleAfterNParses, TUKind, CacheCodeCompletionResults, IncludeBriefCommentsInCodeCompletion, /*AllowPCHWithCompilerErrors=*/true, SkipFunctionBodies, SingleFileParse, - /*UserFilesAreVolatile=*/true, ForSerialization, + /*UserFilesAreVolatile=*/true, ForSerialization, RetainExcludedCB, CXXIdx->getPCHContainerOperations()->getRawReader().getFormat(), &ErrUnit));