Skip to content

Implement multi-span error reporting for structs/records#28893

Closed
tetektoza wants to merge 6 commits into
ProvableHQ:mainnetfrom
tetektoza:feature/multi_span_error_reporting
Closed

Implement multi-span error reporting for structs/records#28893
tetektoza wants to merge 6 commits into
ProvableHQ:mainnetfrom
tetektoza:feature/multi_span_error_reporting

Conversation

@tetektoza
Copy link
Copy Markdown
Contributor

@tetektoza tetektoza commented Oct 3, 2025

As the title says, this adds support for displaying multiple related source locations in error messages, starting with duplicate struct/record member detection.

Before we have only showed the duplicate location, now after this patch we are showing both duplicate and "first declared here" locations.

For a program:

program test_duplicate_demo.aleo {
    struct Person {
        name: field,
        age: u8,
        name: field,
        city: field,
        age: u8,
    }

    record Asset {
        owner: address,
        value: u64,
        owner: address,
        owner: address,
    }

    transition main(public input: u32) -> u32 {
        return input;
    }
}

Before:

Error [ETYC0372015]: Struct Person defined with more than one member with the same name.
    --> \\?\C:\Users\tetektoza\source\repos\leo\test_duplicate_demo\src\main.leo:5:9
     |
   5 |         name: field,
     |         ^^^^^^^^^^^

Error [ETYC0372015]: Struct Person defined with more than one member with the same name.
    --> \\?\C:\Users\tetektoza\source\repos\leo\test_duplicate_demo\src\main.leo:7:9
     |
   7 |         age: u8,
     |         ^^^^^^^

Error [ETYC0372016]: Record Asset defined with more than one variable with the same name.
    --> \\?\C:\Users\tetektoza\source\repos\leo\test_duplicate_demo\src\main.leo:13:9
     |
  13 |         owner: address,
     |         ^^^^^^^^^^^^^^

Error [ETYC0372016]: Record Asset defined with more than one variable with the same name.
    --> \\?\C:\Users\tetektoza\source\repos\leo\test_duplicate_demo\src\main.leo:14:9
     |
  14 |         owner: address,
     |         ^^^^^^^^^^^^^^

After:

Error [ETYC0372017]: the name `name` is defined multiple times in struct `Person`
    --> \\?\C:\Users\tetektoza\source\repos\leo\test_duplicate_demo\src\main.leo:5:9
     |
   3 |         name: field,
     |         ^^^^^^^^^^^ previous definition of the member `name` here
   ...
   5 |         name: field,
     |         ^^^^^^^^^^^ `name` redefined here
     |
     = struct members must have unique names

Error [ETYC0372017]: the name `age` is defined multiple times in struct `Person`
    --> \\?\C:\Users\tetektoza\source\repos\leo\test_duplicate_demo\src\main.leo:7:9
     |
   4 |         age: u8,
     |         ^^^^^^^ previous definition of the member `age` here
   ...
   7 |         age: u8,
     |         ^^^^^^^ `age` redefined here
     |
     = struct members must have unique names

Error [ETYC0372018]: the name `owner` is defined multiple times in record `Asset`
    --> \\?\C:\Users\tetektoza\source\repos\leo\test_duplicate_demo\src\main.leo:13:9
     |
  11 |         owner: address,
     |         ^^^^^^^^^^^^^^ previous definition of the variable `owner` here
     ...
  13 |         owner: address,
     |         ^^^^^^^^^^^^^^ `owner` redefined here
     |
     = record variables must have unique names

Error [ETYC0372018]: the name `owner` is defined multiple times in record `Asset`
    --> \\?\C:\Users\tetektoza\source\repos\leo\test_duplicate_demo\src\main.leo:14:9
     |
  11 |         owner: address,
     |         ^^^^^^^^^^^^^^ previous definition of the variable `owner` here
     ...
  14 |         owner: address,
     |         ^^^^^^^^^^^^^^ `owner` redefined here
     |
     = record variables must have unique names

@mohammadfawaz as promised :)

@mohammadfawaz
Copy link
Copy Markdown
Collaborator

That's awesome :D

I'll review shortly.

@mohammadfawaz
Copy link
Copy Markdown
Collaborator

