Skip to content
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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

Matejkob
Copy link
Contributor

@Matejkob Matejkob commented Sep 18, 2023

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 Functionality

  1. Nested Diagnostic Support:

    • The formatter is now equipped to handle not just top-level diagnostics but also their associated notes.
    • This results in a more holistic and informative debugging experience.
  2. Custom Decorators via DiagnosticDecorator:

    • Incorporated the newly introduced DiagnosticDecorator protocol.
    • Now, you can easily plug in custom decorators for specialized formatting and styling, offering a personalized touch to diagnostic output.
  3. Context Size Control:

    • Introduced configurable options to control the ContextSize.
    • Depending on your preference, you can now expand or limit the amount of source code context displayed around each diagnostic.

Documentation:

  • Comprehensive documentation has been updated to include:
    • Usage scenarios for the formatter.
    • Code snippets for quick integration.
    • Guidelines for future developments and feature extensions.

Testing:

  • Added a set of robust unit tests specifically designed for the new features.

Future Work:

  • Evaluate the possibility of adding more diagnostic decorators for additional formats (e.g. Fix-It).
  • Explore options for further extending testing capabilities.

Fix #2166

@Matejkob Matejkob force-pushed the annotat-source-lines-with-notes branch from 44d3b8b to 79a5db9 Compare September 18, 2023 20:50
Comment on lines 207 to 213
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 │ }
Copy link
Contributor Author

@Matejkob Matejkob Sep 18, 2023

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?

Copy link
Collaborator

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.

Copy link
Member

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?
Copy link
Contributor Author

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?

Copy link
Member

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.

@Matejkob Matejkob force-pushed the annotat-source-lines-with-notes branch from 79a5db9 to 84ffa17 Compare September 25, 2023 15:01
@Matejkob Matejkob changed the title [WIP] Improve DiagnosticsFormatter Enhancements to diagnostic formatters and testing utilities Sep 25, 2023
@Matejkob Matejkob marked this pull request as ready for review September 25, 2023 15:21
@Matejkob
Copy link
Contributor Author

@ahoppen @DougGregor @bnbarham @kimdv
I've introduced a substantial number of changes and am now opening this PR for an initial round of reviews. While the code may have minor issues, I'd appreciate any general feedback before diving into the finer details.

@ahoppen
Copy link
Collaborator

ahoppen commented Sep 26, 2023

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.

@Matejkob
Copy link
Contributor Author

Matejkob commented Sep 26, 2023

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

@Matejkob Matejkob marked this pull request as draft September 26, 2023 21:21
@Matejkob Matejkob force-pushed the annotat-source-lines-with-notes branch from 84ffa17 to 43e9fe4 Compare February 6, 2024 13:47
@Matejkob Matejkob force-pushed the annotat-source-lines-with-notes branch 2 times, most recently from 6030474 to 02c9523 Compare February 18, 2024 14:59
@Matejkob Matejkob marked this pull request as ready for review February 18, 2024 15:03
Copy link
Collaborator

@ahoppen ahoppen left a 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.

Sources/SwiftDiagnostics/DiagnosticsFormatter.swift Outdated Show resolved Hide resolved
Sources/SwiftDiagnostics/DiagnosticsFormatter.swift Outdated Show resolved Hide resolved
Sources/SwiftDiagnostics/DiagnosticsFormatter.swift Outdated Show resolved Hide resolved
/// - `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
Copy link
Collaborator

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

Copy link
Contributor Author

@Matejkob Matejkob Feb 19, 2024

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.

Copy link
Collaborator

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.

Copy link
Member

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.

Sources/SwiftDiagnostics/DiagnosticsFormatter.swift Outdated Show resolved Hide resolved
Sources/SwiftDiagnostics/DiagnosticsFormatter.swift Outdated Show resolved Hide resolved
// upon committing to the new API of `DiagnosticsFormatter` and diagnostic decorators,
// all members will be marked as deprecated.
extension DiagnosticsFormatter {
public var contextSize: Int {
Copy link
Collaborator

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.

Suggested change
public var contextSize: Int {
public var contextSize: Int {

Copy link
Collaborator

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.

Copy link
Contributor Author

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) {
Copy link
Collaborator

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(
Copy link
Collaborator

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.

@ahoppen
Copy link
Collaborator

ahoppen commented Feb 19, 2024

CC @DougGregor I’d like to hear your thoughts on the note printing as well.

@Matejkob Matejkob force-pushed the annotat-source-lines-with-notes branch from 02c9523 to ede2ce8 Compare February 21, 2024 13:17
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.
@Matejkob Matejkob force-pushed the annotat-source-lines-with-notes branch from ede2ce8 to fcbfb1b Compare February 21, 2024 13:27
@ahoppen
Copy link
Collaborator

ahoppen commented Feb 21, 2024

Almost looks good to me. I’d like to

  • Deprecate the contextSize methods mentioned in my last review
  • Add release notes for the deprecations
  • Get @DougGregor’s opinion on the change, in particular the way notes are printed and ContextRange.full

@ahoppen
Copy link
Collaborator

ahoppen commented Mar 15, 2024

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.

@Matejkob
Copy link
Contributor Author

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 DiagnosticDecorator and make the following initializer public?

  /// 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
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

GroupedDiagnostics does not handle notes on diagnostics
3 participants