[modules] Fix incorrect diagnostic mapping computation when a module changes

diagnostic settings using _Pragma within a macro.

The AST writer had previously been assuming that all diagnostic state
transitions would occur within a FileID corresponding to a file. When a
diagnostic state change occured within a macro, it was unable to form a
location for that state change and would instead corrupt the diagnostic state
of the "root" node (and thus that of the main compilation).

Also introduce a "#pragma clang __debug diag_mapping" debugging utility
that I added to track this issue down.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@324695 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Richard Smith 2018-02-09 01:15:13 +00:00
parent 37031df06d
commit 945b034082
8 changed files with 159 additions and 13 deletions

View File

@ -262,6 +262,10 @@ private:
CurDiagStateLoc = SourceLocation();
}
/// Produce a debugging dump of the diagnostic state.
LLVM_DUMP_METHOD void dump(SourceManager &SrcMgr,
StringRef DiagName = StringRef()) const;
/// Grab the most-recently-added state point.
DiagState *getCurDiagState() const { return CurDiagState; }
/// Get the location at which a diagnostic state was last added.
@ -409,6 +413,11 @@ public:
DiagnosticsEngine &operator=(const DiagnosticsEngine &) = delete;
~DiagnosticsEngine();
LLVM_DUMP_METHOD void dump() const { DiagStatesByLoc.dump(*SourceMgr); }
LLVM_DUMP_METHOD void dump(StringRef DiagName) const {
DiagStatesByLoc.dump(*SourceMgr, DiagName);
}
const IntrusiveRefCntPtr<DiagnosticIDs> &getDiagnosticIDs() const {
return Diags;
}

View File

@ -1137,6 +1137,18 @@ public:
/// be used by clients.
SourceLocation getImmediateSpellingLoc(SourceLocation Loc) const;
/// \brief Form a SourceLocation from a FileID and Offset pair.
SourceLocation getComposedLoc(FileID FID, unsigned Offset) const {
bool Invalid = false;
const SrcMgr::SLocEntry &Entry = getSLocEntry(FID, &Invalid);
if (Invalid)
return SourceLocation();
unsigned GlobalOffset = Entry.getOffset() + Offset;
return Entry.isFile() ? SourceLocation::getFileLoc(GlobalOffset)
: SourceLocation::getMacroLoc(GlobalOffset);
}
/// \brief Decompose the specified location into a raw FileID + Offset pair.
///
/// The first element is the FileID, the second is the offset from the

View File

