Index: clang/include/clang/Basic/DiagnosticLexKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticLexKinds.td +++ clang/include/clang/Basic/DiagnosticLexKinds.td @@ -455,9 +455,11 @@ def err_pp_unterminated_conditional : Error< "unterminated conditional directive">; def pp_err_else_after_else : Error<"#else after #else">; -def pp_err_elif_after_else : Error<"#elif after #else">; +def pp_err_elif_after_else : Error< + "%select{#elif|#elifdef|#elifndef}0 after #else">; def pp_err_else_without_if : Error<"#else without #if">; -def pp_err_elif_without_if : Error<"#elif without #if">; +def pp_err_elif_without_if : Error< + "%select{#elif|#elifdef|#elifndef}0 without #if">; def err_pp_endif_without_if : Error<"#endif without #if">; def err_pp_expected_value_in_expr : Error<"expected value in expression">; def err_pp_expected_rparen : Error<"expected ')' in preprocessor expression">; Index: clang/include/clang/Basic/TokenKinds.def =================================================================== --- clang/include/clang/Basic/TokenKinds.def +++ clang/include/clang/Basic/TokenKinds.def @@ -98,6 +98,8 @@ PPKEYWORD(ifdef) PPKEYWORD(ifndef) PPKEYWORD(elif) +PPKEYWORD(elifdef) +PPKEYWORD(elifndef) PPKEYWORD(else) PPKEYWORD(endif) PPKEYWORD(defined) Index: clang/include/clang/Lex/DependencyDirectivesSourceMinimizer.h =================================================================== --- clang/include/clang/Lex/DependencyDirectivesSourceMinimizer.h +++ clang/include/clang/Lex/DependencyDirectivesSourceMinimizer.h @@ -44,6 +44,8 @@ pp_ifdef, pp_ifndef, pp_elif, + pp_elifdef, + pp_elifndef, pp_else, pp_endif, decl_at_import, Index: clang/include/clang/Lex/PPCallbacks.h =================================================================== --- clang/include/clang/Lex/PPCallbacks.h +++ clang/include/clang/Lex/PPCallbacks.h @@ -351,6 +351,22 @@ const MacroDefinition &MD) { } + /// Hook called whenever an \#elifdef branch is taken. + /// \param Loc the source location of the directive. + /// \param MacroNameTok Information on the token being tested. + /// \param MD The MacroDefinition if the name was a macro, null otherwise. + virtual void Elifdef(SourceLocation Loc, const Token &MacroNameTok, + const MacroDefinition &MD) { + } + /// Hook called whenever an \#elifdef is skipped. + /// \param Loc the source location of the directive. + /// \param ConditionRange The SourceRange of the expression being tested. + /// \param IfLoc the source location of the \#if/\#ifdef/\#ifndef directive. + // FIXME: better to pass in a list (or tree!) of Tokens. + virtual void Elifdef(SourceLocation Loc, SourceRange ConditionRange, + SourceLocation IfLoc) { + } + /// Hook called whenever an \#ifndef is seen. /// \param Loc the source location of the directive. /// \param MacroNameTok Information on the token being tested. @@ -359,6 +375,22 @@ const MacroDefinition &MD) { } + /// Hook called whenever an \#elifndef branch is taken. + /// \param Loc the source location of the directive. + /// \param MacroNameTok Information on the token being tested. + /// \param MD The MacroDefinition if the name was a macro, null otherwise. + virtual void Elifndef(SourceLocation Loc, const Token &MacroNameTok, + const MacroDefinition &MD) { + } + /// Hook called whenever an \#elifndef is skipped. + /// \param Loc the source location of the directive. + /// \param ConditionRange The SourceRange of the expression being tested. + /// \param IfLoc the source location of the \#if/\#ifdef/\#ifndef directive. + // FIXME: better to pass in a list (or tree!) of Tokens. + virtual void Elifndef(SourceLocation Loc, SourceRange ConditionRange, + SourceLocation IfLoc) { + } + /// Hook called whenever an \#else is seen. /// \param Loc the source location of the directive. /// \param IfLoc the source location of the \#if/\#ifdef/\#ifndef directive. @@ -586,6 +618,19 @@ Second->Ifdef(Loc, MacroNameTok, MD); } + /// Hook called whenever an \#elifdef is taken. + void Elifdef(SourceLocation Loc, const Token &MacroNameTok, + const MacroDefinition &MD) override { + First->Elifdef(Loc, MacroNameTok, MD); + Second->Elifdef(Loc, MacroNameTok, MD); + } + /// Hook called whenever an \#elifdef is skipped. + void Elifdef(SourceLocation Loc, SourceRange ConditionRange, + SourceLocation IfLoc) override { + First->Elifdef(Loc, ConditionRange, IfLoc); + Second->Elifdef(Loc, ConditionRange, IfLoc); + } + /// Hook called whenever an \#ifndef is seen. void Ifndef(SourceLocation Loc, const Token &MacroNameTok, const MacroDefinition &MD) override { @@ -593,6 +638,19 @@ Second->Ifndef(Loc, MacroNameTok, MD); } + /// Hook called whenever an \#elifndef is taken. + void Elifndef(SourceLocation Loc, const Token &MacroNameTok, + const MacroDefinition &MD) override { + First->Elifndef(Loc, MacroNameTok, MD); + Second->Elifndef(Loc, MacroNameTok, MD); + } + /// Hook called whenever an \#elifndef is skipped. + void Elifndef(SourceLocation Loc, SourceRange ConditionRange, + SourceLocation IfLoc) override { + First->Elifndef(Loc, ConditionRange, IfLoc); + Second->Elifndef(Loc, ConditionRange, IfLoc); + } + /// Hook called whenever an \#else is seen. void Else(SourceLocation Loc, SourceLocation IfLoc) override { First->Else(Loc, IfLoc); Index: clang/include/clang/Lex/PPConditionalDirectiveRecord.h =================================================================== --- clang/include/clang/Lex/PPConditionalDirectiveRecord.h +++ clang/include/clang/Lex/PPConditionalDirectiveRecord.h @@ -93,6 +93,14 @@ const MacroDefinition &MD) override; void Ifndef(SourceLocation Loc, const Token &MacroNameTok, const MacroDefinition &MD) override; + void Elifdef(SourceLocation Loc, const Token &MacroNameTok, + const MacroDefinition &MD) override; + void Elifdef(SourceLocation Loc, SourceRange ConditionRange, + SourceLocation IfLoc) override; + void Elifndef(SourceLocation Loc, const Token &MacroNameTok, + const MacroDefinition &MD) override; + void Elifndef(SourceLocation Loc, SourceRange ConditionRange, + SourceLocation IfLoc) override; void Else(SourceLocation Loc, SourceLocation IfLoc) override; void Endif(SourceLocation Loc, SourceLocation IfLoc) override; }; Index: clang/include/clang/Lex/PreprocessingRecord.h =================================================================== --- clang/include/clang/Lex/PreprocessingRecord.h +++ clang/include/clang/Lex/PreprocessingRecord.h @@ -538,6 +538,10 @@ const MacroDefinition &MD) override; void Ifndef(SourceLocation Loc, const Token &MacroNameTok, const MacroDefinition &MD) override; + void Elifdef(SourceLocation Loc, const Token &MacroNameTok, + const MacroDefinition &MD) override; + void Elifndef(SourceLocation Loc, const Token &MacroNameTok, + const MacroDefinition &MD) override; /// Hook called whenever the 'defined' operator is seen. void Defined(const Token &MacroNameTok, const MacroDefinition &MD, Index: clang/include/clang/Lex/Preprocessor.h =================================================================== --- clang/include/clang/Lex/Preprocessor.h +++ clang/include/clang/Lex/Preprocessor.h @@ -2357,7 +2357,8 @@ bool ReadAnyTokensBeforeDirective); void HandleEndifDirective(Token &EndifToken); void HandleElseDirective(Token &Result, const Token &HashToken); - void HandleElifDirective(Token &ElifToken, const Token &HashToken); + void HandleElifFamilyDirective(Token &ElifToken, const Token &HashToken, + tok::PPKeywordKind Kind); // Pragmas. void HandlePragmaDirective(PragmaIntroducer Introducer); Index: clang/lib/Basic/IdentifierTable.cpp =================================================================== --- clang/lib/Basic/IdentifierTable.cpp +++ clang/lib/Basic/IdentifierTable.cpp @@ -341,9 +341,11 @@ CASE( 6, 'p', 'a', pragma); CASE( 7, 'd', 'f', defined); + CASE( 7, 'e', 'i', elifdef); CASE( 7, 'i', 'c', include); CASE( 7, 'w', 'r', warning); + CASE( 8, 'e', 'i', elifndef); CASE( 8, 'u', 'a', unassert); CASE(12, 'i', 'c', include_next); Index: clang/lib/Format/UnwrappedLineParser.cpp =================================================================== --- clang/lib/Format/UnwrappedLineParser.cpp +++ clang/lib/Format/UnwrappedLineParser.cpp @@ -751,6 +751,8 @@ case tok::pp_else: parsePPElse(); break; + case tok::pp_elifdef: + case tok::pp_elifndef: case tok::pp_elif: parsePPElIf(); break; Index: clang/lib/Index/IndexingAction.cpp =================================================================== --- clang/lib/Index/IndexingAction.cpp +++ clang/lib/Index/IndexingAction.cpp @@ -77,6 +77,23 @@ MacroNameTok.getLocation(), *MD.getMacroInfo()); } + + void Elifdef(SourceLocation Loc, const Token &MacroNameTok, + const MacroDefinition &MD) override { + if (!MD.getMacroInfo()) // Ignore non-existent macro. + return; + IndexCtx->handleMacroReference(*MacroNameTok.getIdentifierInfo(), + MacroNameTok.getLocation(), + *MD.getMacroInfo()); + } + void Elifndef(SourceLocation Loc, const Token &MacroNameTok, + const MacroDefinition &MD) override { + if (!MD.getMacroInfo()) // Ignore non-existent macro. + return; + IndexCtx->handleMacroReference(*MacroNameTok.getIdentifierInfo(), + MacroNameTok.getLocation(), + *MD.getMacroInfo()); + } }; class IndexASTConsumer final : public ASTConsumer { Index: clang/lib/Lex/DependencyDirectivesSourceMinimizer.cpp =================================================================== --- clang/lib/Lex/DependencyDirectivesSourceMinimizer.cpp +++ clang/lib/Lex/DependencyDirectivesSourceMinimizer.cpp @@ -846,6 +846,8 @@ .Case("ifdef", pp_ifdef) .Case("ifndef", pp_ifndef) .Case("elif", pp_elif) + .Case("elifdef", pp_elifdef) + .Case("elifndef", pp_elifndef) .Case("else", pp_else) .Case("endif", pp_endif) .Case("pragma", pp_pragma_import) @@ -904,7 +906,7 @@ struct Directive { enum DirectiveKind { If, // if/ifdef/ifndef - Else // elif,else + Else // elif/elifdef/elifndef, else }; int Offset; DirectiveKind Kind; @@ -919,6 +921,8 @@ break; case pp_elif: + case pp_elifdef: + case pp_elifndef: case pp_else: { if (Offsets.empty()) return true; Index: clang/lib/Lex/Lexer.cpp =================================================================== --- clang/lib/Lex/Lexer.cpp +++ clang/lib/Lex/Lexer.cpp @@ -682,6 +682,8 @@ .Case("ifdef", PDK_Skipped) .Case("ifndef", PDK_Skipped) .Case("elif", PDK_Skipped) + .Case("elifdef", PDK_Skipped) + .Case("elifndef", PDK_Skipped) .Case("else", PDK_Skipped) .Case("endif", PDK_Skipped) .Default(PDK_Unknown); Index: clang/lib/Lex/PPConditionalDirectiveRecord.cpp =================================================================== --- clang/lib/Lex/PPConditionalDirectiveRecord.cpp +++ clang/lib/Lex/PPConditionalDirectiveRecord.cpp @@ -101,6 +101,28 @@ CondDirectiveStack.back() = Loc; } +void PPConditionalDirectiveRecord::Elifdef(SourceLocation Loc, const Token &, + const MacroDefinition &) { + addCondDirectiveLoc(CondDirectiveLoc(Loc, CondDirectiveStack.back())); + CondDirectiveStack.back() = Loc; +} +void PPConditionalDirectiveRecord::Elifdef(SourceLocation Loc, SourceRange, + SourceLocation) { + addCondDirectiveLoc(CondDirectiveLoc(Loc, CondDirectiveStack.back())); + CondDirectiveStack.back() = Loc; +} + +void PPConditionalDirectiveRecord::Elifndef(SourceLocation Loc, const Token &, + const MacroDefinition &) { + addCondDirectiveLoc(CondDirectiveLoc(Loc, CondDirectiveStack.back())); + CondDirectiveStack.back() = Loc; +} +void PPConditionalDirectiveRecord::Elifndef(SourceLocation Loc, SourceRange, + SourceLocation) { + addCondDirectiveLoc(CondDirectiveLoc(Loc, CondDirectiveStack.back())); + CondDirectiveStack.back() = Loc; +} + void PPConditionalDirectiveRecord::Else(SourceLocation Loc, SourceLocation IfLoc) { addCondDirectiveLoc(CondDirectiveLoc(Loc, CondDirectiveStack.back())); Index: clang/lib/Lex/PPDirectives.cpp =================================================================== --- clang/lib/Lex/PPDirectives.cpp +++ clang/lib/Lex/PPDirectives.cpp @@ -100,6 +100,14 @@ MD_ReservedMacro //> #define of #undef reserved id, disabled by default }; +/// Enumerates possible %select values for the pp_err_elif_after_else and +/// pp_err_elif_without_if diagnostics. +enum PPElifDiag { + PED_Elif, + PED_Elifdef, + PED_Elifndef +}; + /// Checks if the specified identifier is reserved in the specified /// language. /// This function does not check if the identifier is a keyword. @@ -582,7 +590,8 @@ PPConditionalInfo &CondInfo = CurPPLexer->peekConditionalLevel(); // If this is a #elif with a #else before it, report the error. - if (CondInfo.FoundElse) Diag(Tok, diag::pp_err_elif_after_else); + if (CondInfo.FoundElse) + Diag(Tok, diag::pp_err_elif_after_else) << PED_Elif; // If this is in a skipping block or if we're already handled this #if // block, don't bother parsing the condition. @@ -609,6 +618,59 @@ break; } } + } else if (Sub == "lifdef" || // "elifdef" + Sub == "lifndef") { // "elifndef" + bool IsElifDef = Sub == "lifdef"; + PPConditionalInfo &CondInfo = CurPPLexer->peekConditionalLevel(); + Token DirectiveToken = Tok; + + // If this is a #elif with a #else before it, report the error. + if (CondInfo.FoundElse) + Diag(Tok, diag::pp_err_elif_after_else) + << (IsElifDef ? PED_Elifdef : PED_Elifndef); + + // If this is in a skipping block or if we're already handled this #if + // block, don't bother parsing the condition. + if (CondInfo.WasSkipping || CondInfo.FoundNonSkip) { + DiscardUntilEndOfDirective(); + } else { + // Restore the value of LexingRawMode so that identifiers are + // looked up, etc, inside the #elif[n]def expression. + assert(CurPPLexer->LexingRawMode && "We have to be skipping here!"); + CurPPLexer->LexingRawMode = false; + Token MacroNameTok; + ReadMacroName(MacroNameTok); + CurPPLexer->LexingRawMode = true; + + // If the macro name token is tok::eod, there was an error that was + // already reported. + if (MacroNameTok.is(tok::eod)) { + // Skip code until we get to #endif. This helps with recovery by + // not emitting an error when the #endif is reached. + continue; + } + + CheckEndOfDirective(IsElifDef ? "elifdef" : "elifndef"); + + IdentifierInfo *MII = MacroNameTok.getIdentifierInfo(); + auto MD = getMacroDefinition(MII); + MacroInfo *MI = MD.getMacroInfo(); + + if (Callbacks) { + if (IsElifDef) { + Callbacks->Elifdef(DirectiveToken.getLocation(), MacroNameTok, + MD); + } else { + Callbacks->Elifndef(DirectiveToken.getLocation(), MacroNameTok, + MD); + } + } + // If this condition is true, enter it! + if (static_cast(MI) == IsElifDef) { + CondInfo.FoundNonSkip = true; + break; + } + } } } @@ -992,7 +1054,10 @@ return HandleIfdefDirective(Result, SavedHash, true, ReadAnyTokensBeforeDirective); case tok::pp_elif: - return HandleElifDirective(Result, SavedHash); + case tok::pp_elifdef: + case tok::pp_elifndef: + return HandleElifFamilyDirective(Result, SavedHash, II->getPPKeywordID()); + case tok::pp_else: return HandleElseDirective(Result, SavedHash); case tok::pp_endif: @@ -3127,10 +3192,13 @@ /*FoundElse*/ true, Result.getLocation()); } -/// HandleElifDirective - Implements the \#elif directive. -/// -void Preprocessor::HandleElifDirective(Token &ElifToken, - const Token &HashToken) { +/// Implements the \#elif, \#elifdef, and \#elifndef directives. +void Preprocessor::HandleElifFamilyDirective(Token &ElifToken, + const Token &HashToken, + tok::PPKeywordKind Kind) { + PPElifDiag DirKind = Kind == tok::pp_elif ? PED_Elif + : Kind == tok::pp_elifdef ? PED_Elifdef + : PED_Elifndef; ++NumElse; // #elif directive in a non-skipping conditional... start skipping. @@ -3140,7 +3208,7 @@ PPConditionalInfo CI; if (CurPPLexer->popConditionalLevel(CI)) { - Diag(ElifToken, diag::pp_err_elif_without_if); + Diag(ElifToken, diag::pp_err_elif_without_if) << DirKind; return; } @@ -3149,11 +3217,23 @@ CurPPLexer->MIOpt.EnterTopLevelConditional(); // If this is a #elif with a #else before it, report the error. - if (CI.FoundElse) Diag(ElifToken, diag::pp_err_elif_after_else); + if (CI.FoundElse) + Diag(ElifToken, diag::pp_err_elif_after_else) << DirKind; - if (Callbacks) - Callbacks->Elif(ElifToken.getLocation(), ConditionRange, - PPCallbacks::CVK_NotEvaluated, CI.IfLoc); + if (Callbacks) { + switch (Kind) { + case tok::pp_elif: + Callbacks->Elif(ElifToken.getLocation(), ConditionRange, + PPCallbacks::CVK_NotEvaluated, CI.IfLoc); + break; + case tok::pp_elifdef: + Callbacks->Elifdef(ElifToken.getLocation(), ConditionRange, CI.IfLoc); + break; + case tok::pp_elifndef: + Callbacks->Elifndef(ElifToken.getLocation(), ConditionRange, CI.IfLoc); + break; + } + } bool RetainExcludedCB = PPOpts->RetainExcludedConditionalBlocks && getSourceManager().isInMainFile(ElifToken.getLocation()); Index: clang/lib/Lex/PreprocessingRecord.cpp =================================================================== --- clang/lib/Lex/PreprocessingRecord.cpp +++ clang/lib/Lex/PreprocessingRecord.cpp @@ -411,6 +411,14 @@ MacroNameTok.getLocation()); } +void PreprocessingRecord::Elifdef(SourceLocation Loc, const Token &MacroNameTok, + const MacroDefinition &MD) { + // This is not actually a macro expansion but record it as a macro reference. + if (MD) + addMacroExpansion(MacroNameTok, MD.getMacroInfo(), + MacroNameTok.getLocation()); +} + void PreprocessingRecord::Ifndef(SourceLocation Loc, const Token &MacroNameTok, const MacroDefinition &MD) { // This is not actually a macro expansion but record it as a macro reference. @@ -419,6 +427,15 @@ MacroNameTok.getLocation()); } +void PreprocessingRecord::Elifndef(SourceLocation Loc, + const Token &MacroNameTok, + const MacroDefinition &MD) { + // This is not actually a macro expansion but record it as a macro reference. + if (MD) + addMacroExpansion(MacroNameTok, MD.getMacroInfo(), + MacroNameTok.getLocation()); +} + void PreprocessingRecord::Defined(const Token &MacroNameTok, const MacroDefinition &MD, SourceRange Range) { Index: clang/lib/Lex/Preprocessor.cpp =================================================================== --- clang/lib/Lex/Preprocessor.cpp +++ clang/lib/Lex/Preprocessor.cpp @@ -274,7 +274,7 @@ llvm::errs() << " " << NumEnteredSourceFiles << " source files entered.\n"; llvm::errs() << " " << MaxIncludeStackDepth << " max include stack depth\n"; llvm::errs() << " " << NumIf << " #if/#ifndef/#ifdef.\n"; - llvm::errs() << " " << NumElse << " #else/#elif.\n"; + llvm::errs() << " " << NumElse << " #else/#elif/#elifdef/#elifndef.\n"; llvm::errs() << " " << NumEndif << " #endif.\n"; llvm::errs() << " " << NumPragma << " #pragma.\n"; llvm::errs() << NumSkipped << " #if/#ifndef#ifdef regions skipped\n"; Index: clang/lib/Sema/SemaCodeComplete.cpp =================================================================== --- clang/lib/Sema/SemaCodeComplete.cpp +++ clang/lib/Sema/SemaCodeComplete.cpp @@ -9198,6 +9198,18 @@ Builder.AddPlaceholderChunk("condition"); Results.AddResult(Builder.TakeString()); + // #elifdef + Builder.AddTypedTextChunk("elifdef"); + Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); + Builder.AddPlaceholderChunk("macro"); + Results.AddResult(Builder.TakeString()); + + // #elifndef + Builder.AddTypedTextChunk("elifndef"); + Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); + Builder.AddPlaceholderChunk("macro"); + Results.AddResult(Builder.TakeString()); + // #else Builder.AddTypedTextChunk("else"); Results.AddResult(Builder.TakeString()); Index: clang/test/Index/complete-preprocessor.m =================================================================== --- clang/test/Index/complete-preprocessor.m +++ clang/test/Index/complete-preprocessor.m @@ -35,6 +35,8 @@ // CHECK-CC2: NotImplemented:{TypedText define}{HorizontalSpace }{Placeholder macro} (40) // CHECK-CC2-NEXT: NotImplemented:{TypedText define}{HorizontalSpace }{Placeholder macro}{LeftParen (}{Placeholder args}{RightParen )} (40) // CHECK-CC2-NEXT: NotImplemented:{TypedText elif}{HorizontalSpace }{Placeholder condition} (40) +// CHECK-CC2-NEXT: NotImplemented:{TypedText elifdef}{HorizontalSpace }{Placeholder macro} (40) +// CHECK-CC2-NEXT: NotImplemented:{TypedText elifndef}{HorizontalSpace }{Placeholder macro} (40) // CHECK-CC2-NEXT: NotImplemented:{TypedText else} (40) // CHECK-CC2-NEXT: NotImplemented:{TypedText endif} (40) // CHECK-CC2-NEXT: NotImplemented:{TypedText error}{HorizontalSpace }{Placeholder message} (40) Index: clang/test/Preprocessor/elifdef.c =================================================================== --- /dev/null +++ clang/test/Preprocessor/elifdef.c @@ -0,0 +1,107 @@ +// RUN: %clang_cc1 %s -Eonly -verify + +#ifdef FOO +#elifdef BAR +#error "did not expect to get here" +#endif + +/* expected-error@+4 {{"got it"}} */ +#ifdef FOO +#elifdef BAR +#else +#error "got it" +#endif + +/* expected-error@+3 {{"got it"}} */ +#ifdef FOO +#elifndef BAR +#error "got it" +#endif + +/* expected-error@+3 {{"got it"}} */ +#ifdef FOO +#elifndef BAR +#error "got it" +#else +#error "did not expect to get here" +#endif + +#define BAR +/* expected-error@+3 {{"got it"}} */ +#ifdef FOO +#elifdef BAR +#error "got it" +#endif +#undef BAR + +#ifdef FOO +#elifdef BAR // test that comments aren't an issue +#error "did not expect to get here" +#endif + +/* expected-error@+4 {{"got it"}} */ +#ifdef FOO +#elifdef BAR // test that comments aren't an issue +#else +#error "got it" +#endif + +/* expected-error@+3 {{"got it"}} */ +#ifdef FOO +#elifndef BAR // test that comments aren't an issue +#error "got it" +#endif + +/* expected-error@+3 {{"got it"}} */ +#ifdef FOO +#elifndef BAR // test that comments aren't an issue +#error "got it" +#else +#error "did not expect to get here" +#endif + +#define BAR +/* expected-error@+3 {{"got it"}} */ +#ifdef FOO +#elifdef BAR // test that comments aren't an issue +#error "got it" +#endif +#undef BAR + +#define BAR +/* expected-error@+6 {{"got it"}} */ +#ifdef FOO +#error "did not expect to get here" +#elifndef BAR +#error "did not expect to get here" +#else +#error "got it" +#endif +#undef BAR + +/* expected-error@+3 {{#elifdef after #else}} */ +#ifdef FOO +#else +#elifdef BAR +#endif + +/* expected-error@+3 {{#elifndef after #else}} */ +#ifdef FOO +#else +#elifndef BAR +#endif + +#elifdef FOO /* expected-error {{#elifdef without #if}} */ +#endif /* expected-error {{#endif without #if}} */ + +#elifndef FOO /* expected-error {{#elifndef without #if}} */ +#endif /* expected-error {{#endif without #if}} */ + +/* Note, we do not expect errors about the missing macro name in the skipped + blocks. This is consistent with #elif behavior. */ +/* expected-error@+2 {{"got it"}} */ +#ifndef FOO +#error "got it" +#elifdef +#elifndef +#endif Index: clang/test/Preprocessor/if_warning.c =================================================================== --- clang/test/Preprocessor/if_warning.c +++ clang/test/Preprocessor/if_warning.c @@ -6,6 +6,7 @@ #endif #ifdef foo +#elifdef foo #endif #if defined(foo) @@ -15,6 +16,7 @@ // PR3938 #if 0 #ifdef D +#elifdef D #else 1 // Should not warn due to C99 6.10p4 #endif #endif Index: clang/test/Preprocessor/ifdef-recover.c =================================================================== --- clang/test/Preprocessor/ifdef-recover.c +++ clang/test/Preprocessor/ifdef-recover.c @@ -19,4 +19,14 @@ #if f(2 #endif +/* expected-error@+2 {{macro name missing}} */ +#ifdef FOO +#elifdef +#endif + +/* expected-error@+2 {{macro name must be an identifier}} */ +#ifdef FOO +#elifdef ! +#endif + int x; Index: clang/test/Preprocessor/macro_misc.c =================================================================== --- clang/test/Preprocessor/macro_misc.c +++ clang/test/Preprocessor/macro_misc.c @@ -2,6 +2,7 @@ // This should not be rejected. #ifdef defined +#elifdef defined #endif Index: clang/test/Preprocessor/macro_vaopt_check.cpp =================================================================== --- clang/test/Preprocessor/macro_vaopt_check.cpp +++ clang/test/Preprocessor/macro_vaopt_check.cpp @@ -68,7 +68,9 @@ #if __VA_OPT__ // expected-warning {{__VA_OPT__ can only appear in the expansion of a variadic macro}} #endif +// expected-warning@+2 {{__VA_OPT__ can only appear in the expansion of a variadic macro}} #ifdef __VA_OPT__ // expected-warning {{__VA_OPT__ can only appear in the expansion of a variadic macro}} +#elifdef __VA_OPT__ #endif #define BAD __VA_OPT__ // expected-warning {{__VA_OPT__ can only appear in the expansion of a variadic macro}} Index: clang/unittests/Lex/DependencyDirectivesSourceMinimizerTest.cpp =================================================================== --- clang/unittests/Lex/DependencyDirectivesSourceMinimizerTest.cpp +++ clang/unittests/Lex/DependencyDirectivesSourceMinimizerTest.cpp @@ -53,6 +53,8 @@ "#if A\n" "#ifdef A\n" "#ifndef A\n" + "#elifdef A\n" + "#elifndef A\n" "#elif A\n" "#else\n" "#include \n" @@ -70,18 +72,20 @@ EXPECT_EQ(pp_if, Tokens[3].K); EXPECT_EQ(pp_ifdef, Tokens[4].K); EXPECT_EQ(pp_ifndef, Tokens[5].K); - EXPECT_EQ(pp_elif, Tokens[6].K); - EXPECT_EQ(pp_else, Tokens[7].K); - EXPECT_EQ(pp_include, Tokens[8].K); - EXPECT_EQ(pp_include_next, Tokens[9].K); - EXPECT_EQ(pp___include_macros, Tokens[10].K); - EXPECT_EQ(pp_import, Tokens[11].K); - EXPECT_EQ(decl_at_import, Tokens[12].K); - EXPECT_EQ(pp_pragma_import, Tokens[13].K); - EXPECT_EQ(cxx_export_decl, Tokens[14].K); - EXPECT_EQ(cxx_module_decl, Tokens[15].K); - EXPECT_EQ(cxx_import_decl, Tokens[16].K); - EXPECT_EQ(pp_eof, Tokens[17].K); + EXPECT_EQ(pp_elifdef, Tokens[6].K); + EXPECT_EQ(pp_elifndef, Tokens[7].K); + EXPECT_EQ(pp_elif, Tokens[8].K); + EXPECT_EQ(pp_else, Tokens[9].K); + EXPECT_EQ(pp_include, Tokens[10].K); + EXPECT_EQ(pp_include_next, Tokens[11].K); + EXPECT_EQ(pp___include_macros, Tokens[12].K); + EXPECT_EQ(pp_import, Tokens[13].K); + EXPECT_EQ(decl_at_import, Tokens[14].K); + EXPECT_EQ(pp_pragma_import, Tokens[15].K); + EXPECT_EQ(cxx_export_decl, Tokens[16].K); + EXPECT_EQ(cxx_module_decl, Tokens[17].K); + EXPECT_EQ(cxx_import_decl, Tokens[18].K); + EXPECT_EQ(pp_eof, Tokens[19].K); } TEST(MinimizeSourceToDependencyDirectivesTest, Define) { @@ -324,6 +328,44 @@ Out.data()); } +TEST(MinimizeSourceToDependencyDirectivesTest, Elifdef) { + SmallVector Out; + + ASSERT_FALSE(minimizeSourceToDependencyDirectives("#ifdef A\n" + "#define B\n" + "#elifdef C\n" + "#define D\n" + "#endif\n", + Out)); + EXPECT_STREQ("#ifdef A\n" + "#define B\n" + "#elifdef C\n" + "#define D\n" + "#endif\n", + Out.data()); + + ASSERT_FALSE(minimizeSourceToDependencyDirectives("#ifdef A\n" + "#define B\n" + "#elifdef B\n" + "#define C\n" + "#elifndef C\n" + "#define D\n" + "#else\n" + "#define E\n" + "#endif\n", + Out)); + EXPECT_STREQ("#ifdef A\n" + "#define B\n" + "#elifdef B\n" + "#define C\n" + "#elifndef C\n" + "#define D\n" + "#else\n" + "#define E\n" + "#endif\n", + Out.data()); +} + TEST(MinimizeSourceToDependencyDirectivesTest, EmptyIfdef) { SmallVector Out; @@ -341,6 +383,23 @@ Out.data()); } +TEST(MinimizeSourceToDependencyDirectivesTest, EmptyElifdef) { + SmallVector Out; + + ASSERT_FALSE(minimizeSourceToDependencyDirectives("#ifdef A\n" + "void skip();\n" + "#elifdef B\n" + "#elifndef C\n" + "#else D\n" + "#endif\n", + Out)); + EXPECT_STREQ("#ifdef A\n" + "#elifdef B\n" + "#elifndef C\n" + "#endif\n", + Out.data()); +} + TEST(MinimizeSourceToDependencyDirectivesTest, Pragma) { SmallVector Out; @@ -708,6 +767,29 @@ EXPECT_EQ(Ranges[0].Length, (int)Out.find("#endif")); } +TEST(MinimizeSourceToDependencyDirectivesTest, SkippedPPRangesBasicElifdef) { + SmallString<128> Out; + SmallVector Toks; + StringRef Source = "#ifdef BLAH\n" + "void skip();\n" + "#elifdef BLAM\n" + "void skip();\n" + "#elifndef GUARD\n" + "#define GUARD\n" + "void foo();\n" + "#endif\n"; + ASSERT_FALSE(minimizeSourceToDependencyDirectives(Source, Out, Toks)); + SmallVector Ranges; + ASSERT_FALSE(computeSkippedRanges(Toks, Ranges)); + EXPECT_EQ(Ranges.size(), 3u); + EXPECT_EQ(Ranges[0].Offset, 0); + EXPECT_EQ(Ranges[0].Length, (int)Out.find("#elifdef")); + EXPECT_EQ(Ranges[1].Offset, (int)Out.find("#elifdef")); + EXPECT_EQ(Ranges[1].Offset + Ranges[1].Length, (int)Out.find("#elifndef")); + EXPECT_EQ(Ranges[2].Offset, (int)Out.find("#elifndef")); + EXPECT_EQ(Ranges[2].Offset + Ranges[2].Length, (int)Out.rfind("#endif")); +} + TEST(MinimizeSourceToDependencyDirectivesTest, SkippedPPRangesNested) { SmallString<128> Out; SmallVector Toks;