void setMapping(diag::kind Diag, DiagnosticMapping Info) {
DiagMap[Diag] = Info;
}
+ DiagnosticMapping lookupMapping(diag::kind Diag) const {
+ return DiagMap.lookup(Diag);
+ }
DiagnosticMapping &getOrAddMapping(diag::kind Diag);
unsigned IsPragma : 1;
unsigned HasNoWarningAsError : 1;
unsigned HasNoErrorAsFatal : 1;
+ unsigned WasUpgradedFromWarning : 1;
public:
static DiagnosticMapping Make(diag::Severity Severity, bool IsUser,
Result.IsPragma = IsPragma;
Result.HasNoWarningAsError = 0;
Result.HasNoErrorAsFatal = 0;
+ Result.WasUpgradedFromWarning = 0;
return Result;
}
bool isUser() const { return IsUser; }
bool isPragma() const { return IsPragma; }
+ bool isErrorOrFatal() const {
+ return getSeverity() == diag::Severity::Error ||
+ getSeverity() == diag::Severity::Fatal;
+ }
+
bool hasNoWarningAsError() const { return HasNoWarningAsError; }
void setNoWarningAsError(bool Value) { HasNoWarningAsError = Value; }
bool hasNoErrorAsFatal() const { return HasNoErrorAsFatal; }
void setNoErrorAsFatal(bool Value) { HasNoErrorAsFatal = Value; }
+
+ /// Whether this mapping attempted to map the diagnostic to a warning, but
+ /// was overruled because the diagnostic was already mapped to an error or
+ /// fatal error.
+ bool wasUpgradedFromWarning() const { return WasUpgradedFromWarning; }
+ void setUpgradedFromWarning(bool Value) { WasUpgradedFromWarning = Value; }
+
+ /// Serialize the bits that aren't based on context.
+ unsigned serializeBits() const {
+ return (WasUpgradedFromWarning << 3) | Severity;
+ }
+ static diag::Severity deserializeSeverity(unsigned Bits) {
+ return (diag::Severity)(Bits & 0x7);
+ }
+ static bool deserializeUpgradedFromWarning(unsigned Bits) {
+ return Bits >> 3;
+ }
};
/// \brief Used for handling and querying diagnostic IDs.
assert((L.isInvalid() || SourceMgr) && "No SourceMgr for valid location");
// Don't allow a mapping to a warning override an error/fatal mapping.
+ bool WasUpgradedFromWarning = false;
if (Map == diag::Severity::Warning) {
DiagnosticMapping &Info = GetCurDiagState()->getOrAddMapping(Diag);
if (Info.getSeverity() == diag::Severity::Error ||
- Info.getSeverity() == diag::Severity::Fatal)
+ Info.getSeverity() == diag::Severity::Fatal) {
Map = Info.getSeverity();
+ WasUpgradedFromWarning = true;
+ }
}
DiagnosticMapping Mapping = makeUserMapping(Map, L);
+ Mapping.setUpgradedFromWarning(WasUpgradedFromWarning);
// Common case; setting all the diagnostics of a group in one place.
if ((L.isInvalid() || L == DiagStatesByLoc.getCurDiagStateLoc()) &&
"Invalid data, not enough diag/map pairs");
while (Size--) {
unsigned DiagID = Record[Idx++];
- diag::Severity Map = (diag::Severity)Record[Idx++];
- DiagnosticMapping Mapping = Diag.makeUserMapping(Map, Loc);
- if (Mapping.isPragma() || IncludeNonPragmaStates)
- NewState->setMapping(DiagID, Mapping);
+ unsigned SeverityAndUpgradedFromWarning = Record[Idx++];
+ bool WasUpgradedFromWarning =
+ DiagnosticMapping::deserializeUpgradedFromWarning(
+ SeverityAndUpgradedFromWarning);
+ DiagnosticMapping NewMapping =
+ Diag.makeUserMapping(DiagnosticMapping::deserializeSeverity(
+ SeverityAndUpgradedFromWarning),
+ Loc);
+ if (!NewMapping.isPragma() && !IncludeNonPragmaStates)
+ continue;
+
+ DiagnosticMapping &Mapping = NewState->getOrAddMapping(DiagID);
+
+ // If this mapping was specified as a warning but the severity was
+ // upgraded due to diagnostic settings, simulate the current diagnostic
+ // settings (and use a warning).
+ if (WasUpgradedFromWarning && !Mapping.isErrorOrFatal()) {
+ Mapping = Diag.makeUserMapping(diag::Severity::Warning, Loc);
+ continue;
+ }
+
+ // Use the deserialized mapping verbatim.
+ Mapping = NewMapping;
+ Mapping.setUpgradedFromWarning(WasUpgradedFromWarning);
}
return NewState;
};
// Read the first state.
- auto *FirstState = ReadDiagState(
- F.isModule() ? DiagState() : *Diag.DiagStatesByLoc.CurDiagState,
- SourceLocation(), F.isModule());
+ DiagState *FirstState;
+ if (F.Kind == MK_ImplicitModule) {
+ // Implicitly-built modules are reused with different diagnostic
+ // settings. Use the initial diagnostic state from Diag to simulate this
+ // compilation's diagnostic settings.
+ FirstState = Diag.DiagStatesByLoc.FirstDiagState;
+ DiagStates.push_back(FirstState);
+
+ // Skip the initial diagnostic state from the serialized module.
+ assert(Record[0] == 0 &&
+ "Invalid data, unexpected backref in initial state");
+ Idx = 2 + Record[1] * 2;
+ assert(Idx < Record.size() &&
+ "Invalid data, not enough state change pairs in initial state");
+ } else {
+ FirstState = ReadDiagState(
+ F.isModule() ? DiagState() : *Diag.DiagStatesByLoc.CurDiagState,
+ SourceLocation(), F.isModule());
+ }
// Read the state transitions.
unsigned NumLocations = Record[Idx++];
for (const auto &I : *State) {
if (I.second.isPragma() || IncludeNonPragmaStates) {
Record.push_back(I.first);
- Record.push_back((unsigned)I.second.getSeverity());
+ Record.push_back(I.second.serializeBits());
}
}
// Update the placeholder.
Record[NumLocationsIdx] = NumLocations;
// Emit CurDiagStateLoc. Do it last in order to match source order.
+ //
+ // This also protects against a hypothetical corner case with simulating
+ // -Werror settings for implicit modules in the ASTReader, where reading
+ // CurDiagState out of context could change whether warning pragmas are
+ // treated as errors.
AddSourceLocation(Diag.DiagStatesByLoc.CurDiagStateLoc, Record);
AddDiagState(Diag.DiagStatesByLoc.CurDiagState, false);
--- /dev/null
+#ifdef USE_PRAGMA
+#pragma clang diagnostic push
+#pragma clang diagnostic warning "-Wshorten-64-to-32"
+#endif
+template <class T> int convert(T V) { return V; }
+#ifdef USE_PRAGMA
+#pragma clang diagnostic pop
+#endif
--- /dev/null
+module convert {
+ header "convert.h"
+}
--- /dev/null
+// Check that implicit modules builds give correct diagnostics, even when
+// reusing a module built with strong -Werror flags.
+//
+// Clear the caches.
+// RUN: rm -rf %t.cache %t-pragma.cache
+//
+// Build with -Werror, then with -W, and then with neither.
+// RUN: not %clang_cc1 -triple x86_64-apple-darwin16 -fsyntax-only -fmodules \
+// RUN: -Werror=shorten-64-to-32 \
+// RUN: -I%S/Inputs/implicit-built-Werror-using-W -fimplicit-module-maps \
+// RUN: -fmodules-cache-path=%t.cache -x c++ %s 2>&1 \
+// RUN: | FileCheck %s -check-prefix=CHECK-ERROR
+// RUN: %clang_cc1 -triple x86_64-apple-darwin16 -fsyntax-only -fmodules \
+// RUN: -Wshorten-64-to-32 \
+// RUN: -I%S/Inputs/implicit-built-Werror-using-W -fimplicit-module-maps \
+// RUN: -fdisable-module-hash \
+// RUN: -fmodules-cache-path=%t.cache -x c++ %s 2>&1 \
+// RUN: | FileCheck %s -check-prefix=CHECK-WARN
+// RUN: %clang_cc1 -triple x86_64-apple-darwin16 -fsyntax-only -fmodules \
+// RUN: -I%S/Inputs/implicit-built-Werror-using-W -fimplicit-module-maps \
+// RUN: -fmodules-cache-path=%t.cache -x c++ %s 2>&1 \
+// RUN: | FileCheck %s -allow-empty
+//
+// In the presence of a warning pragma, build with -Werror and then without.
+// RUN: not %clang_cc1 -triple x86_64-apple-darwin16 -fsyntax-only -fmodules \
+// RUN: -DUSE_PRAGMA -Werror=shorten-64-to-32 \
+// RUN: -I%S/Inputs/implicit-built-Werror-using-W -fimplicit-module-maps \
+// RUN: -fmodules-cache-path=%t-pragma.cache -x c++ %s 2>&1 \
+// RUN: | FileCheck %s -check-prefix=CHECK-ERROR
+// RUN: %clang_cc1 -triple x86_64-apple-darwin16 -fsyntax-only -fmodules \
+// RUN: -DUSE_PRAGMA \
+// RUN: -I%S/Inputs/implicit-built-Werror-using-W -fimplicit-module-maps \
+// RUN: -fmodules-cache-path=%t-pragma.cache -x c++ %s 2>&1 \
+// RUN: | FileCheck %s -check-prefix=CHECK-WARN
+#include <convert.h>
+
+long long foo() { return convert<long long>(0); }
+
+// CHECK-ERROR: error: implicit conversion
+// CHECK-WARN: warning: implicit conversion
+// CHECK-NOT: error: implicit conversion
+// CHECK-NOT: warning: implicit conversion