Skip to content

Commit ab9c378

Browse files
Merge pull request swiftlang#78517 from AnthonyLatsis/micronecta-scholtzi-6.1
[6.1] DiagnosticEngine: Fix escalation for wrapped diagnostics
2 parents f852d7a + 6517bce commit ab9c378

File tree

8 files changed

+288
-35
lines changed

8 files changed

+288
-35
lines changed

include/swift/AST/DiagnosticEngine.h

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ namespace swift {
6767
/// this enumeration type that uniquely identifies it.
6868
enum class DiagID : uint32_t;
6969

70+
enum class DiagGroupID : uint16_t;
71+
7072
/// Describes a diagnostic along with its argument types.
7173
///
7274
/// The diagnostics header introduces instances of this type for each
@@ -498,6 +500,7 @@ namespace swift {
498500

499501
private:
500502
DiagID ID;
503+
DiagGroupID GroupID;
501504
SmallVector<DiagnosticArgument, 3> Args;
502505
SmallVector<CharSourceRange, 2> Ranges;
503506
SmallVector<FixIt, 2> FixIts;
@@ -510,7 +513,16 @@ namespace swift {
510513
friend DiagnosticEngine;
511514
friend class InFlightDiagnostic;
512515

513-
Diagnostic(DiagID ID) : ID(ID) {}
516+
/// Constructs a Diagnostic with DiagGroupID infered from DiagID.
517+
Diagnostic(DiagID ID);
518+
519+
protected:
520+
/// Only use this constructor privately in this class or in unit tests by
521+
/// subclassing.
522+
/// In unit tests, it is used as a means for associating diagnostics with
523+
/// groups and, thus, circumventing the need to otherwise define mock
524+
/// diagnostics, which is not accounted for in the current design.
525+
Diagnostic(DiagID ID, DiagGroupID GroupID) : ID(ID), GroupID(GroupID) {}
514526

515527
public:
516528
// All constructors are intentionally implicit.
@@ -522,8 +534,9 @@ namespace swift {
522534
gatherArgs(VArgs...);
523535
}
524536

525-
/*implicit*/Diagnostic(DiagID ID, ArrayRef<DiagnosticArgument> Args)
526-
: ID(ID), Args(Args.begin(), Args.end()) {}
537+
Diagnostic(DiagID ID, ArrayRef<DiagnosticArgument> Args) : Diagnostic(ID) {
538+
this->Args.append(Args.begin(), Args.end());
539+
}
527540

528541
template <class... ArgTypes>
529542
static Diagnostic fromTuple(Diag<ArgTypes...> id,
@@ -535,6 +548,7 @@ namespace swift {
535548

536549
// Accessors.
537550
DiagID getID() const { return ID; }
551+
DiagGroupID getGroupID() const { return GroupID; }
538552
ArrayRef<DiagnosticArgument> getArgs() const { return Args; }
539553
ArrayRef<CharSourceRange> getRanges() const { return Ranges; }
540554
ArrayRef<FixIt> getFixIts() const { return FixIts; }
@@ -896,7 +910,9 @@ namespace swift {
896910
/// Don't emit any remarks
897911
bool suppressRemarks = false;
898912

899-
/// Treat these warnings as errors. Indices here correspond to DiagID enum
913+
/// A mapping from `DiagGroupID` identifiers to Boolean values indicating
914+
/// whether warnings belonging to the respective diagnostic groups should be
915+
/// escalated to errors.
900916
llvm::BitVector warningsAsErrors;
901917

902918
/// Whether a fatal error has occurred
@@ -938,16 +954,23 @@ namespace swift {
938954
void setSuppressRemarks(bool val) { suppressRemarks = val; }
939955
bool getSuppressRemarks() const { return suppressRemarks; }
940956

941-
/// Whether a warning should be upgraded to an error or not
942-
void setWarningAsErrorForDiagID(DiagID id, bool value) {
957+
/// Sets whether warnings belonging to the diagnostic group identified by
958+
/// `id` should be escalated to errors.
959+
void setWarningsAsErrorsForDiagGroupID(DiagGroupID id, bool value) {
943960
warningsAsErrors[(unsigned)id] = value;
944961
}
945-
bool getWarningAsErrorForDiagID(DiagID id) {
962+
963+
/// Returns a Boolean value indicating whether warnings belonging to the
964+
/// diagnostic group identified by `id` should be escalated to errors.
965+
bool getWarningsAsErrorsForDiagGroupID(DiagGroupID id) {
946966
return warningsAsErrors[(unsigned)id];
947967
}
948968

949-
/// Whether all warnings should be upgraded to errors or not
969+
/// Whether all warnings should be upgraded to errors or not.
950970
void setAllWarningsAsErrors(bool value) {
971+
// This works as intended because every diagnostic belongs to either a
972+
// custom group or the top-level `DiagGroupID::no_group`, which is also
973+
// a group.
951974
if (value) {
952975
warningsAsErrors.set();
953976
} else {
@@ -1434,7 +1457,8 @@ namespace swift {
14341457

14351458
/// Generate DiagnosticInfo for a Diagnostic to be passed to consumers.
14361459
std::optional<DiagnosticInfo>
1437-
diagnosticInfoForDiagnostic(const Diagnostic &diagnostic);
1460+
diagnosticInfoForDiagnostic(const Diagnostic &diagnostic,
1461+
bool includeDiagnosticName);
14381462

14391463
/// Send \c diag to all diagnostic consumers.
14401464
void emitDiagnostic(const Diagnostic &diag);
@@ -1460,9 +1484,16 @@ namespace swift {
14601484
public:
14611485
DiagnosticKind declaredDiagnosticKindFor(const DiagID id);
14621486

1463-
llvm::StringRef
1464-
diagnosticStringFor(const DiagID id,
1465-
PrintDiagnosticNamesMode printDiagnosticNamesMode);
1487+
/// Get a localized format string for a given `DiagID`. If no localization
1488+
/// available returns the default string for that `DiagID`.
1489+
llvm::StringRef diagnosticStringFor(DiagID id);
1490+
1491+
/// Get a localized format string with an optional diagnostic name appended
1492+
/// to it. The diagnostic name type is defined by
1493+
/// `PrintDiagnosticNamesMode`.
1494+
llvm::StringRef diagnosticStringWithNameFor(
1495+
DiagID id, DiagGroupID groupID,
1496+
PrintDiagnosticNamesMode printDiagnosticNamesMode);
14661497

14671498
static llvm::StringRef diagnosticIDStringFor(const DiagID id);
14681499

include/swift/AST/DiagnosticGroups.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ enum class DiagGroupID : uint16_t {
3333

3434
constexpr const auto DiagGroupsCount = [] {
3535
size_t count = 0;
36-
#define GROUP(Name, Version) count++;
36+
#define GROUP(Name, Version) ++count;
3737
#include "DiagnosticGroups.def"
3838
return count;
3939
}();

include/swift/AST/DiagnosticsCommon.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,14 @@ ERROR(error_no_group_info,none,
4747
NOTE(brace_stmt_suggest_do,none,
4848
"did you mean to use a 'do' statement?", ())
4949

50+
// `error_in_future_swift_version` does not have a group because warnings of this kind
51+
// inherit the group from the wrapped error.
5052
WARNING(error_in_future_swift_version,none,
5153
"%0; this is an error in the Swift %1 language mode",
5254
(DiagnosticInfo *, unsigned))
5355

56+
// `error_in_a_future_swift_version` does not have a group because warnings of this kind
57+
// inherit the group from the wrapped error.
5458
WARNING(error_in_a_future_swift_version,none,
5559
"%0; this will be an error in a future Swift language mode",
5660
(DiagnosticInfo *))

lib/AST/DiagnosticEngine.cpp

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -176,9 +176,12 @@ DiagnosticState::DiagnosticState() {
176176
// Initialize our ignored diagnostics to default
177177
ignoredDiagnostics.resize(LocalDiagID::NumDiags);
178178
// Initialize warningsAsErrors to default
179-
warningsAsErrors.resize(LocalDiagID::NumDiags);
179+
warningsAsErrors.resize(DiagGroupsCount);
180180
}
181181

182+
Diagnostic::Diagnostic(DiagID ID)
183+
: Diagnostic(ID, storedDiagnosticInfos[(unsigned)ID].groupID) {}
184+
182185
static CharSourceRange toCharSourceRange(SourceManager &SM, SourceRange SR) {
183186
return CharSourceRange(SM, SR.Start, Lexer::getLocForEndOfToken(SM, SR.End));
184187
}
@@ -464,8 +467,8 @@ InFlightDiagnostic::wrapIn(const Diagnostic &wrapper) {
464467
limit(Engine->getActiveDiagnostic().BehaviorLimit,
465468
DiagnosticBehavior::Unspecified);
466469

467-
Engine->WrappedDiagnostics.push_back(
468-
*Engine->diagnosticInfoForDiagnostic(Engine->getActiveDiagnostic()));
470+
Engine->WrappedDiagnostics.push_back(*Engine->diagnosticInfoForDiagnostic(
471+
Engine->getActiveDiagnostic(), /* includeDiagnosticName= */ false));
469472

470473
Engine->state.swap(tempState);
471474

@@ -478,6 +481,7 @@ InFlightDiagnostic::wrapIn(const Diagnostic &wrapper) {
478481
// Overwrite the ID and argument with those from the wrapper.
479482
Engine->getActiveDiagnostic().ID = wrapper.ID;
480483
Engine->getActiveDiagnostic().Args = wrapper.Args;
484+
// Intentionally keeping the original GroupID here
481485

482486
// Set the argument to the diagnostic being wrapped.
483487
assert(wrapper.getArgs().front().getKind() == DiagnosticArgumentKind::Diagnostic);
@@ -547,9 +551,7 @@ void DiagnosticEngine::setWarningsAsErrorsRules(
547551
if (auto groupID = getDiagGroupIDByName(name);
548552
groupID && *groupID != DiagGroupID::no_group) {
549553
getDiagGroupInfoByID(*groupID).traverseDepthFirst([&](auto group) {
550-
for (DiagID diagID : group.diagnostics) {
551-
state.setWarningAsErrorForDiagID(diagID, isEnabled);
552-
}
554+
state.setWarningsAsErrorsForDiagGroupID(*groupID, isEnabled);
553555
});
554556
} else {
555557
unknownGroups.push_back(std::string(name));
@@ -1228,7 +1230,7 @@ DiagnosticBehavior DiagnosticState::determineBehavior(const Diagnostic &diag) {
12281230
// 4) If the user substituted a different behavior for this behavior, apply
12291231
// that change
12301232
if (lvl == DiagnosticBehavior::Warning) {
1231-
if (getWarningAsErrorForDiagID(diag.getID()))
1233+
if (getWarningsAsErrorsForDiagGroupID(diag.getGroupID()))
12321234
lvl = DiagnosticBehavior::Error;
12331235
if (suppressWarnings)
12341236
lvl = DiagnosticBehavior::Ignore;
@@ -1294,7 +1296,8 @@ void DiagnosticEngine::forwardTentativeDiagnosticsTo(
12941296
}
12951297

12961298
std::optional<DiagnosticInfo>
1297-
DiagnosticEngine::diagnosticInfoForDiagnostic(const Diagnostic &diagnostic) {
1299+
DiagnosticEngine::diagnosticInfoForDiagnostic(const Diagnostic &diagnostic,
1300+
bool includeDiagnosticName) {
12981301
auto behavior = state.determineBehavior(diagnostic);
12991302
if (behavior == DiagnosticBehavior::Ignore)
13001303
return std::nullopt;
@@ -1347,12 +1350,19 @@ DiagnosticEngine::diagnosticInfoForDiagnostic(const Diagnostic &diagnostic) {
13471350
}
13481351
}
13491352

1350-
return DiagnosticInfo(
1351-
diagnostic.getID(), loc, toDiagnosticKind(behavior),
1352-
diagnosticStringFor(diagnostic.getID(), getPrintDiagnosticNamesMode()),
1353-
diagnostic.getArgs(), Category, getDefaultDiagnosticLoc(),
1354-
/*child note info*/ {}, diagnostic.getRanges(), fixIts,
1355-
diagnostic.isChildNote());
1353+
llvm::StringRef format;
1354+
if (includeDiagnosticName)
1355+
format =
1356+
diagnosticStringWithNameFor(diagnostic.getID(), diagnostic.getGroupID(),
1357+
getPrintDiagnosticNamesMode());
1358+
else
1359+
format = diagnosticStringFor(diagnostic.getID());
1360+
1361+
return DiagnosticInfo(diagnostic.getID(), loc, toDiagnosticKind(behavior),
1362+
format, diagnostic.getArgs(), Category,
1363+
getDefaultDiagnosticLoc(),
1364+
/*child note info*/ {}, diagnostic.getRanges(), fixIts,
1365+
diagnostic.isChildNote());
13561366
}
13571367

13581368
static DeclName
@@ -1462,7 +1472,9 @@ void DiagnosticEngine::emitDiagnostic(const Diagnostic &diagnostic) {
14621472
ArrayRef<Diagnostic> childNotes = diagnostic.getChildNotes();
14631473
std::vector<Diagnostic> extendedChildNotes;
14641474

1465-
if (auto info = diagnosticInfoForDiagnostic(diagnostic)) {
1475+
if (auto info =
1476+
diagnosticInfoForDiagnostic(diagnostic,
1477+
/* includeDiagnosticName= */ true)) {
14661478
// If the diagnostic location is within a buffer containing generated
14671479
// source code, add child notes showing where the generation occurred.
14681480
// We need to avoid doing this if this is itself a child note, as otherwise
@@ -1478,7 +1490,9 @@ void DiagnosticEngine::emitDiagnostic(const Diagnostic &diagnostic) {
14781490

14791491
SmallVector<DiagnosticInfo, 1> childInfo;
14801492
for (unsigned i : indices(childNotes)) {
1481-
auto child = diagnosticInfoForDiagnostic(childNotes[i]);
1493+
auto child =
1494+
diagnosticInfoForDiagnostic(childNotes[i],
1495+
/* includeDiagnosticName= */ true);
14821496
assert(child);
14831497
assert(child->Kind == DiagnosticKind::Note &&
14841498
"Expected child diagnostics to all be notes?!");
@@ -1516,12 +1530,18 @@ DiagnosticKind DiagnosticEngine::declaredDiagnosticKindFor(const DiagID id) {
15161530
return storedDiagnosticInfos[(unsigned)id].kind;
15171531
}
15181532

1519-
llvm::StringRef DiagnosticEngine::diagnosticStringFor(
1520-
const DiagID id, PrintDiagnosticNamesMode printDiagnosticNamesMode) {
1533+
llvm::StringRef DiagnosticEngine::diagnosticStringFor(DiagID id) {
15211534
llvm::StringRef message = diagnosticStrings[(unsigned)id];
15221535
if (auto localizationProducer = localization.get()) {
15231536
message = localizationProducer->getMessageOr(id, message);
15241537
}
1538+
return message;
1539+
}
1540+
1541+
llvm::StringRef DiagnosticEngine::diagnosticStringWithNameFor(
1542+
DiagID id, DiagGroupID groupID,
1543+
PrintDiagnosticNamesMode printDiagnosticNamesMode) {
1544+
auto message = diagnosticStringFor(id);
15251545
auto formatMessageWithName = [&](StringRef message, StringRef name) {
15261546
const int additionalCharsLength = 3; // ' ', '[', ']'
15271547
std::string messageWithName;
@@ -1540,7 +1560,6 @@ llvm::StringRef DiagnosticEngine::diagnosticStringFor(
15401560
message = formatMessageWithName(message, diagnosticIDStringFor(id));
15411561
break;
15421562
case PrintDiagnosticNamesMode::Group:
1543-
auto groupID = storedDiagnosticInfos[(unsigned)id].groupID;
15441563
if (groupID != DiagGroupID::no_group) {
15451564
message =
15461565
formatMessageWithName(message, getDiagGroupInfoByID(groupID).name);

lib/IDE/CodeCompletionDiagnostics.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ bool CodeCompletionDiagnostics::getDiagnostics(
6767
typename swift::detail::PassArgument<ArgTypes>::type... VArgs) {
6868
DiagID id = ID.ID;
6969
std::vector<DiagnosticArgument> DiagArgs{std::move(VArgs)...};
70-
auto format = Engine.diagnosticStringFor(id, PrintDiagnosticNamesMode::None);
70+
auto format = Engine.diagnosticStringFor(id);
7171
DiagnosticEngine::formatDiagnosticText(Out, format, DiagArgs);
7272
severity = getSeverity(Engine.declaredDiagnosticKindFor(id));
7373

lib/PrintAsClang/ModuleContentsWriter.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -977,8 +977,8 @@ class ModuleWriter {
977977
const_cast<ValueDecl *>(vd));
978978
// Emit a specific unavailable message when we know why a decl can't be
979979
// exposed, or a generic message otherwise.
980-
auto diagString = M.getASTContext().Diags.diagnosticStringFor(
981-
diag.getID(), PrintDiagnosticNamesMode::None);
980+
auto diagString =
981+
M.getASTContext().Diags.diagnosticStringFor(diag.getID());
982982
DiagnosticEngine::formatDiagnosticText(os, diagString, diag.getArgs(),
983983
DiagnosticFormatOptions());
984984
os << "\");\n";

unittests/AST/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ add_swift_unittest(SwiftASTTests
44
ASTWalkerTests.cpp
55
IndexSubsetTests.cpp
66
DiagnosticConsumerTests.cpp
7+
DiagnosticGroupsTests.cpp
78
SourceLocTests.cpp
89
TestContext.cpp
910
TypeMatchTests.cpp

0 commit comments

Comments
 (0)