aboutsummaryrefslogtreecommitdiff
path: root/lib/Serialization
diff options
context:
space:
mode:
authorDouglas Gregor <dgregor@apple.com>2012-10-24 23:41:50 +0000
committerDouglas Gregor <dgregor@apple.com>2012-10-24 23:41:50 +0000
commit4c0c7e86645dfa1719d17d70e009ab49347aba62 (patch)
treea418e94002d9c7a7c6d7352b1cff125c2b256072 /lib/Serialization
parent3584972866f39d42aa4029586278022d89451bd9 (diff)
Teach the PCH validator to check the preprocessor options, especially
the macros that are #define'd or #undef'd on the command line. This checking happens much earlier than the current macro-definition checking and is far cleaner, because it does a direct comparison rather than a diff of the predefines buffers. Moreover, it allows us to use the result of this check to skip over PCH files within a directory that have non-matching -D's or -U's on the command line. Finally, it improves the diagnostics a bit for mismatches, fixing <rdar://problem/8612222>. The old predefines-buffer diff'ing will go away in a subsequent commit. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@166641 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Serialization')
-rw-r--r--lib/Serialization/ASTReader.cpp134
-rw-r--r--lib/Serialization/ASTWriter.cpp1
2 files changed, 129 insertions, 6 deletions
diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp
index ccadfbd07a..4d0ad91477 100644
--- a/lib/Serialization/ASTReader.cpp
+++ b/lib/Serialization/ASTReader.cpp
@@ -187,8 +187,8 @@ static bool checkTargetOptions(const TargetOptions &TargetOpts,
bool
PCHValidator::ReadLanguageOptions(const LangOptions &LangOpts,
bool Complain) {
- const LangOptions &PPLangOpts = PP.getLangOpts();
- return checkLanguageOptions(LangOpts, PPLangOpts,
+ const LangOptions &ExistingLangOpts = PP.getLangOpts();
+ return checkLanguageOptions(LangOpts, ExistingLangOpts,
Complain? &Reader.Diags : 0);
}
@@ -200,6 +200,119 @@ bool PCHValidator::ReadTargetOptions(const TargetOptions &TargetOpts,
}
namespace {
+ typedef llvm::StringMap<std::pair<StringRef, bool /*IsUndef*/> >
+ MacroDefinitionsMap;
+}
+
+/// \brief Collect the macro definitions provided by the given preprocessor
+/// options.
+static void collectMacroDefinitions(const PreprocessorOptions &PPOpts,
+ MacroDefinitionsMap &Macros,
+ SmallVectorImpl<StringRef> *MacroNames = 0){
+ for (unsigned I = 0, N = PPOpts.Macros.size(); I != N; ++I) {
+ StringRef Macro = PPOpts.Macros[I].first;
+ bool IsUndef = PPOpts.Macros[I].second;
+
+ std::pair<StringRef, StringRef> MacroPair = Macro.split('=');
+ StringRef MacroName = MacroPair.first;
+ StringRef MacroBody = MacroPair.second;
+
+ // For an #undef'd macro, we only care about the name.
+ if (IsUndef) {
+ if (MacroNames && !Macros.count(MacroName))
+ MacroNames->push_back(MacroName);
+
+ Macros[MacroName] = std::make_pair("", true);
+ continue;
+ }
+
+ // For a #define'd macro, figure out the actual definition.
+ if (MacroName.size() == Macro.size())
+ MacroBody = "1";
+ else {
+ // Note: GCC drops anything following an end-of-line character.
+ StringRef::size_type End = MacroBody.find_first_of("\n\r");
+ MacroBody = MacroBody.substr(0, End);
+ }
+
+ if (MacroNames && !Macros.count(MacroName))
+ MacroNames->push_back(MacroName);
+ Macros[MacroName] = std::make_pair(MacroBody, false);
+ }
+}
+
+/// \brief Check the preprocessor options deserialized from the control block
+/// against the preprocessor options in an existing preprocessor.
+///
+/// \param Diags If non-null, produce diagnostics for any mismatches incurred.
+static bool checkPreprocessorOptions(const PreprocessorOptions &PPOpts,
+ const PreprocessorOptions &ExistingPPOpts,
+ DiagnosticsEngine *Diags) {
+ // Check macro definitions.
+ MacroDefinitionsMap ASTFileMacros;
+ collectMacroDefinitions(PPOpts, ASTFileMacros);
+ MacroDefinitionsMap ExistingMacros;
+ SmallVector<StringRef, 4> ExistingMacroNames;
+ collectMacroDefinitions(ExistingPPOpts, ExistingMacros, &ExistingMacroNames);
+
+ for (unsigned I = 0, N = ExistingMacroNames.size(); I != N; ++I) {
+ // Dig out the macro definition in the existing preprocessor options.
+ StringRef MacroName = ExistingMacroNames[I];
+ std::pair<StringRef, bool> Existing = ExistingMacros[MacroName];
+
+ // Check whether we know anything about this macro name or not.
+ llvm::StringMap<std::pair<StringRef, bool /*IsUndef*/> >::iterator Known
+ = ASTFileMacros.find(MacroName);
+ if (Known == ASTFileMacros.end()) {
+ // FIXME: Check whether this identifier was referenced anywhere in the
+ // AST file. If so, we should reject the AST file. Unfortunately, this
+ // information isn't in the control block. What shall we do about it?
+ continue;
+ }
+
+ // If the macro was defined in one but undef'd in the other, we have a
+ // conflict.
+ if (Existing.second != Known->second.second) {
+ if (Diags) {
+ Diags->Report(diag::err_pch_macro_def_undef)
+ << MacroName << Known->second.second;
+ }
+ return true;
+ }
+
+ // If the macro was #undef'd in both, or if the macro bodies are identical,
+ // it's fine.
+ if (Existing.second || Existing.first == Known->second.first)
+ continue;
+
+ // The macro bodies differ; complain.
+ if (Diags) {
+ Diags->Report(diag::err_pch_macro_def_conflict)
+ << MacroName << Known->second.first << Existing.first;
+ }
+ return true;
+ }
+
+ // Check whether we're using predefines.
+ if (PPOpts.UsePredefines != ExistingPPOpts.UsePredefines) {
+ if (Diags) {
+ Diags->Report(diag::err_pch_undef) << ExistingPPOpts.UsePredefines;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+bool PCHValidator::ReadPreprocessorOptions(const PreprocessorOptions &PPOpts,
+ bool Complain) {
+ const PreprocessorOptions &ExistingPPOpts = PP.getPreprocessorOpts();
+
+ return checkPreprocessorOptions(PPOpts, ExistingPPOpts,
+ Complain? &Reader.Diags : 0);
+}
+
+namespace {
struct EmptyStringRef {
bool operator ()(StringRef r) const { return r.empty(); }
};
@@ -3389,12 +3502,15 @@ namespace {
class SimplePCHValidator : public ASTReaderListener {
const LangOptions &ExistingLangOpts;
const TargetOptions &ExistingTargetOpts;
+ const PreprocessorOptions &ExistingPPOpts;
public:
SimplePCHValidator(const LangOptions &ExistingLangOpts,
- const TargetOptions &ExistingTargetOpts)
+ const TargetOptions &ExistingTargetOpts,
+ const PreprocessorOptions &ExistingPPOpts)
: ExistingLangOpts(ExistingLangOpts),
- ExistingTargetOpts(ExistingTargetOpts)
+ ExistingTargetOpts(ExistingTargetOpts),
+ ExistingPPOpts(ExistingPPOpts)
{
}
@@ -3406,13 +3522,18 @@ namespace {
bool Complain) {
return checkTargetOptions(ExistingTargetOpts, TargetOpts, 0);
}
+ virtual bool ReadPreprocessorOptions(const PreprocessorOptions &PPOpts,
+ bool Complain) {
+ return checkPreprocessorOptions(ExistingPPOpts, PPOpts, 0);
+ }
};
}
bool ASTReader::isAcceptableASTFile(StringRef Filename,
FileManager &FileMgr,
const LangOptions &LangOpts,
- const TargetOptions &TargetOpts) {
+ const TargetOptions &TargetOpts,
+ const PreprocessorOptions &PPOpts) {
// Open the AST file.
std::string ErrStr;
OwningPtr<llvm::MemoryBuffer> Buffer;
@@ -3436,7 +3557,7 @@ bool ASTReader::isAcceptableASTFile(StringRef Filename,
return false;
}
- SimplePCHValidator Validator(LangOpts, TargetOpts);
+ SimplePCHValidator Validator(LangOpts, TargetOpts, PPOpts);
RecordData Record;
bool InControlBlock = false;
while (!Stream.AtEndOfStream()) {
@@ -3952,6 +4073,7 @@ bool ASTReader::ParsePreprocessorOptions(const RecordData &Record,
PPOpts.MacroIncludes.push_back(ReadString(Record, Idx));
}
+ PPOpts.UsePredefines = Record[Idx++];
PPOpts.ImplicitPCHInclude = ReadString(Record, Idx);
PPOpts.ImplicitPTHInclude = ReadString(Record, Idx);
PPOpts.ObjCXXARCStandardLibrary =
diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp
index 50e6763308..ea100864e3 100644
--- a/lib/Serialization/ASTWriter.cpp
+++ b/lib/Serialization/ASTWriter.cpp
@@ -1153,6 +1153,7 @@ void ASTWriter::WriteControlBlock(Preprocessor &PP, ASTContext &Context,
for (unsigned I = 0, N = PPOpts.MacroIncludes.size(); I != N; ++I)
AddString(PPOpts.MacroIncludes[I], Record);
+ Record.push_back(PPOpts.UsePredefines);
AddString(PPOpts.ImplicitPCHInclude, Record);
AddString(PPOpts.ImplicitPTHInclude, Record);
Record.push_back(static_cast<unsigned>(PPOpts.ObjCXXARCStandardLibrary));