Skip to content

Commit 3c2dc28

Browse files
committed
[lldb] Also apply Fix-Its in "note:" diagnostics that belong to an error diagnostic
Summary: LLDB currently applies Fix-Its if they are attached to a Clang diagnostic that has the severity "error". Fix-Its connected to warnings and other severities are supposed to be ignored as LLDB doesn't seem to trust Clang Fix-Its in these situations. However, LLDB also ignores all Fix-Its coming from "note:" diagnostics. These diagnostics are usually emitted alongside other diagnostics (both warnings and errors), either to keep a single diagnostic message shorter or because the Fix-It is in a different source line. As they are technically their own (non-error) diagnostics, we currently are ignoring all Fix-Its associated with them. For example, this is a possible Clang diagnostic with a Fix-It that is currently ignored: ``` error: <user expression 1>:2:10: too many arguments provided to function-like macro invocation ToStr(0, {,}) ^ <user expression 1>:1:9: macro 'ToStr' defined here #define ToStr(x) #x ^ <user expression 1>:2:1: cannot use initializer list at the beginning of a macro argument ToStr(0, {,}) ^ ~~~~ ``` We also don't store "note:" diagnostics at all, as LLDB's abstraction around the whole diagnostic concept doesn't have such a concept. The text of "note:" diagnostics is instead appended to the last non-note diagnostic (which is causing that there is no "note:" text in the diagnostic above, as all the "note:" diagnostics have been appended to the first "error: ..." text). This patch fixes the ignored Fix-Its in note-diagnostics by appending them to the last non-note diagnostic, similar to the way we handle the text in these diagnostics. Reviewers: JDevlieghere, jingham Reviewed By: JDevlieghere Subscribers: abidh Differential Revision: https://reviews.llvm.org/D77055
1 parent 4f644ff commit 3c2dc28

File tree

2 files changed

+46
-7
lines changed

2 files changed

+46
-7
lines changed

lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,14 @@ class ClangExpressionParser::LLDBPreprocessorCallbacks : public PPCallbacks {
147147
llvm::StringRef getErrorString() { return m_error_stream.GetString(); }
148148
};
149149

150+
static void AddAllFixIts(ClangDiagnostic *diag, const clang::Diagnostic &Info) {
151+
for (auto &fix_it : Info.getFixItHints()) {
152+
if (fix_it.isNull())
153+
continue;
154+
diag->AddFixitHint(fix_it);
155+
}
156+
}
157+
150158
class ClangDiagnosticManagerAdapter : public clang::DiagnosticConsumer {
151159
public:
152160
ClangDiagnosticManagerAdapter(DiagnosticOptions &opts) {
@@ -162,6 +170,17 @@ class ClangDiagnosticManagerAdapter : public clang::DiagnosticConsumer {
162170
m_manager = manager;
163171
}
164172

173+
/// Returns the last ClangDiagnostic message that the DiagnosticManager
174+
/// received or a nullptr if the DiagnosticMangager hasn't seen any
175+
/// Clang diagnostics yet.
176+
ClangDiagnostic *MaybeGetLastClangDiag() const {
177+
if (m_manager->Diagnostics().empty())
178+
return nullptr;
179+
lldb_private::Diagnostic *diag = m_manager->Diagnostics().back().get();
180+
ClangDiagnostic *clang_diag = dyn_cast<ClangDiagnostic>(diag);
181+
return clang_diag;
182+
}
183+
165184
void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
166185
const clang::Diagnostic &Info) override {
167186
if (!m_manager) {
@@ -204,6 +223,23 @@ class ClangDiagnosticManagerAdapter : public clang::DiagnosticConsumer {
204223
case DiagnosticsEngine::Level::Note:
205224
m_manager->AppendMessageToDiagnostic(m_output);
206225
make_new_diagnostic = false;
226+
227+
// 'note:' diagnostics for errors and warnings can also contain Fix-Its.
228+
// We add these Fix-Its to the last error diagnostic to make sure
229+
// that we later have all Fix-Its related to an 'error' diagnostic when
230+
// we apply them to the user expression.
231+
auto *clang_diag = MaybeGetLastClangDiag();
232+
// If we don't have a previous diagnostic there is nothing to do.
233+
// If the previous diagnostic already has its own Fix-Its, assume that
234+
// the 'note:' Fix-It is just an alternative way to solve the issue and
235+
// ignore these Fix-Its.
236+
if (!clang_diag || clang_diag->HasFixIts())
237+
break;
238+
// Ignore all Fix-Its that are not associated with an error.
239+
if (clang_diag->GetSeverity() != eDiagnosticSeverityError)
240+
break;
241+
AddAllFixIts(clang_diag, Info);
242+
break;
207243
}
208244
if (make_new_diagnostic) {
209245
// ClangDiagnostic messages are expected to have no whitespace/newlines
@@ -218,13 +254,8 @@ class ClangDiagnosticManagerAdapter : public clang::DiagnosticConsumer {
218254
// enough context in an expression for the warning to be useful.
219255
// FIXME: Should we try to filter out FixIts that apply to our generated
220256
// code, and not the user's expression?
221-
if (severity == eDiagnosticSeverityError) {
222-
for (const clang::FixItHint &fixit : Info.getFixItHints()) {
223-
if (fixit.isNull())
224-
continue;
225-
new_diagnostic->AddFixitHint(fixit);
226-
}
227-
}
257+
if (severity == eDiagnosticSeverityError)
258+
AddAllFixIts(new_diagnostic.get(), Info);
228259

229260
m_manager->AddDiagnostic(std::move(new_diagnostic));
230261
}

lldb/test/API/commands/expression/fixits/TestFixIts.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,14 @@ def test_with_target(self):
6161
self.assertTrue(value.GetError().Success())
6262
self.assertEquals(value.GetValueAsUnsigned(), 20)
6363

64+
# Try a Fix-It that is stored in the 'note:' diagnostic of an error.
65+
# The Fix-It here is adding parantheses around the ToStr parameters.
66+
fixit_in_note_expr ="#define ToStr(x) #x\nToStr(0 {, })"
67+
value = frame.EvaluateExpression(fixit_in_note_expr, options)
68+
self.assertTrue(value.IsValid())
69+
self.assertTrue(value.GetError().Success(), value.GetError())
70+
self.assertEquals(value.GetSummary(), '"(0 {, })"')
71+
6472
# Now turn off the fixits, and the expression should fail:
6573
options.SetAutoApplyFixIts(False)
6674
value = frame.EvaluateExpression(two_error_expression, options)

0 commit comments

Comments
 (0)