Skip to content

Commit

Permalink
[preprocessor] Add an opportunity to retain excluded conditional blocks
Browse files Browse the repository at this point in the history
It is handy for clang tooling, for instance, in source to source transformation.

Reviewers: vpykhtin (Valery Pykhtin), erichkeane (Erich Keane)

Subscribers: rsmith (Richard Smith), akyrtzi (Argyrios Kyrtzidis)

Tags: #clang

Differential Revision: https://reviews.llvm.org/D66597

llvm-svn: 370123
  • Loading branch information
emankov committed Aug 27, 2019
1 parent 1fac68b commit 2ed2e62
Show file tree
Hide file tree
Showing 8 changed files with 194 additions and 6 deletions.
7 changes: 6 additions & 1 deletion clang/include/clang-c/Index.h
Expand Up @@ -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,
};

/**
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Frontend/ASTUnit.h
Expand Up @@ -832,6 +832,7 @@ class ASTUnit {
SkipFunctionBodiesScope::None,
bool SingleFileParse = false, bool UserFilesAreVolatile = false,
bool ForSerialization = false,
bool RetainExcludedConditionalBlocks = false,
llvm::Optional<StringRef> ModuleFormat = llvm::None,
std::unique_ptr<ASTUnit> *ErrAST = nullptr,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS = nullptr);
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Lex/PreprocessorOptions.h
Expand Up @@ -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.
Expand Down Expand Up @@ -201,6 +204,7 @@ class PreprocessorOptions {
RetainRemappedFileBuffers = true;
PrecompiledPreambleBytes.first = 0;
PrecompiledPreambleBytes.second = false;
RetainExcludedConditionalBlocks = false;
}
};

Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Frontend/ASTUnit.cpp
Expand Up @@ -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<StringRef> ModuleFormat, std::unique_ptr<ASTUnit> *ErrAST,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
assert(Diags.get() && "no DiagnosticsEngine was provided");
Expand Down Expand Up @@ -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;
Expand Down
20 changes: 16 additions & 4 deletions clang/lib/Lex/PPDirectives.cpp
Expand Up @@ -2855,14 +2855,17 @@ 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
// the directive blocks.
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,
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
132 changes: 132 additions & 0 deletions clang/test/Index/retain-excluded-conditional-blocks.m
@@ -0,0 +1,132 @@
// RUN: c-index-test -retain-excluded-conditional-blocks %s | FileCheck %s

#include <stdint.h>

// 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
30 changes: 30 additions & 0 deletions clang/tools/c-index-test/c-index-test.c
Expand Up @@ -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(). */
/******************************************************************************/
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 3 additions & 1 deletion clang/tools/libclang/CIndex.cpp
Expand Up @@ -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 =
Expand Down Expand Up @@ -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));

Expand Down

0 comments on commit 2ed2e62

Please sign in to comment.