@ -236,6 +236,96 @@ DiagnosticsEngine::DiagStateMap::getFile(SourceManager &SrcMgr,
return &F;
}
void DiagnosticsEngine::DiagStateMap::dump(SourceManager &SrcMgr,
StringRef DiagName) const {
llvm::errs() << "diagnostic state at ";
CurDiagStateLoc.dump(SrcMgr);
llvm::errs() << ": " << CurDiagState << "\n";
for (auto &F : Files) {
FileID ID = F.first;
File &File = F.second;
bool PrintedOuterHeading = false;
auto PrintOuterHeading = [&] {
if (PrintedOuterHeading) return;
PrintedOuterHeading = true;
llvm::errs() << "File " << &File << " <FileID " << ID.getHashValue()
<< ">: " << SrcMgr.getBuffer(ID)->getBufferIdentifier();
if (F.second.Parent) {
std::pair<FileID, unsigned> Decomp =
SrcMgr.getDecomposedIncludedLoc(ID);
assert(File.ParentOffset == Decomp.second);
llvm::errs() << " parent " << File.Parent << " <FileID "
<< Decomp.first.getHashValue() << "> ";
SrcMgr.getLocForStartOfFile(Decomp.first)
.getLocWithOffset(Decomp.second)
.dump(SrcMgr);
}
if (File.HasLocalTransitions)
llvm::errs() << " has_local_transitions";
llvm::errs() << "\n";
};
if (DiagName.empty())
PrintOuterHeading();
for (DiagStatePoint &Transition : File.StateTransitions) {
bool PrintedInnerHeading = false;
auto PrintInnerHeading = [&] {
if (PrintedInnerHeading) return;
PrintedInnerHeading = true;
PrintOuterHeading();
llvm::errs() << " ";
SrcMgr.getLocForStartOfFile(ID)
.getLocWithOffset(Transition.Offset)
.dump(SrcMgr);
llvm::errs() << ": state " << Transition.State << ":\n";
};
if (DiagName.empty())
PrintInnerHeading();
for (auto &Mapping : *Transition.State) {
StringRef Option =
DiagnosticIDs::getWarningOptionForDiag(Mapping.first);
if (!DiagName.empty() && DiagName != Option)
continue;
PrintInnerHeading();
llvm::errs() << " ";
if (Option.empty())
llvm::errs() << "<unknown " << Mapping.first << ">";
else
llvm::errs() << Option;
llvm::errs() << ": ";
switch (Mapping.second.getSeverity()) {
case diag::Severity::Ignored: llvm::errs() << "ignored"; break;
case diag::Severity::Remark: llvm::errs() << "remark"; break;
case diag::Severity::Warning: llvm::errs() << "warning"; break;
case diag::Severity::Error: llvm::errs() << "error"; break;
case diag::Severity::Fatal: llvm::errs() << "fatal"; break;
}
if (!Mapping.second.isUser())
llvm::errs() << " default";
if (Mapping.second.isPragma())
llvm::errs() << " pragma";
if (Mapping.second.hasNoWarningAsError())
llvm::errs() << " no-error";
if (Mapping.second.hasNoErrorAsFatal())
llvm::errs() << " no-fatal";
if (Mapping.second.wasUpgradedFromWarning())
llvm::errs() << " overruled";
llvm::errs() << "\n";
}
}
}
}
void DiagnosticsEngine::PushDiagStatePoint(DiagState *State,
SourceLocation Loc) {
assert(Loc.isValid() && "Adding invalid loc point");

View File

@ -1053,6 +1053,20 @@ struct PragmaDebugHandler : public PragmaHandler {
PP.Diag(Identifier, diag::warn_pragma_debug_missing_argument)
<< II->getName();
}
} else if (II->isStr("diag_mapping")) {
Token DiagName;
PP.LexUnexpandedToken(DiagName);
if (DiagName.is(tok::eod))
PP.getDiagnostics().dump();
else if (DiagName.is(tok::string_literal) && !DiagName.hasUDSuffix()) {
StringLiteralParser Literal(DiagName, PP);
if (Literal.hadError)
return;
PP.getDiagnostics().dump(Literal.GetString());
} else {
PP.Diag(DiagName, diag::warn_pragma_debug_missing_argument)
<< II->getName();
}
} else if (II->isStr("llvm_fatal_error")) {
llvm::report_fatal_error("#pragma clang __debug llvm_fatal_error");
} else if (II->isStr("llvm_unreachable")) {

View File

@ -5761,6 +5761,8 @@ void ASTReader::ReadPragmaDiagnosticMappings(DiagnosticsEngine &Diag) {
Initial.ExtBehavior = (diag::Severity)Flags;
FirstState = ReadDiagState(Initial, SourceLocation(), true);
assert(F.OriginalSourceFileID.isValid());
// Set up the root buffer of the module to start with the initial
// diagnostic state of the module itself, to cover files that contain no
// explicit transitions (for which we did not serialize anything).
@ -5781,6 +5783,7 @@ void ASTReader::ReadPragmaDiagnosticMappings(DiagnosticsEngine &Diag) {
"Invalid data, missing pragma diagnostic states");
SourceLocation Loc = ReadSourceLocation(F, Record[Idx++]);
auto IDAndOffset = SourceMgr.getDecomposedLoc(Loc);
assert(IDAndOffset.first.isValid() && "invalid FileID for transition");
assert(IDAndOffset.second == 0 && "not a start location for a FileID");
unsigned Transitions = Record[Idx++];

View File

@ -3092,8 +3092,11 @@ void ASTWriter::WritePragmaDiagnosticMappings(const DiagnosticsEngine &Diag,
!FileIDAndFile.second.HasLocalTransitions)
continue;
++NumLocations;
AddSourceLocation(Diag.SourceMgr->getLocForStartOfFile(FileIDAndFile.first),
Record);
SourceLocation Loc = Diag.SourceMgr->getComposedLoc(FileIDAndFile.first, 0);
assert(!Loc.isInvalid() && "start loc for valid FileID is invalid");
AddSourceLocation(Loc, Record);
Record.push_back(FileIDAndFile.second.StateTransitions.size());
for (auto &StatePoint : FileIDAndFile.second.StateTransitions) {
Record.push_back(StatePoint.Offset);

View File

@ -1 +1,4 @@
#define pragma _Pragma("clang diagnostic push") _Pragma("clang diagnostic error \"-Wpadded\"") _Pragma("clang diagnostic pop")
pragma
struct Padded { char x; int y; };

View File

@ -3,24 +3,24 @@
// For an implicit module, all that matters are the warning flags in the user.
// RUN: %clang_cc1 -triple %itanium_abi_triple -fmodules -fimplicit-module-maps -emit-module -fmodules-cache-path=%t -fmodule-name=diag_flags -x c++ %S/Inputs/module.map -fmodules-ts
// RUN: %clang_cc1 -triple %itanium_abi_triple -fmodules -fimplicit-module-maps -verify -fmodules-cache-path=%t -I %S/Inputs %s -fmodules-ts
// RUN: %clang_cc1 -triple %itanium_abi_triple -fmodules -fimplicit-module-maps -verify -fmodules-cache-path=%t -I %S/Inputs %s -fmodules-ts -DWARNING -Wpadded
// RUN: %clang_cc1 -triple %itanium_abi_triple -fmodules -fimplicit-module-maps -verify -fmodules-cache-path=%t -I %S/Inputs %s -fmodules-ts -DERROR -Wpadded -Werror
// RUN: %clang_cc1 -triple %itanium_abi_triple -fmodules -fimplicit-module-maps -verify -fmodules-cache-path=%t -I %S/Inputs %s -fmodules-ts -DERROR -Werror=padded
// RUN: %clang_cc1 -triple %itanium_abi_triple -fmodules -fimplicit-module-maps -verify -fmodules-cache-path=%t -I %S/Inputs %s -fmodules-ts -DWARNING -DLOCAL_WARNING -Wpadded
// RUN: %clang_cc1 -triple %itanium_abi_triple -fmodules -fimplicit-module-maps -verify -fmodules-cache-path=%t -I %S/Inputs %s -fmodules-ts -DERROR -DLOCAL_ERROR -Wpadded -Werror
// RUN: %clang_cc1 -triple %itanium_abi_triple -fmodules -fimplicit-module-maps -verify -fmodules-cache-path=%t -I %S/Inputs %s -fmodules-ts -DERROR -DLOCAL_ERROR -Werror=padded
//
// For an explicit module, all that matters are the warning flags in the module build.
// RUN: %clang_cc1 -triple %itanium_abi_triple -fmodules -fimplicit-module-maps -emit-module -fmodule-name=diag_flags -x c++ %S/Inputs/module.map -fmodules-ts -o %t/nodiag.pcm
// RUN: %clang_cc1 -triple %itanium_abi_triple -fmodules -fimplicit-module-maps -verify -fmodules-cache-path=%t -I %S/Inputs %s -fmodules-ts -fmodule-file=%t/nodiag.pcm -Wpadded
// RUN: %clang_cc1 -triple %itanium_abi_triple -fmodules -fimplicit-module-maps -verify -fmodules-cache-path=%t -I %S/Inputs %s -fmodules-ts -fmodule-file=%t/nodiag.pcm -Werror -Wpadded
// RUN: %clang_cc1 -triple %itanium_abi_triple -fmodules -fimplicit-module-maps -verify -fmodules-cache-path=%t -I %S/Inputs %s -fmodules-ts -fmodule-file=%t/nodiag.pcm -Wpadded -DLOCAL_WARNING
// RUN: %clang_cc1 -triple %itanium_abi_triple -fmodules -fimplicit-module-maps -verify -fmodules-cache-path=%t -I %S/Inputs %s -fmodules-ts -fmodule-file=%t/nodiag.pcm -Werror -Wpadded -DLOCAL_ERROR
//
// RUN: %clang_cc1 -triple %itanium_abi_triple -fmodules -fimplicit-module-maps -emit-module -fmodule-name=diag_flags -x c++ %S/Inputs/module.map -fmodules-ts -o %t/warning.pcm -Wpadded
// RUN: %clang_cc1 -triple %itanium_abi_triple -fmodules -fimplicit-module-maps -verify -fmodules-cache-path=%t -I %S/Inputs %s -fmodules-ts -DWARNING -fmodule-file=%t/warning.pcm
// RUN: %clang_cc1 -triple %itanium_abi_triple -fmodules -fimplicit-module-maps -verify -fmodules-cache-path=%t -I %S/Inputs %s -fmodules-ts -DWARNING -fmodule-file=%t/warning.pcm -Werror=padded
// RUN: %clang_cc1 -triple %itanium_abi_triple -fmodules -fimplicit-module-maps -verify -fmodules-cache-path=%t -I %S/Inputs %s -fmodules-ts -DWARNING -fmodule-file=%t/warning.pcm -Werror=padded -DLOCAL_ERROR
// RUN: %clang_cc1 -triple %itanium_abi_triple -fmodules -fimplicit-module-maps -verify -fmodules-cache-path=%t -I %S/Inputs %s -fmodules-ts -DWARNING -fmodule-file=%t/warning.pcm -Werror
//
// RUN: %clang_cc1 -triple %itanium_abi_triple -fmodules -fimplicit-module-maps -emit-module -fmodule-name=diag_flags -x c++ %S/Inputs/module.map -fmodules-ts -o %t/werror-no-error.pcm -Werror -Wpadded -Wno-error=padded
// RUN: %clang_cc1 -triple %itanium_abi_triple -fmodules -fimplicit-module-maps -verify -fmodules-cache-path=%t -I %S/Inputs %s -fmodules-ts -DWARNING -fmodule-file=%t/werror-no-error.pcm
// RUN: %clang_cc1 -triple %itanium_abi_triple -fmodules -fimplicit-module-maps -verify -fmodules-cache-path=%t -I %S/Inputs %s -fmodules-ts -DWARNING -fmodule-file=%t/werror-no-error.pcm -Wno-padded
// RUN: %clang_cc1 -triple %itanium_abi_triple -fmodules -fimplicit-module-maps -verify -fmodules-cache-path=%t -I %S/Inputs %s -fmodules-ts -DWARNING -fmodule-file=%t/werror-no-error.pcm -Werror=padded
// RUN: %clang_cc1 -triple %itanium_abi_triple -fmodules -fimplicit-module-maps -verify -fmodules-cache-path=%t -I %S/Inputs %s -fmodules-ts -DWARNING -fmodule-file=%t/werror-no-error.pcm -Werror=padded -DLOCAL_ERROR
//
// RUN: %clang_cc1 -triple %itanium_abi_triple -fmodules -fimplicit-module-maps -emit-module -fmodule-name=diag_flags -x c++ %S/Inputs/module.map -fmodules-ts -o %t/error.pcm -Werror=padded
// RUN: %clang_cc1 -triple %itanium_abi_triple -fmodules -fimplicit-module-maps -verify -fmodules-cache-path=%t -I %S/Inputs %s -fmodules-ts -DERROR -fmodule-file=%t/error.pcm -Wno-padded
@ -35,10 +35,22 @@ import diag_flags;
// Diagnostic flags from the module user make no difference to diagnostics
// emitted within the module when using an explicitly-loaded module.
#if ERROR
// expected-error@diag_flags.h:14 {{padding struct}}
// expected-error@diag_flags.h:4 {{padding struct}}
#elif WARNING
// expected-warning@diag_flags.h:14 {{padding struct}}
#else
// expected-no-diagnostics
// expected-warning@diag_flags.h:4 {{padding struct}}
#endif
int arr[sizeof(Padded)];
// Diagnostic flags from the module make no difference to diagnostics emitted
// in the module user.
#if LOCAL_ERROR
// expected-error@+4 {{padding struct}}
#elif LOCAL_WARNING
// expected-warning@+2 {{padding struct}}
#endif
struct Padded2 { char x; int y; };
int arr2[sizeof(Padded2)];
#if !ERROR && !WARNING && !LOCAL_ERROR && !LOCAL_WARNING
// expected-no-diagnostics
#endif