DiagnosticEngine: Print the ID of the wrapped, not wrapper, diagnostic

This commit is contained in:
Anthony Latsis
2025-03-11 00:45:34 +00:00
parent 012ac5da5d
commit 77e673a723
8 changed files with 215 additions and 81 deletions

View File

@@ -1530,16 +1530,17 @@ namespace swift {
public:
DiagnosticKind declaredDiagnosticKindFor(const DiagID id);
/// Get a localized format string for a given `DiagID`. If no localization
/// available returns the default string for that `DiagID`.
llvm::StringRef diagnosticStringFor(DiagID id);
/// Get a localized format string for the given `DiagID`. If no localization
/// is available, returns the default string.
llvm::StringRef getFormatStringForDiagnostic(DiagID id);
/// Get a localized format string with an optional diagnostic name appended
/// to it. The diagnostic name type is defined by
/// `PrintDiagnosticNamesMode`.
llvm::StringRef diagnosticStringWithNameFor(
DiagID id, DiagGroupID groupID,
PrintDiagnosticNamesMode printDiagnosticNamesMode);
/// Get a localized format string for the given diagnostic. If no
/// localization is available, returns the default string.
///
/// \param includeDiagnosticName Whether to at all consider embedding the
/// name of the diagnostic identifier or group, per the setting.
llvm::StringRef getFormatStringForDiagnostic(const Diagnostic &diagnostic,
bool includeDiagnosticName);
static llvm::StringRef diagnosticIDStringFor(const DiagID id);

View File

@@ -1429,17 +1429,11 @@ DiagnosticEngine::diagnosticInfoForDiagnostic(const Diagnostic &diagnostic,
}
}
llvm::StringRef format;
if (includeDiagnosticName) {
format =
diagnosticStringWithNameFor(diagnostic.getID(), groupID,
getPrintDiagnosticNamesMode());
} else {
format = diagnosticStringFor(diagnostic.getID());
}
auto formatString =
getFormatStringForDiagnostic(diagnostic, includeDiagnosticName);
return DiagnosticInfo(diagnostic.getID(), loc, toDiagnosticKind(behavior),
format, diagnostic.getArgs(), Category,
formatString, diagnostic.getArgs(), Category,
getDefaultDiagnosticLoc(),
/*child note info*/ {}, diagnostic.getRanges(), fixIts,
diagnostic.isChildNote());
@@ -1622,7 +1616,7 @@ DiagnosticKind DiagnosticEngine::declaredDiagnosticKindFor(const DiagID id) {
return storedDiagnosticInfos[(unsigned)id].kind;
}
llvm::StringRef DiagnosticEngine::diagnosticStringFor(DiagID id) {
llvm::StringRef DiagnosticEngine::getFormatStringForDiagnostic(DiagID id) {
llvm::StringRef message = diagnosticStrings[(unsigned)id];
if (auto localizationProducer = localization.get()) {
message = localizationProducer->getMessageOr(id, message);
@@ -1630,10 +1624,16 @@ llvm::StringRef DiagnosticEngine::diagnosticStringFor(DiagID id) {
return message;
}
llvm::StringRef DiagnosticEngine::diagnosticStringWithNameFor(
DiagID id, DiagGroupID groupID,
PrintDiagnosticNamesMode printDiagnosticNamesMode) {
auto message = diagnosticStringFor(id);
llvm::StringRef
DiagnosticEngine::getFormatStringForDiagnostic(const Diagnostic &diagnostic,
bool includeDiagnosticName) {
auto diagID = diagnostic.getID();
auto message = getFormatStringForDiagnostic(diagID);
if (!includeDiagnosticName) {
return message;
}
auto formatMessageWithName = [&](StringRef message, StringRef name) {
const int additionalCharsLength = 3; // ' ', '[', ']'
std::string messageWithName;
@@ -1648,16 +1648,28 @@ llvm::StringRef DiagnosticEngine::diagnosticStringWithNameFor(
switch (printDiagnosticNamesMode) {
case PrintDiagnosticNamesMode::None:
break;
case PrintDiagnosticNamesMode::Identifier:
message = formatMessageWithName(message, diagnosticIDStringFor(id));
case PrintDiagnosticNamesMode::Identifier: {
// If this diagnostic is a wrapper for another diagnostic, use the ID of
// the wrapped diagnostic.
for (const auto &arg : diagnostic.getArgs()) {
if (arg.getKind() == DiagnosticArgumentKind::Diagnostic) {
diagID = arg.getAsDiagnostic()->ID;
break;
}
}
message = formatMessageWithName(message, diagnosticIDStringFor(diagID));
break;
}
case PrintDiagnosticNamesMode::Group:
auto groupID = diagnostic.getGroupID();
if (groupID != DiagGroupID::no_group) {
message =
formatMessageWithName(message, getDiagGroupInfoByID(groupID).name);
}
break;
}
return message;
}

View File

@@ -74,7 +74,7 @@ bool CodeCompletionDiagnostics::getDiagnostics(
typename swift::detail::PassArgument<ArgTypes>::type... VArgs) {
DiagID id = ID.ID;
std::vector<DiagnosticArgument> DiagArgs{std::move(VArgs)...};
auto format = Engine.diagnosticStringFor(id);
auto format = Engine.getFormatStringForDiagnostic(id);
DiagnosticEngine::formatDiagnosticText(Out, format, DiagArgs);
severity = getSeverity(Engine.declaredDiagnosticKindFor(id));

View File

@@ -1086,7 +1086,7 @@ public:
// Emit a specific unavailable message when we know why a decl can't be
// exposed, or a generic message otherwise.
auto diagString =
M.getASTContext().Diags.diagnosticStringFor(diag.getID());
M.getASTContext().Diags.getFormatStringForDiagnostic(diag.getID());
DiagnosticEngine::formatDiagnosticText(os, diagString, diag.getArgs(),
DiagnosticFormatOptions());
os << "\");\n";

View File

@@ -8,6 +8,7 @@ add_swift_unittest(SwiftASTTests
DiagnosticBehaviorTests.cpp
DiagnosticConsumerTests.cpp
DiagnosticGroupsTests.cpp
DiagnosticInfoTests.cpp
SourceLocTests.cpp
TestContext.cpp
TypeMatchTests.cpp

View File

@@ -64,7 +64,7 @@ TEST(DiagnosticBehavior, WarnUntilSwiftLangMode) {
[](DiagnosticEngine &diags, const DiagnosticInfo &info) {
EXPECT_EQ(info.Kind, DiagnosticKind::Error);
EXPECT_EQ(info.FormatString,
diags.diagnosticStringFor(
diags.getFormatStringForDiagnostic(
diag::error_immediate_mode_missing_stdlib.ID));
},
/*expectedNumCallbackCalls=*/1);
@@ -77,12 +77,12 @@ TEST(DiagnosticBehavior, WarnUntilSwiftLangMode) {
},
[](DiagnosticEngine &diags, const DiagnosticInfo &info) {
EXPECT_EQ(info.Kind, DiagnosticKind::Warning);
EXPECT_EQ(info.FormatString,
diags.diagnosticStringFor(diag::error_in_swift_lang_mode.ID));
EXPECT_EQ(info.FormatString, diags.getFormatStringForDiagnostic(
diag::error_in_swift_lang_mode.ID));
auto wrappedDiagInfo = info.FormatArgs.front().getAsDiagnostic();
EXPECT_EQ(wrappedDiagInfo->FormatString,
diags.diagnosticStringFor(
diags.getFormatStringForDiagnostic(
diag::error_immediate_mode_missing_stdlib.ID));
},
/*expectedNumCallbackCalls=*/1);
@@ -96,12 +96,12 @@ TEST(DiagnosticBehavior, WarnUntilSwiftLangMode) {
[](DiagnosticEngine &diags, const DiagnosticInfo &info) {
EXPECT_EQ(info.Kind, DiagnosticKind::Warning);
EXPECT_EQ(info.FormatString,
diags.diagnosticStringFor(
diags.getFormatStringForDiagnostic(
diag::error_in_a_future_swift_lang_mode.ID));
auto wrappedDiagInfo = info.FormatArgs.front().getAsDiagnostic();
EXPECT_EQ(wrappedDiagInfo->FormatString,
diags.diagnosticStringFor(
diags.getFormatStringForDiagnostic(
diag::error_immediate_mode_missing_stdlib.ID));
},
/*expectedNumCallbackCalls=*/1);

View File

@@ -63,54 +63,6 @@ static void diagnosticGroupsTestCase(
EXPECT_EQ(count, expectedNumCallbackCalls);
}
TEST(DiagnosticGroups, PrintDiagnosticGroups) {
// Test that we append the correct group in the format string.
diagnosticGroupsTestCase(
[](DiagnosticEngine &diags) {
diags.setPrintDiagnosticNamesMode(PrintDiagnosticNamesMode::Group);
TestDiagnostic diagnostic(diag::error_immediate_mode_missing_stdlib.ID,
DiagGroupID::DeprecatedDeclaration);
diags.diagnose(SourceLoc(), diagnostic);
},
[](const DiagnosticInfo &info) {
EXPECT_TRUE(info.FormatString.ends_with(" [DeprecatedDeclaration]"));
},
/*expectedNumCallbackCalls=*/1);
diagnosticGroupsTestCase(
[](DiagnosticEngine &diags) {
diags.setPrintDiagnosticNamesMode(PrintDiagnosticNamesMode::Group);
TestDiagnostic diagnostic(diag::error_immediate_mode_missing_stdlib.ID,
DiagGroupID::no_group);
diags.diagnose(SourceLoc(), diagnostic);
},
[](const DiagnosticInfo &info) {
EXPECT_FALSE(info.FormatString.ends_with("]"));
},
/*expectedNumCallbackCalls=*/1);
}
TEST(DiagnosticGroups, DiagnosticsWrappersInheritGroups) {
// Test that we don't loose the group of a diagnostic when it gets wrapped in
// another one.
diagnosticGroupsTestCase(
[](DiagnosticEngine &diags) {
diags.setPrintDiagnosticNamesMode(PrintDiagnosticNamesMode::Group);
TestDiagnostic diagnostic(diag::error_immediate_mode_missing_stdlib.ID,
DiagGroupID::DeprecatedDeclaration);
diags.diagnose(SourceLoc(), diagnostic)
.limitBehaviorUntilSwiftVersion(DiagnosticBehavior::Warning, 99);
},
[](const DiagnosticInfo &info) {
EXPECT_EQ(info.ID, diag::error_in_a_future_swift_lang_mode.ID);
EXPECT_TRUE(info.FormatString.ends_with(" [DeprecatedDeclaration]"));
},
/*expectedNumCallbackCalls=*/1);
}
TEST(DiagnosticGroups, TargetAll) {
// Test that uncategorized diagnostics are escalated when escalating all
// warnings.

View File

@@ -0,0 +1,168 @@
//===--- DiagnosticInfoTests.cpp --------------------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/DiagnosticGroups.h"
#include "swift/AST/DiagnosticsFrontend.h"
#include "swift/Basic/SourceManager.h"
#include "gtest/gtest.h"
using namespace swift;
namespace {
class TestDiagnosticConsumer : public DiagnosticConsumer {
llvm::function_ref<void(const DiagnosticInfo &)> callback;
public:
TestDiagnosticConsumer(decltype(callback) callback) : callback(callback) {}
void handleDiagnostic(SourceManager &SM,
const DiagnosticInfo &Info) override {
this->callback(Info);
}
};
struct TestDiagnostic : public Diagnostic {
TestDiagnostic(DiagID ID, DiagGroupID GroupID) : Diagnostic(ID, GroupID) {}
};
static void
testCase(llvm::function_ref<void(DiagnosticEngine &)> diagnose,
llvm::function_ref<void(DiagnosticEngine &, const DiagnosticInfo &)>
callback,
unsigned expectedNumCallbackCalls) {
SourceManager sourceMgr;
DiagnosticEngine diags(sourceMgr);
unsigned count = 0;
const auto countingCallback = [&](const DiagnosticInfo &info) {
++count;
callback(diags, info);
};
TestDiagnosticConsumer consumer(countingCallback);
diags.addConsumer(consumer);
diagnose(diags);
diags.removeConsumer(consumer);
EXPECT_EQ(count, expectedNumCallbackCalls);
}
// MARK: PrintDiagnosticNamesMode
TEST(DiagnosticInfo, PrintDiagnosticNamesMode_None) {
// Test that we don't embed anything in the format string if told so.
testCase(
[](DiagnosticEngine &diags) {
diags.setPrintDiagnosticNamesMode(PrintDiagnosticNamesMode::None);
TestDiagnostic diagnostic(diag::error_immediate_mode_missing_stdlib.ID,
DiagGroupID::DeprecatedDeclaration);
diags.diagnose(SourceLoc(), diagnostic);
},
[](DiagnosticEngine &diags, const DiagnosticInfo &info) {
EXPECT_EQ(info.FormatString,
diags.getFormatStringForDiagnostic(
diag::error_immediate_mode_missing_stdlib.ID));
},
/*expectedNumCallbackCalls=*/1);
}
TEST(DiagnosticInfo, PrintDiagnosticNamesMode_Identifier) {
// Test that we embed correct identifier in the format string.
testCase(
[](DiagnosticEngine &diags) {
diags.setPrintDiagnosticNamesMode(PrintDiagnosticNamesMode::Identifier);
diags.diagnose(SourceLoc(), diag::error_immediate_mode_missing_stdlib);
},
[](DiagnosticEngine &diags, const DiagnosticInfo &info) {
EXPECT_TRUE(info.FormatString.ends_with(
" [error_immediate_mode_missing_stdlib]"));
},
/*expectedNumCallbackCalls=*/1);
}
TEST(DiagnosticInfo, PrintDiagnosticNamesMode_Identifier_WrappedDiag) {
// For a wrapper diagnostic, test that we embed the identifier of the wrapped
// diagnostic.
testCase(
[](DiagnosticEngine &diags) {
diags.setPrintDiagnosticNamesMode(PrintDiagnosticNamesMode::Identifier);
diags.diagnose(SourceLoc(), diag::error_immediate_mode_missing_stdlib)
.limitBehaviorUntilSwiftVersion(DiagnosticBehavior::Warning, 99);
},
[](DiagnosticEngine &diags, const DiagnosticInfo &info) {
EXPECT_EQ(info.ID, diag::error_in_a_future_swift_lang_mode.ID);
EXPECT_TRUE(info.FormatString.ends_with(
" [error_immediate_mode_missing_stdlib]"));
},
/*expectedNumCallbackCalls=*/1);
}
TEST(DiagnosticInfo, PrintDiagnosticNamesMode_Group_NoGroup) {
// Test that we don't embed anything in the format string if the diagnostic
// has no group.
testCase(
[](DiagnosticEngine &diags) {
diags.setPrintDiagnosticNamesMode(PrintDiagnosticNamesMode::Group);
TestDiagnostic diagnostic(diag::error_immediate_mode_missing_stdlib.ID,
DiagGroupID::no_group);
diags.diagnose(SourceLoc(), diagnostic);
},
[](DiagnosticEngine &diags, const DiagnosticInfo &info) {
EXPECT_EQ(info.FormatString,
diags.getFormatStringForDiagnostic(
diag::error_immediate_mode_missing_stdlib.ID));
},
/*expectedNumCallbackCalls=*/1);
}
TEST(DiagnosticInfo, PrintDiagnosticNamesMode_Group) {
// Test that we embed the correct group in the format string.
testCase(
[](DiagnosticEngine &diags) {
diags.setPrintDiagnosticNamesMode(PrintDiagnosticNamesMode::Group);
TestDiagnostic diagnostic(diag::error_immediate_mode_missing_stdlib.ID,
DiagGroupID::DeprecatedDeclaration);
diags.diagnose(SourceLoc(), diagnostic);
},
[](DiagnosticEngine &, const DiagnosticInfo &info) {
EXPECT_TRUE(info.FormatString.ends_with(" [DeprecatedDeclaration]"));
},
/*expectedNumCallbackCalls=*/1);
}
TEST(DiagnosticInfo, PrintDiagnosticNamesMode_Group_WrappedDiag) {
// For a wrapper diagnostic, test that we embed the group name of the wrapped
// diagnostic.
testCase(
[](DiagnosticEngine &diags) {
diags.setPrintDiagnosticNamesMode(PrintDiagnosticNamesMode::Group);
TestDiagnostic diagnostic(diag::error_immediate_mode_missing_stdlib.ID,
DiagGroupID::DeprecatedDeclaration);
diags.diagnose(SourceLoc(), diagnostic)
.limitBehaviorUntilSwiftVersion(DiagnosticBehavior::Warning, 99);
},
[](DiagnosticEngine &, const DiagnosticInfo &info) {
EXPECT_EQ(info.ID, diag::error_in_a_future_swift_lang_mode.ID);
EXPECT_TRUE(info.FormatString.ends_with(" [DeprecatedDeclaration]"));
},
/*expectedNumCallbackCalls=*/1);
}
} // end anonymous namespace