[cfe-commits] r85834 - in /cfe/trunk: docs/LanguageExtensions.html include/clang/Basic/DiagnosticLexKinds.td include/clang/Lex/Preprocessor.h lib/Headers/stdint.h lib/Lex/PPMacroExpansion.cpp test/Preprocessor/has_include.c

John Thompson John.Thompson.JTSoftware at gmail.com
Mon Nov 2 14:28:13 PST 2009


Author: jtsoftware
Date: Mon Nov  2 16:28:12 2009
New Revision: 85834

URL: http://llvm.org/viewvc/llvm-project?rev=85834&view=rev
Log:
Added __has_include and __has_include_next.

Added:
    cfe/trunk/test/Preprocessor/has_include.c
Modified:
    cfe/trunk/docs/LanguageExtensions.html
    cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td
    cfe/trunk/include/clang/Lex/Preprocessor.h
    cfe/trunk/lib/Headers/stdint.h
    cfe/trunk/lib/Lex/PPMacroExpansion.cpp

Modified: cfe/trunk/docs/LanguageExtensions.html
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/LanguageExtensions.html?rev=85834&r1=85833&r2=85834&view=diff

==============================================================================
--- cfe/trunk/docs/LanguageExtensions.html (original)
+++ cfe/trunk/docs/LanguageExtensions.html Mon Nov  2 16:28:12 2009
@@ -20,6 +20,7 @@
 <ul>
 <li><a href="#intro">Introduction</a></li>
 <li><a href="#feature_check">Feature Checking Macros</a></li>
+<li><a href="#has_include">Include File Checking Macros</a></li>
 <li><a href="#builtinmacros">Builtin Macros</a></li>
 <li><a href="#vectors">Vectors and Extended Vectors</a></li>
 <li><a href="#blocks">Blocks</a></li>
@@ -112,6 +113,69 @@
 
 <p>The feature tag is described along with the language feature below.</p>
 
+<!-- ======================================================================= -->
+<h2 id="has_include">Include File Checking Macros</h2>
+<!-- ======================================================================= -->
+
+<p>Not all developments systems have the same include files.
+The <a href="#__has_include">__has_include</a> and
+<a href="#__has_include_next">__has_include_next</a> macros allow you to
+check for the existence of an include file before doing
+a possibly failing #include directive.</p>
+
+<!-- ======================================================================= -->
+<h3 id="__has_include">__has_include</h3>
+<!-- ======================================================================= -->
+
+<p>This function-like macro takes a single file name string argument that
+is the name of an include file.  It evaluates to 1 if the file can
+be found using the include paths, or 0 otherwise:</p>
+
+<blockquote>
+<pre>
+// Note the two possible file name string formats.
+#if __has_include("myinclude.h") && __has_include(<stdint.h>)
+# include "myinclude.h"
+#endif
+
+// To avoid problem with non-clang compilers not having this macro.
+#if defined(__has_include) && __has_include("myinclude.h")
+# include "myinclude.h"
+#endif
+</pre>
+</blockquote>
+
+<p>To test for this feature, use #if defined(__has_include).</p>
+
+<!-- ======================================================================= -->
+<h3 id="__has_include_next">__has_include_next</h3>
+<!-- ======================================================================= -->
+
+<p>This function-like macro takes a single file name string argument that
+is the name of an include file.  It is like __has_include except that it
+looks for the second instance of the given file found in the include
+paths.  It evaluates to 1 if the second instance of the file can
+be found using the include paths, or 0 otherwise:</p>
+
+<blockquote>
+<pre>
+// Note the two possible file name string formats.
+#if __has_include_next("myinclude.h") && __has_include_next(<stdint.h>)
+# include_next "myinclude.h"
+#endif
+
+// To avoid problem with non-clang compilers not having this macro.
+#if defined(__has_include_next) && __has_include_next("myinclude.h")
+# include_next "myinclude.h"
+#endif
+</pre>
+</blockquote>
+
+<p>Note that __has_include_next, like the GNU extension
+#include_next directive, is intended for use in headers only,
+and will issue a warning if used in the top-level compilation
+file.  A warning will also be issued if an absolute path
+is used in the file argument.</p>
 
 <!-- ======================================================================= -->
 <h2 id="builtinmacros">Builtin Macros</h2>

