Author: Aaron Ballman Date: 2021-05-27T08:57:47-04:00 New Revision: 8edd3464afbff65d7d5945b3a8b20009d6ff5deb
URL: https://github.com/llvm/llvm-project/commit/8edd3464afbff65d7d5945b3a8b20009d6ff5deb DIFF: https://github.com/llvm/llvm-project/commit/8edd3464afbff65d7d5945b3a8b20009d6ff5deb.diff LOG: Add support for #elifdef and #elifndef WG14 adopted N2645 and WG21 EWG has accepted P2334 in principle (still subject to full EWG vote + CWG review + plenary vote), which add support for #elifdef as shorthand for #elif defined and #elifndef as shorthand for #elif !defined. This patch adds support for the new preprocessor directives. Added: clang/test/Preprocessor/elifdef.c Modified: clang/include/clang/Basic/DiagnosticLexKinds.td clang/include/clang/Basic/TokenKinds.def clang/include/clang/Lex/DependencyDirectivesSourceMinimizer.h clang/include/clang/Lex/PPCallbacks.h clang/include/clang/Lex/PPConditionalDirectiveRecord.h clang/include/clang/Lex/PreprocessingRecord.h clang/include/clang/Lex/Preprocessor.h clang/lib/Basic/IdentifierTable.cpp clang/lib/Format/UnwrappedLineParser.cpp clang/lib/Index/IndexingAction.cpp clang/lib/Lex/DependencyDirectivesSourceMinimizer.cpp clang/lib/Lex/Lexer.cpp clang/lib/Lex/PPConditionalDirectiveRecord.cpp clang/lib/Lex/PPDirectives.cpp clang/lib/Lex/PreprocessingRecord.cpp clang/lib/Lex/Preprocessor.cpp clang/lib/Sema/SemaCodeComplete.cpp clang/test/Index/complete-preprocessor.m clang/test/Preprocessor/if_warning.c clang/test/Preprocessor/ifdef-recover.c clang/test/Preprocessor/macro_misc.c clang/test/Preprocessor/macro_vaopt_check.cpp clang/unittests/Lex/DependencyDirectivesSourceMinimizerTest.cpp Removed: ################################################################################ diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td index 64026a1ee9227..ce6d0d0394b48 100644 --- a/clang/include/clang/Basic/DiagnosticLexKinds.td +++ b/clang/include/clang/Basic/DiagnosticLexKinds.td @@ -455,9 +455,11 @@ def err_pp_malformed_ident : Error<"invalid #ident directive">; 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">; diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 572ebae6618db..be258c95d2fac 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -98,6 +98,8 @@ PPKEYWORD(if) PPKEYWORD(ifdef) PPKEYWORD(ifndef) PPKEYWORD(elif) +PPKEYWORD(elifdef) +PPKEYWORD(elifndef) PPKEYWORD(else) PPKEYWORD(endif) PPKEYWORD(defined) diff --git a/clang/include/clang/Lex/DependencyDirectivesSourceMinimizer.h b/clang/include/clang/Lex/DependencyDirectivesSourceMinimizer.h index d832df6b6146b..9bb820156c252 100644 --- a/clang/include/clang/Lex/DependencyDirectivesSourceMinimizer.h +++ b/clang/include/clang/Lex/DependencyDirectivesSourceMinimizer.h @@ -44,6 +44,8 @@ enum TokenKind { pp_ifdef, pp_ifndef, pp_elif, + pp_elifdef, + pp_elifndef, pp_else, pp_endif, decl_at_import, diff --git a/clang/include/clang/Lex/PPCallbacks.h b/clang/include/clang/Lex/PPCallbacks.h index de5e8eb2ca220..d57be1990caff 100644 --- a/clang/include/clang/Lex/PPCallbacks.h +++ b/clang/include/clang/Lex/PPCallbacks.h @@ -351,6 +351,22 @@ class PPCallbacks { 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 @@ class PPCallbacks { 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 @@ class PPChainedCallbacks : public PPCallbacks { 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 @@ class PPChainedCallbacks : public PPCallbacks { 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); diff --git a/clang/include/clang/Lex/PPConditionalDirectiveRecord.h b/clang/include/clang/Lex/PPConditionalDirectiveRecord.h index 0774374353030..d8c556ae25316 100644 --- a/clang/include/clang/Lex/PPConditionalDirectiveRecord.h +++ b/clang/include/clang/Lex/PPConditionalDirectiveRecord.h @@ -93,6 +93,14 @@ class PPConditionalDirectiveRecord : public PPCallbacks { 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; }; diff --git a/clang/include/clang/Lex/PreprocessingRecord.h b/clang/include/clang/Lex/PreprocessingRecord.h index 11607811dc8f7..52f1ec9e59bb4 100644 --- a/clang/include/clang/Lex/PreprocessingRecord.h +++ b/clang/include/clang/Lex/PreprocessingRecord.h @@ -538,6 +538,10 @@ class Token; 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, diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index d89c4753f8d1a..16fbf5ea5a5bb 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -2357,7 +2357,8 @@ class Preprocessor { 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); diff --git a/clang/lib/Basic/IdentifierTable.cpp b/clang/lib/Basic/IdentifierTable.cpp index e59153060c678..3c18219c91c9b 100644 --- a/clang/lib/Basic/IdentifierTable.cpp +++ b/clang/lib/Basic/IdentifierTable.cpp @@ -341,9 +341,11 @@ tok::PPKeywordKind IdentifierInfo::getPPKeywordID() const { 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); diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index 4edfe7c49c1a4..0fb5428f89673 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -751,6 +751,8 @@ void UnwrappedLineParser::parsePPDirective() { case tok::pp_else: parsePPElse(); break; + case tok::pp_elifdef: + case tok::pp_elifndef: case tok::pp_elif: parsePPElIf(); break; diff --git a/clang/lib/Index/IndexingAction.cpp b/clang/lib/Index/IndexingAction.cpp index 6152e2c3701e6..65479a483b17e 100644 --- a/clang/lib/Index/IndexingAction.cpp +++ b/clang/lib/Index/IndexingAction.cpp @@ -77,6 +77,23 @@ class IndexPPCallbacks final : public PPCallbacks { 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 { diff --git a/clang/lib/Lex/DependencyDirectivesSourceMinimizer.cpp b/clang/lib/Lex/DependencyDirectivesSourceMinimizer.cpp index cdb4a79fa11a7..cfca167f8bf1e 100644 --- a/clang/lib/Lex/DependencyDirectivesSourceMinimizer.cpp +++ b/clang/lib/Lex/DependencyDirectivesSourceMinimizer.cpp @@ -846,6 +846,8 @@ bool Minimizer::lexPPLine(const char *&First, const char *const End) { .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 @@ bool clang::minimize_source_to_dependency_directives::computeSkippedRanges( struct Directive { enum DirectiveKind { If, // if/ifdef/ifndef - Else // elif,else + Else // elif/elifdef/elifndef, else }; int Offset; DirectiveKind Kind; @@ -919,6 +921,8 @@ bool clang::minimize_source_to_dependency_directives::computeSkippedRanges( break; case pp_elif: + case pp_elifdef: + case pp_elifndef: case pp_else: { if (Offsets.empty()) return true; diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp index d31987a432b89..cb2b19b59c4ec 100644 --- a/clang/lib/Lex/Lexer.cpp +++ b/clang/lib/Lex/Lexer.cpp @@ -682,6 +682,8 @@ PreambleBounds Lexer::ComputePreamble(StringRef Buffer, .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); diff --git a/clang/lib/Lex/PPConditionalDirectiveRecord.cpp b/clang/lib/Lex/PPConditionalDirectiveRecord.cpp index facee28007c7d..ddc88f8e8f952 100644 --- a/clang/lib/Lex/PPConditionalDirectiveRecord.cpp +++ b/clang/lib/Lex/PPConditionalDirectiveRecord.cpp @@ -101,6 +101,28 @@ void PPConditionalDirectiveRecord::Elif(SourceLocation Loc, 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())); diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp index a74b42a883fec..1ccf24c6f767d 100644 --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -100,6 +100,14 @@ enum MacroDiag { 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 +}; + // The -fmodule-name option tells the compiler to textually include headers in // the specified module, meaning clang won't build the specified module. This is // useful in a number of situations, for instance, when building a library that @@ -578,7 +586,8 @@ void Preprocessor::SkipExcludedConditionalBlock(SourceLocation HashTokenLoc, PPConditionalInfo &CondInfo = CurPPLexer->peekConditionalLevel(); // If this is a #else with a #else before it, report the error. - if (CondInfo.FoundElse) Diag(Tok, diag::pp_err_else_after_else); + if (CondInfo.FoundElse) + Diag(Tok, diag::pp_err_else_after_else) << PED_Elif; // Note that we've seen a #else in this conditional. CondInfo.FoundElse = true; @@ -632,6 +641,59 @@ void Preprocessor::SkipExcludedConditionalBlock(SourceLocation HashTokenLoc, 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<bool>(MI) == IsElifDef) { + CondInfo.FoundNonSkip = true; + break; + } + } } } @@ -1015,7 +1077,10 @@ void Preprocessor::HandleDirective(Token &Result) { 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: @@ -3154,10 +3219,13 @@ void Preprocessor::HandleElseDirective(Token &Result, const Token &HashToken) { /*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. @@ -3167,7 +3235,7 @@ void Preprocessor::HandleElifDirective(Token &ElifToken, PPConditionalInfo CI; if (CurPPLexer->popConditionalLevel(CI)) { - Diag(ElifToken, diag::pp_err_elif_without_if); + Diag(ElifToken, diag::pp_err_elif_without_if) << DirKind; return; } @@ -3176,11 +3244,23 @@ void Preprocessor::HandleElifDirective(Token &ElifToken, 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()); diff --git a/clang/lib/Lex/PreprocessingRecord.cpp b/clang/lib/Lex/PreprocessingRecord.cpp index 115256db48095..ed59dbdf018dc 100644 --- a/clang/lib/Lex/PreprocessingRecord.cpp +++ b/clang/lib/Lex/PreprocessingRecord.cpp @@ -411,6 +411,14 @@ void PreprocessingRecord::Ifdef(SourceLocation Loc, const Token &MacroNameTok, 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 @@ void PreprocessingRecord::Ifndef(SourceLocation Loc, const Token &MacroNameTok, 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) { diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp index e39b78d5ffec2..e376fff904329 100644 --- a/clang/lib/Lex/Preprocessor.cpp +++ b/clang/lib/Lex/Preprocessor.cpp @@ -274,7 +274,7 @@ void Preprocessor::PrintStats() { 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"; diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp index 8893bd2cf00e7..0bde8eaf75507 100644 --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -9198,6 +9198,18 @@ void Sema::CodeCompletePreprocessorDirective(bool InConditional) { Builder.AddPlaceholderChunk("condition"); Results.AddResult(Builder.TakeString()); + // #elifdef <macro> + Builder.AddTypedTextChunk("elifdef"); + Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); + Builder.AddPlaceholderChunk("macro"); + Results.AddResult(Builder.TakeString()); + + // #elifndef <macro> + Builder.AddTypedTextChunk("elifndef"); + Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); + Builder.AddPlaceholderChunk("macro"); + Results.AddResult(Builder.TakeString()); + // #else Builder.AddTypedTextChunk("else"); Results.AddResult(Builder.TakeString()); diff --git a/clang/test/Index/complete-preprocessor.m b/clang/test/Index/complete-preprocessor.m index d22ace6e1c541..1cc2f32b7efa6 100644 --- a/clang/test/Index/complete-preprocessor.m +++ b/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) diff --git a/clang/test/Preprocessor/elifdef.c b/clang/test/Preprocessor/elifdef.c new file mode 100644 index 0000000000000..3954159c5e08e --- /dev/null +++ b/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 diff --git a/clang/test/Preprocessor/if_warning.c b/clang/test/Preprocessor/if_warning.c index 47bc1c6b170ac..511d10232445d 100644 --- a/clang/test/Preprocessor/if_warning.c +++ b/clang/test/Preprocessor/if_warning.c @@ -6,6 +6,7 @@ extern int x; #endif #ifdef foo +#elifdef foo #endif #if defined(foo) @@ -15,6 +16,7 @@ extern int x; // PR3938 #if 0 #ifdef D +#elifdef D #else 1 // Should not warn due to C99 6.10p4 #endif #endif diff --git a/clang/test/Preprocessor/ifdef-recover.c b/clang/test/Preprocessor/ifdef-recover.c index a6481359f437a..b058851f6d1c1 100644 --- a/clang/test/Preprocessor/ifdef-recover.c +++ b/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; diff --git a/clang/test/Preprocessor/macro_misc.c b/clang/test/Preprocessor/macro_misc.c index 3feaa210f7ddc..92cd614867683 100644 --- a/clang/test/Preprocessor/macro_misc.c +++ b/clang/test/Preprocessor/macro_misc.c @@ -2,6 +2,7 @@ // This should not be rejected. #ifdef defined +#elifdef defined #endif diff --git a/clang/test/Preprocessor/macro_vaopt_check.cpp b/clang/test/Preprocessor/macro_vaopt_check.cpp index c5c0ac518bc01..a0bec14ded1aa 100644 --- a/clang/test/Preprocessor/macro_vaopt_check.cpp +++ b/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}} diff --git a/clang/unittests/Lex/DependencyDirectivesSourceMinimizerTest.cpp b/clang/unittests/Lex/DependencyDirectivesSourceMinimizerTest.cpp index b5bba30db78da..81945cf4618c1 100644 --- a/clang/unittests/Lex/DependencyDirectivesSourceMinimizerTest.cpp +++ b/clang/unittests/Lex/DependencyDirectivesSourceMinimizerTest.cpp @@ -53,6 +53,8 @@ TEST(MinimizeSourceToDependencyDirectivesTest, AllTokens) { "#if A\n" "#ifdef A\n" "#ifndef A\n" + "#elifdef A\n" + "#elifndef A\n" "#elif A\n" "#else\n" "#include <A>\n" @@ -70,18 +72,20 @@ TEST(MinimizeSourceToDependencyDirectivesTest, AllTokens) { 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 @@ TEST(MinimizeSourceToDependencyDirectivesTest, Ifdef) { Out.data()); } +TEST(MinimizeSourceToDependencyDirectivesTest, Elifdef) { + SmallVector<char, 128> 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<char, 128> Out; @@ -341,6 +383,23 @@ TEST(MinimizeSourceToDependencyDirectivesTest, EmptyIfdef) { Out.data()); } +TEST(MinimizeSourceToDependencyDirectivesTest, EmptyElifdef) { + SmallVector<char, 128> 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<char, 128> Out; @@ -708,6 +767,29 @@ TEST(MinimizeSourceToDependencyDirectivesTest, SkippedPPRangesBasic) { EXPECT_EQ(Ranges[0].Length, (int)Out.find("#endif")); } +TEST(MinimizeSourceToDependencyDirectivesTest, SkippedPPRangesBasicElifdef) { + SmallString<128> Out; + SmallVector<Token, 32> 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<SkippedRange, 4> 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<Token, 32> Toks; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits