[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