Modified: cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td?rev=85834&r1=85833&r2=85834&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td Mon Nov  2 16:28:12 2009
@@ -194,6 +194,7 @@
   "expected end of line in preprocessor expression">;
 def err_pp_defined_requires_identifier : Error<
   "operator 'defined' requires an identifier">;
+def err_pp_missing_lparen : Error<"missing '(' after '%0'">;
 def err_pp_missing_rparen : Error<"missing ')' after '%0'">;
 def err_pp_colon_without_question : Error<"':' without preceding '?'">;
 def err_pp_division_by_zero : Error<

Modified: cfe/trunk/include/clang/Lex/Preprocessor.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Lex/Preprocessor.h?rev=85834&r1=85833&r2=85834&view=diff

==============================================================================
--- cfe/trunk/include/clang/Lex/Preprocessor.h (original)
+++ cfe/trunk/include/clang/Lex/Preprocessor.h Mon Nov  2 16:28:12 2009
@@ -75,6 +75,8 @@
   IdentifierInfo *Ident_Pragma, *Ident__VA_ARGS__; // _Pragma, __VA_ARGS__
   IdentifierInfo *Ident__has_feature;              // __has_feature
   IdentifierInfo *Ident__has_builtin;              // __has_builtin
+  IdentifierInfo *Ident__has_include;              // __has_include
+  IdentifierInfo *Ident__has_include_next;         // __has_include_next
 
   SourceLocation DATELoc, TIMELoc;
   unsigned CounterValue;  // Next __COUNTER__ value.

Modified: cfe/trunk/lib/Headers/stdint.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Headers/stdint.h?rev=85834&r1=85833&r2=85834&view=diff

==============================================================================
--- cfe/trunk/lib/Headers/stdint.h (original)
+++ cfe/trunk/lib/Headers/stdint.h Mon Nov  2 16:28:12 2009
@@ -28,7 +28,8 @@
 /* If we're hosted, fall back to the system's stdint.h, which might have
  * additional definitions.
  */
-#if __STDC_HOSTED__
+#if __STDC_HOSTED__ && \
+    defined(__has_include_next) && __has_include_next(<stdint.h>)
 # include_next <stdint.h>
 #else
 

Modified: cfe/trunk/lib/Lex/PPMacroExpansion.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/PPMacroExpansion.cpp?rev=85834&r1=85833&r2=85834&view=diff

==============================================================================
--- cfe/trunk/lib/Lex/PPMacroExpansion.cpp (original)
+++ cfe/trunk/lib/Lex/PPMacroExpansion.cpp Mon Nov  2 16:28:12 2009
@@ -64,8 +64,10 @@
   Ident__TIMESTAMP__     = RegisterBuiltinMacro(*this, "__TIMESTAMP__");
 
   // Clang Extensions.
-  Ident__has_feature     = RegisterBuiltinMacro(*this, "__has_feature");
-  Ident__has_builtin     = RegisterBuiltinMacro(*this, "__has_builtin");
+  Ident__has_feature      = RegisterBuiltinMacro(*this, "__has_feature");
+  Ident__has_builtin      = RegisterBuiltinMacro(*this, "__has_builtin");
+  Ident__has_include      = RegisterBuiltinMacro(*this, "__has_include");
+  Ident__has_include_next = RegisterBuiltinMacro(*this, "__has_include_next");
 }
 
 /// isTrivialSingleTokenExpansion - Return true if MI, which has a single token
@@ -503,6 +505,117 @@
   }
 }
 
