diff --git a/lib/Rewrite/Frontend/InclusionRewriter.cpp b/lib/Rewrite/Frontend/InclusionRewriter.cpp index cbdc702815d1428cec897895d7d535ce83306d40..878be84224ae4d23efb6715e3ce4e38b680784bb 100644 --- a/lib/Rewrite/Frontend/InclusionRewriter.cpp +++ b/lib/Rewrite/Frontend/InclusionRewriter.cpp @@ -15,7 +15,9 @@ #include "clang/Rewrite/Frontend/Rewriters.h" #include "clang/Basic/SourceManager.h" #include "clang/Frontend/PreprocessorOutputOptions.h" +#include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -74,6 +76,9 @@ private: void CommentOutDirective(Lexer &DirectivesLex, const Token &StartToken, const MemoryBuffer &FromFile, StringRef EOL, unsigned &NextToWrite, int &Lines); + bool HandleHasInclude(FileID FileId, Lexer &RawLex, + const DirectoryLookup *Lookup, Token &Tok, + bool &FileExists); const FileChange *FindFileChangeLocation(SourceLocation Loc) const; StringRef NextIdentifierName(Lexer &RawLex, Token &RawToken); }; @@ -254,6 +259,75 @@ StringRef InclusionRewriter::NextIdentifierName(Lexer &RawLex, return StringRef(); } +// Expand __has_include and __has_include_next if possible. If there's no +// definitive answer return false. +bool InclusionRewriter::HandleHasInclude( + FileID FileId, Lexer &RawLex, const DirectoryLookup *Lookup, Token &Tok, + bool &FileExists) { + // Lex the opening paren. + RawLex.LexFromRawLexer(Tok); + if (Tok.isNot(tok::l_paren)) + return false; + + RawLex.LexFromRawLexer(Tok); + + SmallString<128> FilenameBuffer; + StringRef Filename; + // Since the raw lexer doesn't give us angle_literals we have to parse them + // ourselves. + // FIXME: What to do if the file name is a macro? + if (Tok.is(tok::less)) { + RawLex.LexFromRawLexer(Tok); + + FilenameBuffer += '<'; + do { + if (Tok.is(tok::eod)) // Sanity check. + return false; + + if (Tok.is(tok::raw_identifier)) + PP.LookUpIdentifierInfo(Tok); + + // Get the string piece. + SmallVector<char, 128> TmpBuffer; + bool Invalid = false; + StringRef TmpName = PP.getSpelling(Tok, TmpBuffer, &Invalid); + if (Invalid) + return false; + + FilenameBuffer += TmpName; + + RawLex.LexFromRawLexer(Tok); + } while (Tok.isNot(tok::greater)); + + FilenameBuffer += '>'; + Filename = FilenameBuffer; + } else { + if (Tok.isNot(tok::string_literal)) + return false; + + bool Invalid = false; + Filename = PP.getSpelling(Tok, FilenameBuffer, &Invalid); + if (Invalid) + return false; + } + + // Lex the closing paren. + RawLex.LexFromRawLexer(Tok); + if (Tok.isNot(tok::r_paren)) + return false; + + // Now ask HeaderInfo if it knows about the header. + // FIXME: Subframeworks aren't handled here. Do we care? + bool isAngled = PP.GetIncludeFilenameSpelling(Tok.getLocation(), Filename); + const DirectoryLookup *CurDir; + const FileEntry *File = PP.getHeaderSearchInfo().LookupFile( + Filename, isAngled, 0, CurDir, + PP.getSourceManager().getFileEntryForID(FileId), 0, 0, 0, false); + + FileExists = File != 0; + return true; +} + /// Use a raw lexer to analyze \p FileId, inccrementally copying parts of it /// and including content of included files recursively. bool InclusionRewriter::Process(FileID FileId, @@ -291,7 +365,7 @@ bool InclusionRewriter::Process(FileID FileId, RawLex.LexFromRawLexer(RawToken); if (RawToken.is(tok::raw_identifier)) PP.LookUpIdentifierInfo(RawToken); - if (RawToken.is(tok::identifier)) { + if (RawToken.is(tok::identifier) || RawToken.is(tok::kw_if)) { switch (RawToken.getIdentifierInfo()->getPPKeywordID()) { case tok::pp_include: case tok::pp_include_next: @@ -337,6 +411,50 @@ bool InclusionRewriter::Process(FileID FileId, } break; } + case tok::pp_if: + case tok::pp_elif: + // Rewrite special builtin macros to avoid pulling in host details. + do { + // Walk over the directive. + RawLex.LexFromRawLexer(RawToken); + if (RawToken.is(tok::raw_identifier)) + PP.LookUpIdentifierInfo(RawToken); + + if (RawToken.is(tok::identifier)) { + bool HasFile; + SourceLocation Loc = RawToken.getLocation(); + + // Rewrite __has_include(x) + if (RawToken.getIdentifierInfo()->isStr("__has_include")) { + if (!HandleHasInclude(FileId, RawLex, 0, RawToken, HasFile)) + continue; + // Rewrite __has_include_next(x) + } else if (RawToken.getIdentifierInfo()->isStr( + "__has_include_next")) { + const DirectoryLookup *Lookup = PP.GetCurDirLookup(); + if (Lookup) + ++Lookup; + + if (!HandleHasInclude(FileId, RawLex, Lookup, RawToken, + HasFile)) + continue; + } else { + continue; + } + // Replace the macro with (0) or (1), followed by the commented + // out macro for reference. + OutputContentUpTo(FromFile, NextToWrite, SM.getFileOffset(Loc), + EOL, Line); + OS << '(' << (int) HasFile << ")/*"; + OutputContentUpTo(FromFile, NextToWrite, + SM.getFileOffset(RawToken.getLocation()) + + RawToken.getLength(), + EOL, Line); + OS << "*/"; + } + } while (RawToken.isNot(tok::eod)); + + break; default: break; } diff --git a/test/Frontend/Inputs/rewrite-includes8.h b/test/Frontend/Inputs/rewrite-includes8.h new file mode 100644 index 0000000000000000000000000000000000000000..e827ad99cf376cb008f270673eb990f2d2f82639 --- /dev/null +++ b/test/Frontend/Inputs/rewrite-includes8.h @@ -0,0 +1,5 @@ +#if __has_include_next(<rewrite-includes8.h>) +#elif __has_include(<rewrite-includes8.hfail>) +#endif +#if !__has_include("rewrite-includes8.h") +#endif diff --git a/test/Frontend/rewrite-includes-invalid-hasinclude.c b/test/Frontend/rewrite-includes-invalid-hasinclude.c new file mode 100644 index 0000000000000000000000000000000000000000..e32d6ad8a3ed9dbec174485ec5b873b96ff902ac --- /dev/null +++ b/test/Frontend/rewrite-includes-invalid-hasinclude.c @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -E -frewrite-includes -DFIRST -I %S/Inputs %s -o - | FileCheck -strict-whitespace %s + +#if __has_include bar.h +#endif + +#if __has_include(bar.h) +#endif + +#if __has_include(<bar.h) +#endif + +// CHECK: #if __has_include bar.h +// CHECK: #endif +// CHECK: #if __has_include(bar.h) +// CHECK: #endif +// CHECK: #if __has_include(<bar.h) +// CHECK: #endif diff --git a/test/Frontend/rewrite-includes.c b/test/Frontend/rewrite-includes.c index 546a2c44af2c6044ed5e768bd6c1f22cd912d715..bf330a60a3d6b83c03b744ae042d0f476d0e9064 100644 --- a/test/Frontend/rewrite-includes.c +++ b/test/Frontend/rewrite-includes.c @@ -18,6 +18,7 @@ A(1,2) continues */ #include "rewrite-includes7.h" #include "rewrite-includes7.h" +#include "rewrite-includes8.h" // ENDCOMPARE // CHECK: {{^}}// STARTCOMPARE{{$}} // CHECK-NEXT: {{^}}#define A(a,b) a ## b{{$}} @@ -88,6 +89,16 @@ A(1,2) // CHECK-NEXT: {{^}}#include "rewrite-includes7.h"{{$}} // CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} // CHECK-NEXT: {{^}}# 21 "{{.*}}rewrite-includes.c"{{$}} +// CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} +// CHECK-NEXT: {{^}}#include "rewrite-includes8.h"{{$}} +// CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} +// CHECK-NEXT: {{^}}# 1 "{{.*[/\\]Inputs[/\\]}}rewrite-includes8.h" 1{{$}} +// CHECK-NEXT: {{^}}#if (1)/*__has_include_next(<rewrite-includes8.h>)*/{{$}} +// CHECK-NEXT: {{^}}#elif (0)/*__has_include(<rewrite-includes8.hfail>)*/{{$}} +// CHECK-NEXT: {{^}}#endif{{$}} +// CHECK-NEXT: {{^}}#if !(1)/*__has_include("rewrite-includes8.h")*/{{$}} +// CHECK-NEXT: {{^}}#endif{{$}} +// CHECK-NEXT: {{^}}# 22 "{{.*}}rewrite-includes.c" 2{{$}} // CHECK-NEXT: {{^}}// ENDCOMPARE{{$}} // CHECKNL: {{^}}// STARTCOMPARE{{$}} @@ -142,4 +153,12 @@ A(1,2) // CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECKNL-NEXT: {{^}}#include "rewrite-includes7.h"{{$}} // CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} +// CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} +// CHECKNL-NEXT: {{^}}#include "rewrite-includes8.h"{{$}} +// CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} +// CHECKNL-NEXT: {{^}}#if (1)/*__has_include_next(<rewrite-includes8.h>)*/{{$}} +// CHECKNL-NEXT: {{^}}#elif (0)/*__has_include(<rewrite-includes8.hfail>)*/{{$}} +// CHECKNL-NEXT: {{^}}#endif{{$}} +// CHECKNL-NEXT: {{^}}#if !(1)/*__has_include("rewrite-includes8.h")*/{{$}} +// CHECKNL-NEXT: {{^}}#endif{{$}} // CHECKNL-NEXT: {{^}}// ENDCOMPARE{{$}}