diff options
author | Douglas Gregor <dgregor@apple.com> | 2012-10-24 23:41:50 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2012-10-24 23:41:50 +0000 |
commit | 4c0c7e86645dfa1719d17d70e009ab49347aba62 (patch) | |
tree | a418e94002d9c7a7c6d7352b1cff125c2b256072 /lib/Serialization | |
parent | 3584972866f39d42aa4029586278022d89451bd9 (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.cpp | 134 | ||||
-rw-r--r-- | lib/Serialization/ASTWriter.cpp | 1 |
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)); |