-
Notifications
You must be signed in to change notification settings - Fork 420
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enhancements to diagnostic formatters and testing utilities #2214
base: main
Are you sure you want to change the base?
Conversation
44d3b8b
to
79a5db9
Compare
1 │ func foo() -> Int { | ||
| ╰─ note: Second message // This note comes from from diagnostic, how to present it? | ||
2 │ if 1 != 0 { | ||
│ ├─ error: My message goes here! | ||
│ ╰─ note: First message | ||
3 │ return 0 | ||
4 │ } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How to render a scenario when a diagnostic has a note in a different line?
Should we add the information about parent diagnostic to each note?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this looks good if we use GroupedDiagnosticFormatter
. The idea would be that each error basically gets its own printout of the source (we wouldn’t be merging multiple diagnostics into a single source printout anymore) and thus it’s clear which diagnostic the note belongs to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this looks good to me, too. Right now, the Swift compiler is intentionally grouping things so that there is only a single top-level diagnostic, because that made sure all of the information about a single issue (with all of its notes) would occur in the one printout.
Notes in different files are still a little tricky---they look like top-level things right now, but @ahoppen has reasonably suggested that they should be in a "box" as if they were nested, because notes are never standalone---they're always associated with some other error/warning/remark.
let severity: DiagnosticSeverity | ||
let highlight: [Syntax] // TODO: How to create an abstract model for this? | ||
let noteDescriptors: [NoteDescriptor] | ||
let fixIts: [FixIt] // TODO: How to create an abstract model for this? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@DougGregor in #2166 mentioned:
Note: a similar issue exists with Fix-Its, where we'd need a way to render what they actually do.
Do we already have a way to render this somewhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should try to render Fix-Its into the diagnostic output. We tried with the diagnostic formatter that we've been using in the compiler for years now, and it's hard to provide that much information via a console interface without obscuring the rest of the error. Fix-Its are best handled by editors.
79a5db9
to
84ffa17
Compare
DiagnosticsFormatter
@ahoppen @DougGregor @bnbarham @kimdv |
Could you split this PR into multiple smaller PRs? 1800 is just quite hard to review. I think commits 1 and 2 would make a good PR by themselves. |
I've split commit 1 and 2 into separate PR: #2238 |
84ffa17
to
43e9fe4
Compare
6030474
to
02c9523
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice with all the comments explaining what the code is doing. I mostly have nitpicky comments.
/// - `full`: Display the entire source code along with the diagnostics. Useful for getting a full overview, but could result in a large output. | ||
enum ContextRange { | ||
case limited(Int) | ||
case full |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When do you think the full context range could be useful? I think recently, we have been aiming to only display a single error/warning + its inline buffer. If we display multiple errors inside the same context, it’s also not clear which notes belong to which errors.
CC @DougGregor
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My thinking behind offering the full context range was to provide end-developers with extensive customization options if they would intent to use this entity in their projects. For example, the pointfree project uses contextSize
set to max number of lines to achieve a full context view.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see. I still have concerns about how this interacts with the notes but maybe it’s good enough even if the connection between notes and errors/warnings is not obvious.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not opposed to having the full context range. From a user perspective, I do wonder if I want to have a different context range for the top-level diagnostic vs. diagnostics in nested buffers (e.g., for macro expansions), because the nested buffers generally have content that's been synthesized.
// upon committing to the new API of `DiagnosticsFormatter` and diagnostic decorators, | ||
// all members will be marked as deprecated. | ||
extension DiagnosticsFormatter { | ||
public var contextSize: Int { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should deprecate this and make contextRange
public instead.
public var contextSize: Int { | |
public var contextSize: Int { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don’t think you deprecated this one.
Also, I just remembered that we should add release notes for the deprecations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So if we decide to discard the idea with full
context we would need to deprecate this property. In this case, contextSize can still be an Int. So I guess we waiting for Doug's respond here: #2214 (comment)
diagnosticDecorator is ANSIDiagnosticDecorator | ||
} | ||
|
||
public init(contextSize: Int = 2, colorize: Bool = false) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let’s deprecate this and make the ContextRange
initializer public instead.
annotatedSource(inSyntaxTree: tree, withDiagnostics: diags) | ||
} | ||
|
||
public static func annotatedSource( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, let’s deprecate it in favor of contextRange
.
CC @DougGregor I’d like to hear your thoughts on the note printing as well. |
02c9523
to
ede2ce8
Compare
This commit refactors the existing code for `DiagnosticsFormatter` and introduces several new features, complete with documentation and unit tests. Key Enhancements: 1. Nested Diagnostic Support: Enhanced to include not only top-level diagnostics but also related notes, improving the debugging experience. 2. Custom Decorators: Incorporate the `DiagnosticDecorator` protocol, allowing for custom formatting and styling of diagnostic output. 3. Context Size Control: Added options to control the `ContextSize`, providing more flexibility in how much source code context is displayed around each diagnostic. Documentation: - Comprehensive documentation added, detailing the purpose, usage examples, and future developments for `DiagnosticsFormatter`. Testing: - Added robust unit tests to validate the new features and ensure reliability. This refactor and feature addition make `DiagnosticsFormatter` a more versatile and developer-friendly tool for debugging and understanding Swift code.
ede2ce8
to
fcbfb1b
Compare
Almost looks good to me. I’d like to
|
I chatted with @DougGregor about this the other day and he doesn’t have objections. If you could address the remaining open points from my previous comment, I would like to get this in. |
I'll try to carve out some time this weekend to tackle that. Thanks for your patience. I do have one question, though: Are we ready to finalize the design of /// Initializes a new `DiagnosticsFormatter` instance.
///
/// - Parameters:
/// - contextRange: Specifies the number of contextual lines around each diagnostic message. Default is `.limited(2)`.
/// - diagnosticDecorator: The decorator used for formatting diagnostic output. Default is `BasicDiagnosticDecorator`.
init(
contextRange: ContextRange = .limited(2),
diagnosticDecorator: DiagnosticDecorator = BasicDiagnosticDecorator()
) {
self.contextRange = contextRange
self.diagnosticDecorator = diagnosticDecorator
} |
Overview
This pull request introduces substantial enhancements to our diagnostic functionalities and testing utilities, focusing on greater customizability, readability, and effectiveness in diagnosing Swift code issues.
Details
Refactor
DiagnosticsFormatter
and Extend FunctionalityNested Diagnostic Support:
Custom Decorators via
DiagnosticDecorator
:DiagnosticDecorator
protocol.Context Size Control:
ContextSize
.Documentation:
Testing:
Future Work:
Fix #2166