What you have here is great!

Just a few cosmetic things. What if, for those errors that need multiple spans, we follow Rust's error formats:
image
So, a multi-span error would have the following:

  1. An error message with file and line number.
  2. A bunch of labels. Each label is a message + span.
    In the Rust example above, the error message is just "the name Foo is defined multiple times". The, we have two labels:
  3. "previous definition of the type Foo here" that points to the first span.
  4. "Foo redefined here" that points to the second span.

Just a thought!

Comment thread errors/src/common/formatted.rs Outdated
writeln!(
f,
"{INDENT } |\n\
{INDENT } = note: {label}\n\
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a big fan of note: here.. Just a generic label here is probably better.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed that. You can check new changes.

@tetektoza tetektoza force-pushed the feature/multi_span_error_reporting branch from 6954a06 to 41356f0 Compare October 5, 2025 17:20
As the title says, this adds support for displaying multiple related
source locations in error messages, starting with duplicate
struct/record member detection.

Before we have only showed the duplicate location, now after this patch
we are showing both duplicate and "first declared here" locations.
@tetektoza tetektoza force-pushed the feature/multi_span_error_reporting branch from 41356f0 to 8636134 Compare October 5, 2025 17:21
@tetektoza
Copy link
Copy Markdown
Contributor Author

@mohammadfawaz I really liked your proposal and went ahead and tried to reflect Rust as closely as possible.
Check updated PR message for a before and after, and also check expectations. I've also extended current testcase files to cover more duplicate scenarios :)

Key changes:

  • Error block is now unified (previously we had two separate --> blocks with = note: in between them, now it is one --> header and both spans with inline labels,
  • Now we have inline with carets - previous definition of the member name here
  • New gap indicator (...) if spans are across multiple lines and not next to each other
  • updated error message, new one is: the name name is defined multiple times in struct Person

Hopefully you like it. Let's make this attempt the best possible, so I'm really open for more suggestions if you see some additional improvements.

@mohammadfawaz
Copy link
Copy Markdown
Collaborator

This is really awesome. I'll review carefully tomorrow, but a tiny feedback is to color both the label messages and the "caret"s in red, as in

write!(f, "{}", "^".bold().red())?;

and

write!(f, " {}", label.bold().red())?;

Now, in a dream world, the colors would be optionally passed for each label... and the default would be red.

- Carets and labels now display in bold red by default
- added SpanColor enum for optional per-label color customization
@tetektoza
Copy link
Copy Markdown
Contributor Author

@mohammadfawaz Sure, implemented it as well and tried to respect NOCOLOR variable. Not sure we need the SpanColor implementation but maybe you will have better ideas.
Looks like this now:
image

Copy link
Copy Markdown
Collaborator

@mohammadfawaz mohammadfawaz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall this looks great! Just wondering about the color feature and whether it's enabled.

Comment thread errors/src/common/formatted.rs Outdated

impl SpanColor {
/// apply bold formatting with an optional color (defaults to red if None).
pub fn apply_bold(text: &str, color: Option<&SpanColor>) -> String {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we rename this to something like format_bold_colored

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread errors/src/common/formatted.rs Outdated

/// Creates a backtraced error from multiple spans with labels, colors, and a backtrace.
#[allow(clippy::too_many_arguments)]
pub fn new_from_spans_with_colors<S>(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this not used yet?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, I've started adding this, then figured out I can merge it with new_from_spans and then dropped the idea midway :D Had to be late night.

}

let mut last_line: Option<usize> = None;
let use_color = std::env::var("NOCOLOR").unwrap_or_default().trim().to_owned().is_empty();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the reasoning for gating the color functionality with NOCOLOR?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, so just a little bit above those lines there's this comment:

        // To avoid the color enabling characters for comparison with test expectations.
        if std::env::var("NOCOLOR").unwrap_or_default().trim().to_owned().is_empty() {

I figured there could be some problem in the past with color being enabled and test expectations not being matched correctly. Although blame shows latest modifications 6 months ago, so maybe it's redundant now? I can change it, let me know.

@mohammadfawaz
Copy link
Copy Markdown
Collaborator

Closing in favor of #28917

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.

2 participants