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
This commit is contained in:
Aaron Ballman 2012-03-02 22:51:54 +00:00
Родитель 7ec419aa8f
Коммит 4c55c54db8
6 изменённых файлов: 219 добавлений и 0 удалений

Просмотреть файл

@ -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<

Просмотреть файл

@ -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;

Просмотреть файл

@ -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

Просмотреть файл

@ -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;

Просмотреть файл

@ -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());
}
}

Просмотреть файл

@ -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}}