+/// EvaluateHasIncludeCommon - Process a '__has_include("path")'
+/// or '__has_include_next("path")' expression.
+/// Returns true if successful.
+static bool EvaluateHasIncludeCommon(bool &Result, Token &Tok,
+        IdentifierInfo *II, Preprocessor &PP,
+        const DirectoryLookup *LookupFrom) {
+  SourceLocation LParenLoc;
+
+  // Get '('.
+  PP.LexNonComment(Tok);
+
+  // Ensure we have a '('.
+  if (Tok.isNot(tok::l_paren)) {
+    PP.Diag(Tok.getLocation(), diag::err_pp_missing_lparen) << II->getName();
+    return false;
+  }
+
+  // Save '(' location for possible missing ')' message.
+  LParenLoc = Tok.getLocation();
+
+  // Get the file name.
+  PP.getCurrentLexer()->LexIncludeFilename(Tok);
+
+  // Reserve a buffer to get the spelling.
+  llvm::SmallVector<char, 128> FilenameBuffer;
+  const char *FilenameStart, *FilenameEnd;
+
+  switch (Tok.getKind()) {
+  case tok::eom:
+    // If the token kind is EOM, the error has already been diagnosed.
+    return false;
+
+  case tok::angle_string_literal:
+  case tok::string_literal: {
+    FilenameBuffer.resize(Tok.getLength());
+    FilenameStart = &FilenameBuffer[0];
+    unsigned Len = PP.getSpelling(Tok, FilenameStart);
+    FilenameEnd = FilenameStart+Len;
+    break;
+  }
+
+  case tok::less:
+    // This could be a <foo/bar.h> file coming from a macro expansion.  In this
+    // case, glue the tokens together into FilenameBuffer and interpret those.
+    FilenameBuffer.push_back('<');
+    if (PP.ConcatenateIncludeName(FilenameBuffer))
+      return false;   // Found <eom> but no ">"?  Diagnostic already emitted.
+    FilenameStart = FilenameBuffer.data();
+    FilenameEnd = FilenameStart + FilenameBuffer.size();
+    break;
+  default:
+    PP.Diag(Tok.getLocation(), diag::err_pp_expects_filename);
+    return false;
+  }
+
+  bool isAngled = PP.GetIncludeFilenameSpelling(Tok.getLocation(),
+                                             FilenameStart, FilenameEnd);
+  // If GetIncludeFilenameSpelling set the start ptr to null, there was an
+  // error.
+  if (FilenameStart == 0) {
+    return false;
+  }
+
+  // Search include directories.
+  const DirectoryLookup *CurDir;
+  const FileEntry *File = PP.LookupFile(FilenameStart, FilenameEnd,
+                                     isAngled, LookupFrom, CurDir);
+
+  // Get the result value.  Result = true means the file exists.
+  Result = File != 0;
+
+  // Get ')'.
+  PP.LexNonComment(Tok);
+
+  // Ensure we have a trailing ).
+  if (Tok.isNot(tok::r_paren)) {
+    PP.Diag(Tok.getLocation(), diag::err_pp_missing_rparen) << II->getName();
+    PP.Diag(LParenLoc, diag::note_matching) << "(";
+    return false;
+  }
+
+  return true;
+}
+
+/// EvaluateHasInclude - Process a '__has_include("path")' expression.
+/// Returns true if successful.
+static bool EvaluateHasInclude(bool &Result, Token &Tok, IdentifierInfo *II,
+                               Preprocessor &PP) {
+  return(EvaluateHasIncludeCommon(Result, Tok, II, PP, NULL));
+}
+
+/// EvaluateHasIncludeNext - Process '__has_include_next("path")' expression.
+/// Returns true if successful.
+static bool EvaluateHasIncludeNext(bool &Result, Token &Tok,
+                                   IdentifierInfo *II, Preprocessor &PP) {
+  // __has_include_next is like __has_include, except that we start
+  // searching after the current found directory.  If we can't do this,
+  // issue a diagnostic.
+  const DirectoryLookup *Lookup = PP.GetCurDirLookup();
+  if (PP.isInPrimaryFile()) {
+    Lookup = 0;
+    PP.Diag(Tok, diag::pp_include_next_in_primary);
+  } else if (Lookup == 0) {
+    PP.Diag(Tok, diag::pp_include_next_absolute_path);
+  } else {
+    // Start looking up in the next directory.
+    ++Lookup;
+  }
+
+  return(EvaluateHasIncludeCommon(Result, Tok, II, PP, Lookup));
+}
 
 /// ExpandBuiltinMacro - If an identifier token is read that is to be expanded
 /// as a builtin macro, handle it and return the next token as 'Tok'.
