[clang] 8edd346 - Add support for #elifdef and #elifndef
Aaron Ballman via cfe-commits
cfe-commits at lists.llvm.org
Thu May 27 05:58:01 PDT 2021
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 at +4 {{"got it"}} */
+#ifdef FOO
+#elifdef BAR
+#else
+#error "got it"
+#endif
+
+/* expected-error at +3 {{"got it"}} */
+#ifdef FOO
+#elifndef BAR
+#error "got it"
+#endif
+
+/* expected-error at +3 {{"got it"}} */
+#ifdef FOO
+#elifndef BAR
+#error "got it"
+#else
+#error "did not expect to get here"
+#endif
+
+#define BAR
+/* expected-error at +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 at +4 {{"got it"}} */
+#ifdef FOO
+#elifdef BAR // test that comments aren't an issue
+#else
+#error "got it"
+#endif
+
+/* expected-error at +3 {{"got it"}} */
+#ifdef FOO
+#elifndef BAR // test that comments aren't an issue
+#error "got it"
+#endif
+
+/* expected-error at +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 at +3 {{"got it"}} */
+#ifdef FOO
+#elifdef BAR // test that comments aren't an issue
+#error "got it"
+#endif
+#undef BAR
+
+#define BAR
+/* expected-error at +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 at +3 {{#elifdef after #else}} */
+#ifdef FOO
+#else
+#elifdef BAR
+#endif
+
+/* expected-error at +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 at +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 at +2 {{macro name missing}} */
+#ifdef FOO
+#elifdef
+#endif
+
+/* expected-error at +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 at +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;
More information about the cfe-commits
mailing list