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.
This commit is contained in:
Aaron Ballman 2021-05-27 08:41:00 -04:00
parent ce4f99e7f2
commit 8edd3464af
24 changed files with 469 additions and 28 deletions

View File

@ -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">;

View File

@ -98,6 +98,8 @@ PPKEYWORD(if)
PPKEYWORD(ifdef)
PPKEYWORD(ifndef)
PPKEYWORD(elif)
PPKEYWORD(elifdef)
PPKEYWORD(elifndef)
PPKEYWORD(else)
PPKEYWORD(endif)
PPKEYWORD(defined)

View File

@ -44,6 +44,8 @@ enum TokenKind {
pp_ifdef,
pp_ifndef,
pp_elif,
pp_elifdef,
pp_elifndef,
pp_else,
pp_endif,
decl_at_import,

View File

@ -351,6 +351,22 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
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);

View File

@ -93,6 +93,14 @@ private:
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;
};

View File

@ -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,

View File

@ -2357,7 +2357,8 @@ private:
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);

View File

@ -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);

View File

@ -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;

View File

@ -77,6 +77,23 @@ public:
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 {

View File

@ -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;

View File

@ -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);

View File

@ -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()));

View File

@ -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());

View File

@ -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) {

View File

@ -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";

View File

@ -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());

View File

@ -35,6 +35,8 @@ FOO(in,t) value;
// 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)

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -2,6 +2,7 @@
// This should not be rejected.
#ifdef defined
#elifdef defined
#endif

View File

@ -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}}

View File

@ -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;