@@ -671,6 +784,20 @@
     sprintf(TmpBuffer, "%d", (int)Value);
     Tok.setKind(tok::numeric_constant);
     CreateString(TmpBuffer, strlen(TmpBuffer), Tok, Tok.getLocation());
+  } else if (II == Ident__has_include ||
+             II == Ident__has_include_next) {
+    // The argument to these two builtins should be a parenthesized
+    // file name string literal using angle brackets (<>) or
+    // double-quotes ("").
+    bool Value = false;
+    bool IsValid;
+    if (II == Ident__has_include)
+      IsValid = EvaluateHasInclude(Value, Tok, II, *this);
+    else
+      IsValid = EvaluateHasIncludeNext(Value, Tok, II, *this);
+    sprintf(TmpBuffer, "%d", (int)Value);
+    Tok.setKind(tok::numeric_constant);
+    CreateString(TmpBuffer, strlen(TmpBuffer), Tok, Tok.getLocation());
   } else {
     assert(0 && "Unknown identifier!");
   }

Added: cfe/trunk/test/Preprocessor/has_include.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Preprocessor/has_include.c?rev=85834&view=auto

==============================================================================
--- cfe/trunk/test/Preprocessor/has_include.c (added)
+++ cfe/trunk/test/Preprocessor/has_include.c Mon Nov  2 16:28:12 2009
@@ -0,0 +1,83 @@
+// RUN: clang-cc -Eonly -verify %s
+
+// Try different path permutations of __has_include with existing file.
+#if __has_include("stdio.h")
+#else
+  #error "__has_include failed (1)."
+#endif
+
+#if __has_include(<stdio.h>)
+#else
+  #error "__has_include failed (2)."
+#endif
+
+// Try unary expression.
+#if !__has_include("stdio.h")
+  #error "__has_include failed (5)."
+#endif
+
+// Try binary expression.
+#if __has_include("stdio.h") && __has_include("stddef.h")
+#else
+  #error "__has_include failed (6)."
+#endif
+
+// Try non-existing file.
+#if __has_include("blahblah.h")
+  #error "__has_include failed (7)."
+#endif
+
+// Try defined.
+#if !defined(__has_include)
+  #error "defined(__has_include) failed (8)."
+#endif
+
+// Try different path permutations of __has_include_next with existing file.
+#if __has_include_next("stddef.h") // expected-warning {{#include_next in primary source file}}
+#else
+  #error "__has_include failed (1)."
+#endif
+
+#if __has_include_next(<stddef.h>) // expected-warning {{#include_next in primary source file}}
+#else
+  #error "__has_include failed (2)."
+#endif
+
+// Try unary expression.
+#if !__has_include_next("stdio.h") // expected-warning {{#include_next in primary source file}}
+  #error "__has_include_next failed (5)."
+#endif
+
+// Try binary expression.
+#if __has_include_next("stdio.h") && __has_include("stddef.h") // expected-warning {{#include_next in primary source file}}
+#else
+  #error "__has_include_next failed (6)."
+#endif
+
+// Try non-existing file.
+#if __has_include_next("blahblah.h") // expected-warning {{#include_next in primary source file}}
+  #error "__has_include_next failed (7)."
+#endif
+
+// Try defined.
+#if !defined(__has_include_next)
+  #error "defined(__has_include_next) failed (8)."
+#endif
+
+// Try badly formed expressions.
+// FIXME: I don't quite know how to avoid preprocessor side effects.
+// Use FileCheck?
+// It also assert due to unterminated #if's.
+//#if __has_include("stdio.h"
+//#if __has_include "stdio.h")
+//#if __has_include(stdio.h)
+//#if __has_include()
+//#if __has_include(
+//#if __has_include)
+//#if __has_include
+//#if __has_include(<stdio.h>
+//#if __has_include<stdio.h>)
+//#if __has_include("stdio.h)
+//#if __has_include(stdio.h")
+//#if __has_include(<stdio.h)
+//#if __has_include(stdio.h>)





More information about the cfe-commits mailing list