aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/Basic/DiagnosticFrontendKinds.td15
-rw-r--r--include/clang/Frontend/PCHReader.h15
-rw-r--r--lib/Frontend/PCHReader.cpp224
-rw-r--r--test/PCH/fuzzy-pch.c17
-rw-r--r--test/PCH/fuzzy-pch.h2
-rw-r--r--tools/clang-cc/clang-cc.cpp8
6 files changed, 230 insertions, 51 deletions
diff --git a/include/clang/Basic/DiagnosticFrontendKinds.td b/include/clang/Basic/DiagnosticFrontendKinds.td
index 64a1777924..2c5dfcca09 100644
--- a/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -110,13 +110,18 @@ def warn_pch_gc_mode : Warning<
"the PCH file was built with %select{no||hybrid}0 garbage collection but "
"the current translation unit will compiled with %select{no||hybrid}1 "
"garbage collection">;
-def warn_pch_preprocessor : Warning<
- "the PCH file was built with different preprocessor definitions than the "
- "current translation unit">;
-def note_predef_in_pch : Note<
- "preprocessor definitions in PCH file">;
def warn_pch_version_too_old : Warning<
"PCH file uses an older PCH format that is no longer supported">;
def warn_pch_version_too_new : Warning<
"PCH file uses a newer PCH format that cannot be read">;
+def warn_cmdline_conflicting_macro_def : Warning<
+ "definition of the macro '%0' conflicts with the definition used to "
+ "build the precompiled header">;
+def note_pch_macro_defined_as : Note<
+ "definition of macro '%0' in the precompiled header">;
+def warn_cmdline_missing_macro_defs : Warning<
+ "macro definitions used to build the precompiled header are missing">;
+def note_using_macro_def_from_pch : Note<
+ "using this macro definition from precompiled header">;
+
}
diff --git a/include/clang/Frontend/PCHReader.h b/include/clang/Frontend/PCHReader.h
index c0e869fc1f..907ab7e0da 100644
--- a/include/clang/Frontend/PCHReader.h
+++ b/include/clang/Frontend/PCHReader.h
@@ -280,6 +280,16 @@ private:
/// Objective-C protocols.
llvm::SmallVector<Decl *, 16> InterestingDecls;
+ /// \brief Suggested contents of the predefines buffer, after this
+ /// PCH file has been processed.
+ ///
+ /// In most cases, this string will be empty, because the predefines
+ /// buffer computed to build the PCH file will be identical to the
+ /// predefines buffer computed from the command line. However, when
+ /// there are differences that the PCH reader can work around, this
+ /// predefines buffer may contain additional definitions.
+ std::string SuggestedPredefines;
+
PCHReadResult ReadPCHBlock();
bool CheckPredefinesBuffer(const char *PCHPredef,
unsigned PCHPredefLen,
@@ -303,6 +313,11 @@ public:
PCHReadResult ReadPCH(const std::string &FileName);
+ /// \brief Returns the suggested contents of the predefines buffer,
+ /// which contains a (typically-empty) subset of the predefines
+ /// build prior to including the precompiled header.
+ const std::string &getSuggestedPredefines() { return SuggestedPredefines; }
+
/// \brief Resolve a type ID into a type, potentially building a new
/// type.
virtual QualType GetType(pch::TypeID ID);
diff --git a/lib/Frontend/PCHReader.cpp b/lib/Frontend/PCHReader.cpp
index 6dd7806d0a..df9f301f79 100644
--- a/lib/Frontend/PCHReader.cpp
+++ b/lib/Frontend/PCHReader.cpp
@@ -30,6 +30,7 @@
#include "llvm/Support/Compiler.h"
#include "llvm/Support/MemoryBuffer.h"
#include <algorithm>
+#include <iterator>
#include <cstdio>
#include <sys/stat.h>
using namespace clang;
@@ -306,6 +307,47 @@ static bool Error(const char *Str) {
return true;
}
+/// \brief Split the given string into a vector of lines, eliminating
+/// any empty lines in the process.
+///
+/// \param Str the string to split.
+/// \param Len the length of Str.
+/// \param KeepEmptyLines true if empty lines should be included
+/// \returns a vector of lines, with the line endings removed
+std::vector<std::string> splitLines(const char *Str, unsigned Len,
+ bool KeepEmptyLines = false) {
+ std::vector<std::string> Lines;
+ for (unsigned LineStart = 0; LineStart < Len; ++LineStart) {
+ unsigned LineEnd = LineStart;
+ while (LineEnd < Len && Str[LineEnd] != '\n')
+ ++LineEnd;
+ if (LineStart != LineEnd || KeepEmptyLines)
+ Lines.push_back(std::string(&Str[LineStart], &Str[LineEnd]));
+ LineStart = LineEnd;
+ }
+ return Lines;
+}
+
+/// \brief Determine whether the string Haystack starts with the
+/// substring Needle.
+static bool startsWith(const std::string &Haystack, const char *Needle) {
+ for (unsigned I = 0, N = Haystack.size(); Needle[I] != 0; ++I) {
+ if (I == N)
+ return false;
+ if (Haystack[I] != Needle[I])
+ return false;
+ }
+
+ return true;
+}
+
+/// \brief Determine whether the string Haystack starts with the
+/// substring Needle.
+static inline bool startsWith(const std::string &Haystack,
+ const std::string &Needle) {
+ return startsWith(Haystack, Needle.c_str());
+}
+
/// \brief Check the contents of the predefines buffer against the
/// contents of the predefines buffer used to build the PCH file.
///
@@ -329,56 +371,154 @@ bool PCHReader::CheckPredefinesBuffer(const char *PCHPredef,
const char *Predef = PP.getPredefines().c_str();
unsigned PredefLen = PP.getPredefines().size();
- // If the two predefines buffers compare equal, we're done!.
+ // If the two predefines buffers compare equal, we're done!
if (PredefLen == PCHPredefLen &&
strncmp(Predef, PCHPredef, PCHPredefLen) == 0)
return false;
-
- // The predefines buffers are different. Produce a reasonable
- // diagnostic showing where they are different.
- // The source locations (potentially in the two different predefines
- // buffers)
- SourceLocation Loc1, Loc2;
SourceManager &SourceMgr = PP.getSourceManager();
+
+ // The predefines buffers are different. Determine what the
+ // differences are, and whether they require us to reject the PCH
+ // file.
+ std::vector<std::string> CmdLineLines = splitLines(Predef, PredefLen);
+ std::vector<std::string> PCHLines = splitLines(PCHPredef, PCHPredefLen);
+
+ // Sort both sets of predefined buffer lines, since
+ std::sort(CmdLineLines.begin(), CmdLineLines.end());
+ std::sort(PCHLines.begin(), PCHLines.end());
+
+ // Determine which predefines that where used to build the PCH file
+ // are missing from the command line.
+ std::vector<std::string> MissingPredefines;
+ std::set_difference(PCHLines.begin(), PCHLines.end(),
+ CmdLineLines.begin(), CmdLineLines.end(),
+ std::back_inserter(MissingPredefines));
+
+ bool MissingDefines = false;
+ bool ConflictingDefines = false;
+ for (unsigned I = 0, N = MissingPredefines.size(); I != N; ++I) {
+ const std::string &Missing = MissingPredefines[I];
+ if (!startsWith(Missing, "#define ") != 0) {
+ fprintf(stderr, "FIXME: command line is missing a non-macro entry in the predefines buffer that was used to build the PCH file\n%s\n",
+ Missing.c_str());
+ return true;
+ }
+
+ // This is a macro definition. Determine the name of the macro
+ // we're defining.
+ std::string::size_type StartOfMacroName = strlen("#define ");
+ std::string::size_type EndOfMacroName
+ = Missing.find_first_of("( \n\r", StartOfMacroName);
+ assert(EndOfMacroName != std::string::npos &&
+ "Couldn't find the end of the macro name");
+ std::string MacroName = Missing.substr(StartOfMacroName,
+ EndOfMacroName - StartOfMacroName);
+
+ // Determine whether this macro was given a different definition
+ // on the command line.
+ std::string MacroDefStart = "#define " + MacroName;
+ std::string::size_type MacroDefLen = MacroDefStart.size();
+ std::vector<std::string>::iterator ConflictPos
+ = std::lower_bound(CmdLineLines.begin(), CmdLineLines.end(),
+ MacroDefStart);
+ for (; ConflictPos != CmdLineLines.end(); ++ConflictPos) {
+ if (!startsWith(*ConflictPos, MacroDefStart)) {
+ // Different macro; we're done.
+ ConflictPos = CmdLineLines.end();
+ break;
+ }
+
+ assert(ConflictPos->size() > MacroDefLen &&
+ "Invalid #define in predefines buffer?");
+ if ((*ConflictPos)[MacroDefLen] != ' ' &&
+ (*ConflictPos)[MacroDefLen] != '(')
+ continue; // Longer macro name; keep trying.
+
+ // We found a conflicting macro definition.
+ break;
+ }
+
+ if (ConflictPos != CmdLineLines.end()) {
+ Diag(diag::warn_cmdline_conflicting_macro_def)
+ << MacroName;
+
+ // Show the definition of this macro within the PCH file.
+ const char *MissingDef = strstr(PCHPredef, Missing.c_str());
+ unsigned Offset = MissingDef - PCHPredef;
+ SourceLocation PCHMissingLoc
+ = SourceMgr.getLocForStartOfFile(PCHBufferID)
+ .getFileLocWithOffset(Offset);
+ Diag(PCHMissingLoc, diag::note_pch_macro_defined_as)
+ << MacroName;
+
+ ConflictingDefines = true;
+ continue;
+ }
+
+ // If the macro doesn't conflict, then we'll just pick up the
+ // macro definition from the PCH file. Warn the user that they
+ // made a mistake.
+ if (ConflictingDefines)
+ continue; // Don't complain if there are already conflicting defs
+
+ if (!MissingDefines) {
+ Diag(diag::warn_cmdline_missing_macro_defs);
+ MissingDefines = true;
+ }
- // Create a source buffer for our predefines string, so
- // that we can build a diagnostic that points into that
- // source buffer.
- FileID BufferID;
- if (Predef && Predef[0]) {
- llvm::MemoryBuffer *Buffer
- = llvm::MemoryBuffer::getMemBuffer(Predef, Predef + PredefLen,
- "<built-in>");
- BufferID = SourceMgr.createFileIDForMemBuffer(Buffer);
- }
-
- unsigned MinLen = std::min(PredefLen, PCHPredefLen);
- std::pair<const char *, const char *> Locations
- = std::mismatch(Predef, Predef + MinLen, PCHPredef);
-
- if (Locations.first != Predef + MinLen) {
- // We found the location in the two buffers where there is a
- // difference. Form source locations to point there (in both
- // buffers).
- unsigned Offset = Locations.first - Predef;
- Loc1 = SourceMgr.getLocForStartOfFile(BufferID)
- .getFileLocWithOffset(Offset);
- Loc2 = SourceMgr.getLocForStartOfFile(PCHBufferID)
- .getFileLocWithOffset(Offset);
- } else if (PredefLen > PCHPredefLen) {
- Loc1 = SourceMgr.getLocForStartOfFile(BufferID)
- .getFileLocWithOffset(MinLen);
- } else {
- Loc1 = SourceMgr.getLocForStartOfFile(PCHBufferID)
- .getFileLocWithOffset(MinLen);
+ // Show the definition of this macro within the PCH file.
+ const char *MissingDef = strstr(PCHPredef, Missing.c_str());
+ unsigned Offset = MissingDef - PCHPredef;
+ SourceLocation PCHMissingLoc
+ = SourceMgr.getLocForStartOfFile(PCHBufferID)
+ .getFileLocWithOffset(Offset);
+ Diag(PCHMissingLoc, diag::note_using_macro_def_from_pch);
}
- Diag(Loc1, diag::warn_pch_preprocessor);
- if (Loc2.isValid())
- Diag(Loc2, diag::note_predef_in_pch);
- Diag(diag::note_ignoring_pch) << FileName;
- return true;
+ if (ConflictingDefines) {
+ Diag(diag::note_ignoring_pch) << FileName;
+ return true;
+ }
+
+ // Determine what predefines were introduced based on command-line
+ // parameters that were not present when building the PCH
+ // file. Extra #defines are okay, so long as the identifiers being
+ // defined were not used within the precompiled header.
+ std::vector<std::string> ExtraPredefines;
+ std::set_difference(CmdLineLines.begin(), CmdLineLines.end(),
+ PCHLines.begin(), PCHLines.end(),
+ std::back_inserter(ExtraPredefines));
+ for (unsigned I = 0, N = ExtraPredefines.size(); I != N; ++I) {
+ const std::string &Extra = ExtraPredefines[I];
+ if (!startsWith(Extra, "#define ") != 0) {
+ fprintf(stderr, "FIXME: command line has extra predefines not used to build the PCH file.%s\n",
+ Extra.c_str());
+ return true;
+ }
+
+ // This is an extra macro definition. Determine the name of the
+ // macro we're defining.
+ std::string::size_type StartOfMacroName = strlen("#define ");
+ std::string::size_type EndOfMacroName
+ = Extra.find_first_of("( \n\r", StartOfMacroName);
+ assert(EndOfMacroName != std::string::npos &&
+ "Couldn't find the end of the macro name");
+ std::string MacroName = Extra.substr(StartOfMacroName,
+ EndOfMacroName - StartOfMacroName);
+
+ // FIXME: Perform this check!
+ fprintf(stderr, "FIXME: check whether '%s' was used in the PCH file\n",
+ MacroName.c_str());
+
+ // Add this definition to the suggested predefines buffer.
+ SuggestedPredefines += Extra;
+ SuggestedPredefines += '\n';
+ }
+
+ // If we get here, it's because the predefines buffer had compatible
+ // contents. Accept the PCH file.
+ return false;
}
//===----------------------------------------------------------------------===//
diff --git a/test/PCH/fuzzy-pch.c b/test/PCH/fuzzy-pch.c
new file mode 100644
index 0000000000..ce4634d756
--- /dev/null
+++ b/test/PCH/fuzzy-pch.c
@@ -0,0 +1,17 @@
+// Test with pch.
+// RUN: clang-cc -emit-pch -DFOO -o %t %S/variables.h &&
+// RUN: clang-cc -DBAR=int -include-pch %t -fsyntax-only -pedantic %s
+
+BAR bar = 17;
+
+#ifndef FOO
+# error FOO was not defined
+#endif
+
+#if FOO != 1
+# error FOO has the wrong definition
+#endif
+
+#ifndef BAR
+# error BAR was not defined
+#endif
diff --git a/test/PCH/fuzzy-pch.h b/test/PCH/fuzzy-pch.h
new file mode 100644
index 0000000000..9eb1005ce4
--- /dev/null
+++ b/test/PCH/fuzzy-pch.h
@@ -0,0 +1,2 @@
+// Header for PCH test fuzzy-pch.c
+void f(int X);
diff --git a/tools/clang-cc/clang-cc.cpp b/tools/clang-cc/clang-cc.cpp
index 92452ec5be..ad00664af5 100644
--- a/tools/clang-cc/clang-cc.cpp
+++ b/tools/clang-cc/clang-cc.cpp
@@ -1755,6 +1755,10 @@ static void ProcessInputFile(Preprocessor &PP, PreprocessorFactory &PPF,
// the precompiled header into the AST context.
switch (Reader->ReadPCH(ImplicitIncludePCH)) {
case PCHReader::Success: {
+ // Set the predefines buffer as suggested by the PCH
+ // reader. Typically, the predefines buffer will be empty.
+ PP.setPredefines(Reader->getSuggestedPredefines());
+
// Attach the PCH reader to the AST context as an external AST
// source, so that declarations will be deserialized from the
// PCH file as needed.
@@ -1762,10 +1766,6 @@ static void ProcessInputFile(Preprocessor &PP, PreprocessorFactory &PPF,
Source.reset(Reader.take());
ContextOwner->setExternalSource(Source);
}
-
- // Clear out the predefines buffer, because all of the
- // predefines are already in the PCH file.
- PP.setPredefines("");
break;
}