diff --git a/docs/LanguageExtensions.html b/docs/LanguageExtensions.html index 9ac35e1dc2dc23d5d13e2c050a6e0f9bd9ca998b..1c892fd23a5a645b9ff501fce2eea0aa50c60031 100644 --- a/docs/LanguageExtensions.html +++ b/docs/LanguageExtensions.html @@ -20,6 +20,7 @@ td { <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 @@ can be used like this:</p> <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> diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td index f8c013427cac5c422fb12e866686ed83d2094227..bbc551373d967473361d9cfe003dd694b965f22e 100644 --- a/include/clang/Basic/DiagnosticLexKinds.td +++ b/include/clang/Basic/DiagnosticLexKinds.td @@ -194,6 +194,7 @@ def err_pp_expected_eol : Error< "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< diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h index d721278cbe78dd2e6efbeb05df50634c1fbc41d2..35960ff328a6d0930e973de2e4f49c8621869bcc 100644 --- a/include/clang/Lex/Preprocessor.h +++ b/include/clang/Lex/Preprocessor.h @@ -75,6 +75,8 @@ class Preprocessor { 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. diff --git a/lib/Headers/stdint.h b/lib/Headers/stdint.h index a7020d838ec96558bed6390bb9a6def96042397f..f79a0f4af557e25344d62603b7289f6f76aa1e32 100644 --- a/lib/Headers/stdint.h +++ b/lib/Headers/stdint.h @@ -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 diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp index 7ddf215020d019de0ce20170eb9230555c744c5c..699b701ea870736b9bd7d6431e52fb3dea9baae9 100644 --- a/lib/Lex/PPMacroExpansion.cpp +++ b/lib/Lex/PPMacroExpansion.cpp @@ -64,8 +64,10 @@ void Preprocessor::RegisterBuiltinMacros() { 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 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { } } +/// 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 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { 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!"); } diff --git a/test/Preprocessor/has_include.c b/test/Preprocessor/has_include.c new file mode 100644 index 0000000000000000000000000000000000000000..40697c099f4fca63dabb518e70ab9aa4126955d9 --- /dev/null +++ b/test/Preprocessor/has_include.c @@ -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>)