diff options
author | Aaron Ballman <aaron@aaronballman.com> | 2012-03-02 22:51:54 +0000 |
---|---|---|
committer | Aaron Ballman <aaron@aaronballman.com> | 2012-03-02 22:51:54 +0000 |
commit | 4c55c54db8e676aa3e042188773d9d82d59fff91 (patch) | |
tree | 42b222d331d777115f834eb5efae4cc1a8acf2b9 | |
parent | 7ec419aa8f4ff83bc8ff707b45b9bec47fef3a1a (diff) |
Adding support for #pragma include_alias in MS compatibility mode. This implements PR 10705.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@151949 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/Basic/DiagnosticLexKinds.td | 13 | ||||
-rw-r--r-- | include/clang/Lex/HeaderSearch.h | 36 | ||||
-rw-r--r-- | include/clang/Lex/Preprocessor.h | 1 | ||||
-rw-r--r-- | lib/Lex/PPDirectives.cpp | 10 | ||||
-rw-r--r-- | lib/Lex/Pragma.cpp | 116 | ||||
-rw-r--r-- | test/Preprocessor/pragma_microsoft.c | 43 |
6 files changed, 219 insertions, 0 deletions
diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td index 46e0bebb3e..12f23cea50 100644 --- a/include/clang/Basic/DiagnosticLexKinds.td +++ b/include/clang/Basic/DiagnosticLexKinds.td @@ -292,6 +292,19 @@ def warn_has_warning_invalid_option : ExtWarn<"__has_warning expected option name (e.g. \"-Wundef\")">, InGroup<MalformedWarningCheck>; +def warn_pragma_include_alias_mismatch_angle : + ExtWarn<"angle-bracketed include <%0> cannot be aliased to double-quoted " + "include \"%1\"">, InGroup<UnknownPragmas>; +def warn_pragma_include_alias_mismatch_quote : + ExtWarn<"double-quoted include \"%0\" cannot be aliased to angle-bracketed " + "include <%1>">, InGroup<UnknownPragmas>; +def warn_pragma_include_alias_expected : + ExtWarn<"pragma include_alias expected '%0'">, + InGroup<UnknownPragmas>; +def warn_pragma_include_alias_expected_filename : + ExtWarn<"pragma include_alias expected include filename">, + InGroup<UnknownPragmas>; + def err__Pragma_malformed : Error< "_Pragma takes a parenthesized string literal">; def err_pragma_comment_malformed : Error< diff --git a/include/clang/Lex/HeaderSearch.h b/include/clang/Lex/HeaderSearch.h index 84bb37da3a..8ed2ec3362 100644 --- a/include/clang/Lex/HeaderSearch.h +++ b/include/clang/Lex/HeaderSearch.h @@ -19,6 +19,7 @@ #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/Allocator.h" +#include "llvm/ADT/OwningPtr.h" #include <vector> namespace clang { @@ -154,6 +155,13 @@ class HeaderSearch { llvm::StringMap<const DirectoryEntry *, llvm::BumpPtrAllocator> FrameworkMap; + /// IncludeAliases - maps include file names (including the quotes or + /// angle brackets) to other include file names. This is used to support the + /// include_alias pragma for Microsoft compatibility. + typedef llvm::StringMap<std::string, llvm::BumpPtrAllocator> + IncludeAliasMap; + OwningPtr<IncludeAliasMap> IncludeAliases; + /// HeaderMaps - This is a mapping from FileEntry -> HeaderMap, uniquing /// headermaps. This vector owns the headermap. std::vector<std::pair<const FileEntry*, const HeaderMap*> > HeaderMaps; @@ -217,6 +225,34 @@ public: SystemDirIdx++; } + /// HasIncludeAliasMap - Checks whether the map exists or not + bool HasIncludeAliasMap() const { + return IncludeAliases; + } + + /// AddIncludeAlias - Map the source include name to the dest include name. + /// The Source should include the angle brackets or quotes, the dest + /// should not. This allows for distinction between <> and "" headers. + void AddIncludeAlias(StringRef Source, StringRef Dest) { + if (!IncludeAliases) + IncludeAliases.reset(new IncludeAliasMap); + (*IncludeAliases)[Source] = Dest; + } + + /// MapHeaderToIncludeAlias - Maps one header file name to a different header + /// file name, for use with the include_alias pragma. Note that the source + /// file name should include the angle brackets or quotes. Returns StringRef + /// as null if the header cannot be mapped. + StringRef MapHeaderToIncludeAlias(StringRef Source) { + assert(IncludeAliases && "Trying to map headers when there's no map"); + + // Do any filename replacements before anything else + IncludeAliasMap::const_iterator Iter = IncludeAliases->find(Source); + if (Iter != IncludeAliases->end()) + return Iter->second; + return StringRef(); + } + /// \brief Set the path to the module cache. void setModuleCachePath(StringRef CachePath) { ModuleCachePath = CachePath; diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h index feed6a8bfa..9ee9e82529 100644 --- a/include/clang/Lex/Preprocessor.h +++ b/include/clang/Lex/Preprocessor.h @@ -1262,6 +1262,7 @@ public: void HandlePragmaMessage(Token &MessageTok); void HandlePragmaPushMacro(Token &Tok); void HandlePragmaPopMacro(Token &Tok); + void HandlePragmaIncludeAlias(Token &Tok); IdentifierInfo *ParsePragmaPushOrPopMacro(Token &Tok); // Return true and store the first token only if any CommentHandler diff --git a/lib/Lex/PPDirectives.cpp b/lib/Lex/PPDirectives.cpp index b08bcffd20..38256815ef 100644 --- a/lib/Lex/PPDirectives.cpp +++ b/lib/Lex/PPDirectives.cpp @@ -1274,6 +1274,7 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc, return; } + StringRef OriginalFilename = Filename; bool isAngled = GetIncludeFilenameSpelling(FilenameTok.getLocation(), Filename); // If GetIncludeFilenameSpelling set the start ptr to null, there was an @@ -1304,6 +1305,15 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc, PragmaARCCFCodeAuditedLoc = SourceLocation(); } + if (HeaderInfo.HasIncludeAliasMap()) { + // Map the filename with the brackets still attached. If the name doesn't + // map to anything, fall back on the filename we've already gotten the + // spelling for. + StringRef NewName = HeaderInfo.MapHeaderToIncludeAlias(OriginalFilename); + if (!NewName.empty()) + Filename = NewName; + } + // Search include directories. const DirectoryLookup *CurDir; SmallString<1024> SearchPath; diff --git a/lib/Lex/Pragma.cpp b/lib/Lex/Pragma.cpp index 503fffe53d..8a8789be0a 100644 --- a/lib/Lex/Pragma.cpp +++ b/lib/Lex/Pragma.cpp @@ -663,6 +663,112 @@ void Preprocessor::HandlePragmaPopMacro(Token &PopMacroTok) { } } +void Preprocessor::HandlePragmaIncludeAlias(Token &Tok) { + // We will either get a quoted filename or a bracketed filename, and we + // have to track which we got. The first filename is the source name, + // and the second name is the mapped filename. If the first is quoted, + // the second must be as well (cannot mix and match quotes and brackets). + SourceLocation Loc = Tok.getLocation(); + + // Get the open paren + Lex(Tok); + if (Tok.isNot(tok::l_paren)) { + Diag(Tok, diag::warn_pragma_include_alias_expected) << "("; + return; + } + + // We expect either a quoted string literal, or a bracketed name + Token SourceFilenameTok; + CurPPLexer->LexIncludeFilename(SourceFilenameTok); + if (SourceFilenameTok.is(tok::eod)) { + // The diagnostic has already been handled + return; + } + + StringRef SourceFileName; + SmallString<128> FileNameBuffer; + if (SourceFilenameTok.is(tok::string_literal) || + SourceFilenameTok.is(tok::angle_string_literal)) { + SourceFileName = getSpelling(SourceFilenameTok, FileNameBuffer); + } else if (SourceFilenameTok.is(tok::less)) { + // This could be a path instead of just a name + FileNameBuffer.push_back('<'); + SourceLocation End; + if (ConcatenateIncludeName(FileNameBuffer, End)) + return; // Diagnostic already emitted + SourceFileName = FileNameBuffer.str(); + } else { + Diag(Tok, diag::warn_pragma_include_alias_expected_filename); + return; + } + FileNameBuffer.clear(); + + // Now we expect a comma, followed by another include name + Lex(Tok); + if (Tok.isNot(tok::comma)) { + Diag(Tok, diag::warn_pragma_include_alias_expected) << ","; + return; + } + + Token ReplaceFilenameTok; + CurPPLexer->LexIncludeFilename(ReplaceFilenameTok); + if (ReplaceFilenameTok.is(tok::eod)) { + // The diagnostic has already been handled + return; + } + + StringRef ReplaceFileName; + if (ReplaceFilenameTok.is(tok::string_literal) || + ReplaceFilenameTok.is(tok::angle_string_literal)) { + ReplaceFileName = getSpelling(ReplaceFilenameTok, FileNameBuffer); + } else if (ReplaceFilenameTok.is(tok::less)) { + // This could be a path instead of just a name + FileNameBuffer.push_back('<'); + SourceLocation End; + if (ConcatenateIncludeName(FileNameBuffer, End)) + return; // Diagnostic already emitted + ReplaceFileName = FileNameBuffer.str(); + } else { + Diag(Tok, diag::warn_pragma_include_alias_expected_filename); + return; + } + + // Finally, we expect the closing paren + Lex(Tok); + if (Tok.isNot(tok::r_paren)) { + Diag(Tok, diag::warn_pragma_include_alias_expected) << ")"; + return; + } + + // Now that we have the source and target filenames, we need to make sure + // they're both of the same type (angled vs non-angled) + StringRef OriginalSource = SourceFileName; + + bool SourceIsAngled = + GetIncludeFilenameSpelling(SourceFilenameTok.getLocation(), + SourceFileName); + bool ReplaceIsAngled = + GetIncludeFilenameSpelling(ReplaceFilenameTok.getLocation(), + ReplaceFileName); + if (!SourceFileName.empty() && !ReplaceFileName.empty() && + (SourceIsAngled != ReplaceIsAngled)) { + unsigned int DiagID; + if (SourceIsAngled) + DiagID = diag::warn_pragma_include_alias_mismatch_angle; + else + DiagID = diag::warn_pragma_include_alias_mismatch_quote; + + Diag(SourceFilenameTok.getLocation(), DiagID) + << SourceFileName + << ReplaceFileName; + + return; + } + + // Now we can let the include handler know about this mapping + getHeaderSearchInfo().AddIncludeAlias(OriginalSource, ReplaceFileName); +} + /// AddPragmaHandler - Add the specified pragma handler to the preprocessor. /// If 'Namespace' is non-null, then it is a token required to exist on the /// pragma line before the pragma string starts, e.g. "STDC" or "GCC". @@ -943,6 +1049,15 @@ struct PragmaCommentHandler : public PragmaHandler { } }; +/// PragmaIncludeAliasHandler - "#pragma include_alias("...")". +struct PragmaIncludeAliasHandler : public PragmaHandler { + PragmaIncludeAliasHandler() : PragmaHandler("include_alias") {} + virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer, + Token &IncludeAliasTok) { + PP.HandlePragmaIncludeAlias(IncludeAliasTok); + } +}; + /// PragmaMessageHandler - "#pragma message("...")". struct PragmaMessageHandler : public PragmaHandler { PragmaMessageHandler() : PragmaHandler("message") {} @@ -1095,5 +1210,6 @@ void Preprocessor::RegisterBuiltinPragmas() { // MS extensions. if (Features.MicrosoftExt) { AddPragmaHandler(new PragmaCommentHandler()); + AddPragmaHandler(new PragmaIncludeAliasHandler()); } } diff --git a/test/Preprocessor/pragma_microsoft.c b/test/Preprocessor/pragma_microsoft.c index b68d6e363e..e461c707a9 100644 --- a/test/Preprocessor/pragma_microsoft.c +++ b/test/Preprocessor/pragma_microsoft.c @@ -38,3 +38,46 @@ void f() // this warning should go away. MACRO_WITH__PRAGMA // expected-warning {{expression result unused}} } + + +// This should include macro_arg_directive even though the include +// is looking for test.h This allows us to assign to "n" +#pragma include_alias("test.h", "macro_arg_directive.h" ) +#include "test.h" +void test( void ) { + n = 12; +} + +#pragma include_alias(<bar.h>, "bar.h") // expected-warning {{angle-bracketed include <bar.h> cannot be aliased to double-quoted include "bar.h"}} +#pragma include_alias("foo.h", <bar.h>) // expected-warning {{double-quoted include "foo.h" cannot be aliased to angle-bracketed include <bar.h>}} +#pragma include_alias("test.h") // expected-warning {{pragma include_alias expected ','}} + +// Make sure that the names match exactly for a replacement, including path information. If +// this were to fail, we would get a file not found error +#pragma include_alias(".\pp-record.h", "does_not_exist.h") +#include "pp-record.h" + +#pragma include_alias(12) // expected-warning {{pragma include_alias expected include filename}} + +// It's expected that we can map "bar" and <bar> separately +#define test +// We can't actually look up stdio.h because we're using cc1 without header paths, but this will ensure +// that we get the right bar.h, because the "bar.h" will undef test for us, where <bar.h> won't +#pragma include_alias(<bar.h>, <stdio.h>) +#pragma include_alias("bar.h", "pr2086.h") // This should #undef test + +#include "bar.h" +#if defined(test) +// This should not warn because test should not be defined +#pragma include_alias("test.h") +#endif + +// Test to make sure there are no use-after-free problems +#define B "pp-record.h" +#pragma include_alias("quux.h", B) +void g() {} +#include "quux.h" + +// Make sure that empty includes don't work +#pragma include_alias("", "foo.h") // expected-error {{empty filename}} +#pragma include_alias(<foo.h>, <>) // expected-error {{empty filename}} |