Index: include/clang/Lex/Preprocessor.h =================================================================== --- include/clang/Lex/Preprocessor.h +++ include/clang/Lex/Preprocessor.h @@ -1835,11 +1835,16 @@ /// \brief A fast PTH version of SkipExcludedConditionalBlock. void PTHSkipExcludedConditionalBlock(); + struct DirectiveEvalResult { + bool Conditional; + bool IncludedUndefinedIds; + }; + /// \brief Evaluate an integer constant expression that may occur after a /// \#if or \#elif directive and return it as a bool. /// /// If the expression is equivalent to "!defined(X)" return X in IfNDefMacro. - bool EvaluateDirectiveExpression(IdentifierInfo *&IfNDefMacro); + DirectiveEvalResult EvaluateDirectiveExpression(IdentifierInfo *&IfNDefMacro); /// \brief Install the standard preprocessor pragmas: /// \#pragma GCC poison/system_header/dependency and \#pragma once. Index: include/clang/Lex/PreprocessorOptions.h =================================================================== --- include/clang/Lex/PreprocessorOptions.h +++ include/clang/Lex/PreprocessorOptions.h @@ -96,6 +96,10 @@ std::string TokenCache; /// When enabled, preprocessor is in a mode for parsing a single file only. + /// + /// Disables #includes of other files and if there are unresolved identifiers + /// in preprocessor directive conditions it causes all blocks to be parsed so + /// that the client can get the max amount of info from the parser. bool SingleFileParseMode = false; /// \brief True if the SourceManager should report the original file name for Index: lib/Lex/PPDirectives.cpp =================================================================== --- lib/Lex/PPDirectives.cpp +++ lib/Lex/PPDirectives.cpp @@ -538,7 +538,7 @@ assert(CurPPLexer->LexingRawMode && "We have to be skipping here!"); CurPPLexer->LexingRawMode = false; IdentifierInfo *IfNDefMacro = nullptr; - const bool CondValue = EvaluateDirectiveExpression(IfNDefMacro); + const bool CondValue = EvaluateDirectiveExpression(IfNDefMacro).Conditional; CurPPLexer->LexingRawMode = true; if (Callbacks) { const SourceLocation CondEnd = CurPPLexer->getSourceLocation(); @@ -635,7 +635,7 @@ // Evaluate the condition of the #elif. IdentifierInfo *IfNDefMacro = nullptr; CurPTHLexer->ParsingPreprocessorDirective = true; - bool ShouldEnter = EvaluateDirectiveExpression(IfNDefMacro); + bool ShouldEnter = EvaluateDirectiveExpression(IfNDefMacro).Conditional; CurPTHLexer->ParsingPreprocessorDirective = false; // If this condition is true, enter it! @@ -2654,7 +2654,13 @@ } // Should we include the stuff contained by this directive? - if (!MI == isIfndef) { + if (PPOpts->SingleFileParseMode && !MI) { + // In 'single-file-parse mode' undefined identifiers trigger parsing of all + // the directive blocks. + CurPPLexer->pushConditionalLevel(DirectiveTok.getLocation(), + /*wasskip*/true, /*foundnonskip*/false, + /*foundelse*/false); + } else if (!MI == isIfndef) { // Yes, remember that we are inside a conditional, then lex the next token. CurPPLexer->pushConditionalLevel(DirectiveTok.getLocation(), /*wasskip*/false, /*foundnonskip*/true, @@ -2676,7 +2682,8 @@ // Parse and evaluate the conditional expression. IdentifierInfo *IfNDefMacro = nullptr; const SourceLocation ConditionalBegin = CurPPLexer->getSourceLocation(); - const bool ConditionalTrue = EvaluateDirectiveExpression(IfNDefMacro); + const DirectiveEvalResult DER = EvaluateDirectiveExpression(IfNDefMacro); + const bool ConditionalTrue = DER.Conditional; const SourceLocation ConditionalEnd = CurPPLexer->getSourceLocation(); // If this condition is equivalent to #ifndef X, and if this is the first @@ -2695,7 +2702,12 @@ (ConditionalTrue ? PPCallbacks::CVK_True : PPCallbacks::CVK_False)); // Should we include the stuff contained by this directive? - if (ConditionalTrue) { + if (PPOpts->SingleFileParseMode && DER.IncludedUndefinedIds) { + // In 'single-file-parse mode' undefined identifiers trigger parsing of all + // the directive blocks. + CurPPLexer->pushConditionalLevel(IfToken.getLocation(), /*wasskip*/true, + /*foundnonskip*/false, /*foundelse*/false); + } else if (ConditionalTrue) { // Yes, remember that we are inside a conditional, then lex the next token. CurPPLexer->pushConditionalLevel(IfToken.getLocation(), /*wasskip*/false, /*foundnonskip*/true, /*foundelse*/false); @@ -2756,6 +2768,14 @@ if (Callbacks) Callbacks->Else(Result.getLocation(), CI.IfLoc); + if (PPOpts->SingleFileParseMode && CI.WasSkipping) { + // In 'single-file-parse mode' undefined identifiers trigger parsing of all + // the directive blocks. + CurPPLexer->pushConditionalLevel(CI.IfLoc, /*wasskip*/false, + /*foundnonskip*/true, /*foundelse*/true); + return; + } + // Finally, skip the rest of the contents of this block. SkipExcludedConditionalBlock(CI.IfLoc, /*Foundnonskip*/true, /*FoundElse*/true, Result.getLocation()); @@ -2791,6 +2811,14 @@ SourceRange(ConditionalBegin, ConditionalEnd), PPCallbacks::CVK_NotEvaluated, CI.IfLoc); + if (PPOpts->SingleFileParseMode && CI.WasSkipping) { + // In 'single-file-parse mode' undefined identifiers trigger parsing of all + // the directive blocks. + CurPPLexer->pushConditionalLevel(ElifToken.getLocation(), /*wasskip*/true, + /*foundnonskip*/false, /*foundelse*/false); + return; + } + // Finally, skip the rest of the contents of this block. SkipExcludedConditionalBlock(CI.IfLoc, /*Foundnonskip*/true, /*FoundElse*/CI.FoundElse, Index: lib/Lex/PPExpressions.cpp =================================================================== --- lib/Lex/PPExpressions.cpp +++ lib/Lex/PPExpressions.cpp @@ -73,6 +73,7 @@ static bool EvaluateDirectiveSubExpr(PPValue &LHS, unsigned MinPrec, Token &PeekTok, bool ValueLive, + bool &IncludedUndefinedIds, Preprocessor &PP); /// DefinedTracker - This struct is used while parsing expressions to keep track @@ -93,6 +94,7 @@ /// TheMacro - When the state is DefinedMacro or NotDefinedMacro, this /// indicates the macro that was checked. IdentifierInfo *TheMacro; + bool IncludedUndefinedIds = false; }; /// EvaluateDefined - Process a 'defined(sym)' expression. @@ -128,6 +130,7 @@ MacroDefinition Macro = PP.getMacroDefinition(II); Result.Val = !!Macro; Result.Val.setIsUnsigned(false); // Result is signed intmax_t. + DT.IncludedUndefinedIds = !Macro; // If there is a macro, mark it used. if (Result.Val != 0 && ValueLive) @@ -255,6 +258,8 @@ Result.Val.setIsUnsigned(false); // "0" is signed intmax_t 0. Result.setIdentifier(II); Result.setRange(PeekTok.getLocation()); + DT.IncludedUndefinedIds = (II->getTokenID() != tok::kw_true && + II->getTokenID() != tok::kw_false); PP.LexNonComment(PeekTok); return false; } @@ -400,7 +405,8 @@ // Just use DT unmodified as our result. } else { // Otherwise, we have something like (x+y), and we consumed '(x'. - if (EvaluateDirectiveSubExpr(Result, 1, PeekTok, ValueLive, PP)) + if (EvaluateDirectiveSubExpr(Result, 1, PeekTok, ValueLive, + DT.IncludedUndefinedIds, PP)) return true; if (PeekTok.isNot(tok::r_paren)) { @@ -532,6 +538,7 @@ /// evaluation, such as division by zero warnings. static bool EvaluateDirectiveSubExpr(PPValue &LHS, unsigned MinPrec, Token &PeekTok, bool ValueLive, + bool &IncludedUndefinedIds, Preprocessor &PP) { unsigned PeekPrec = getPrecedence(PeekTok.getKind()); // If this token isn't valid, report the error. @@ -571,6 +578,7 @@ // Parse the RHS of the operator. DefinedTracker DT; if (EvaluateValue(RHS, PeekTok, DT, RHSIsLive, PP)) return true; + IncludedUndefinedIds = DT.IncludedUndefinedIds; // Remember the precedence of this operator and get the precedence of the // operator immediately to the right of the RHS. @@ -601,7 +609,8 @@ RHSPrec = ThisPrec+1; if (PeekPrec >= RHSPrec) { - if (EvaluateDirectiveSubExpr(RHS, RHSPrec, PeekTok, RHSIsLive, PP)) + if (EvaluateDirectiveSubExpr(RHS, RHSPrec, PeekTok, RHSIsLive, + IncludedUndefinedIds, PP)) return true; PeekPrec = getPrecedence(PeekTok.getKind()); } @@ -769,7 +778,8 @@ // Parse anything after the : with the same precedence as ?. We allow // things of equal precedence because ?: is right associative. if (EvaluateDirectiveSubExpr(AfterColonVal, ThisPrec, - PeekTok, AfterColonLive, PP)) + PeekTok, AfterColonLive, + IncludedUndefinedIds, PP)) return true; // Now that we have the condition, the LHS and the RHS of the :, evaluate. @@ -806,7 +816,8 @@ /// EvaluateDirectiveExpression - Evaluate an integer constant expression that /// may occur after a #if or #elif directive. If the expression is equivalent /// to "!defined(X)" return X in IfNDefMacro. -bool Preprocessor::EvaluateDirectiveExpression(IdentifierInfo *&IfNDefMacro) { +Preprocessor::DirectiveEvalResult +Preprocessor::EvaluateDirectiveExpression(IdentifierInfo *&IfNDefMacro) { SaveAndRestore PPDir(ParsingIfOrElifDirective, true); // Save the current state of 'DisableMacroExpansion' and reset it to false. If // 'DisableMacroExpansion' is true, then we must be in a macro argument list @@ -833,7 +844,7 @@ // Restore 'DisableMacroExpansion'. DisableMacroExpansion = DisableMacroExpansionAtStartOfDirective; - return false; + return {false, DT.IncludedUndefinedIds}; } // If we are at the end of the expression after just parsing a value, there @@ -847,20 +858,20 @@ // Restore 'DisableMacroExpansion'. DisableMacroExpansion = DisableMacroExpansionAtStartOfDirective; - return ResVal.Val != 0; + return {ResVal.Val != 0, DT.IncludedUndefinedIds}; } // Otherwise, we must have a binary operator (e.g. "#if 1 < 2"), so parse the // operator and the stuff after it. if (EvaluateDirectiveSubExpr(ResVal, getPrecedence(tok::question), - Tok, true, *this)) { + Tok, true, DT.IncludedUndefinedIds, *this)) { // Parse error, skip the rest of the macro line. if (Tok.isNot(tok::eod)) DiscardUntilEndOfDirective(); // Restore 'DisableMacroExpansion'. DisableMacroExpansion = DisableMacroExpansionAtStartOfDirective; - return false; + return {false, DT.IncludedUndefinedIds}; } // If we aren't at the tok::eod token, something bad happened, like an extra @@ -872,5 +883,5 @@ // Restore 'DisableMacroExpansion'. DisableMacroExpansion = DisableMacroExpansionAtStartOfDirective; - return ResVal.Val != 0; + return {ResVal.Val != 0, DT.IncludedUndefinedIds}; } Index: test/Index/singe-file-parse.m =================================================================== --- test/Index/singe-file-parse.m +++ test/Index/singe-file-parse.m @@ -9,3 +9,103 @@ // CHECK: [[@LINE+1]]:8: ObjCInstanceMethodDecl=some_meth -(void)some_meth; @end + +#if 1 +// CHECK: [[@LINE+1]]:12: ObjCInterfaceDecl=Test1 +@interface Test1 @end +#else +// CHECK-NOT: [[@LINE+1]]:12: +@interface Test2 @end +#endif + +#if 0 +// CHECK-NOT: [[@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-NOT: [[@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-NOT: [[@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-NOT: [[@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-NOT: [[@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