diff --git a/.changeset/empty-lies-fetch.md b/.changeset/empty-lies-fetch.md new file mode 100644 index 0000000000..d4a6ac3bf8 --- /dev/null +++ b/.changeset/empty-lies-fetch.md @@ -0,0 +1,5 @@ +--- +"@nomicfoundation/slang": patch +--- + +fix source locations for unicode characters in error reports diff --git a/crates/codegen/parser/runtime/src/parse_error.rs b/crates/codegen/parser/runtime/src/parse_error.rs index b733316b50..458c6be8c5 100644 --- a/crates/codegen/parser/runtime/src/parse_error.rs +++ b/crates/codegen/parser/runtime/src/parse_error.rs @@ -1,7 +1,7 @@ use std::collections::BTreeSet; use crate::kinds::TokenKind; -use crate::text_index::{TextIndex, TextRange}; +use crate::text_index::{TextIndex, TextRange, TextRangeExtensions}; #[derive(Debug, PartialEq, Eq, Clone)] pub struct ParseError { @@ -64,8 +64,6 @@ pub(crate) fn render_error_report( let kind = ReportKind::Error; let color = if with_color { Color::Red } else { Color::Unset }; - let source_start = error.text_range.start.utf8; - let source_end = error.text_range.end.utf8; let tokens_that_would_have_allowed_more_progress = error.tokens_that_would_have_allowed_more_progress(); @@ -82,12 +80,14 @@ pub(crate) fn render_error_report( return format!("{kind}: {message}\n ─[{source_id}:0:0]"); } - let mut builder = Report::build(kind, source_id, source_start) + let range = error.text_range.char(); + + let mut builder = Report::build(kind, source_id, range.start) .with_config(Config::default().with_color(with_color)) .with_message(message); builder.add_label( - Label::new((source_id, source_start..source_end)) + Label::new((source_id, range)) .with_color(color) .with_message("Error occurred here.".to_string()), ); diff --git a/crates/solidity/outputs/cargo/slang_solidity/src/generated/parse_error.rs b/crates/solidity/outputs/cargo/slang_solidity/src/generated/parse_error.rs index 18f49a82e2..8e4fc80a6a 100644 --- a/crates/solidity/outputs/cargo/slang_solidity/src/generated/parse_error.rs +++ b/crates/solidity/outputs/cargo/slang_solidity/src/generated/parse_error.rs @@ -3,7 +3,7 @@ use std::collections::BTreeSet; use crate::kinds::TokenKind; -use crate::text_index::{TextIndex, TextRange}; +use crate::text_index::{TextIndex, TextRange, TextRangeExtensions}; #[derive(Debug, PartialEq, Eq, Clone)] pub struct ParseError { @@ -66,8 +66,6 @@ pub(crate) fn render_error_report( let kind = ReportKind::Error; let color = if with_color { Color::Red } else { Color::Unset }; - let source_start = error.text_range.start.utf8; - let source_end = error.text_range.end.utf8; let tokens_that_would_have_allowed_more_progress = error.tokens_that_would_have_allowed_more_progress(); @@ -84,12 +82,14 @@ pub(crate) fn render_error_report( return format!("{kind}: {message}\n ─[{source_id}:0:0]"); } - let mut builder = Report::build(kind, source_id, source_start) + let range = error.text_range.char(); + + let mut builder = Report::build(kind, source_id, range.start) .with_config(Config::default().with_color(with_color)) .with_message(message); builder.add_label( - Label::new((source_id, source_start..source_end)) + Label::new((source_id, range)) .with_color(color) .with_message("Error occurred here.".to_string()), ); diff --git a/crates/solidity/outputs/cargo/tests/src/cst_output/generated/ContractDefinition.rs b/crates/solidity/outputs/cargo/tests/src/cst_output/generated/ContractDefinition.rs index 413727a0a2..eb1ada1bc1 100644 --- a/crates/solidity/outputs/cargo/tests/src/cst_output/generated/ContractDefinition.rs +++ b/crates/solidity/outputs/cargo/tests/src/cst_output/generated/ContractDefinition.rs @@ -125,6 +125,11 @@ fn recovery_testbed() -> Result<()> { run("ContractDefinition", "recovery_testbed") } +#[test] +fn unicode_in_doc_comments() -> Result<()> { + run("ContractDefinition", "unicode_in_doc_comments") +} + #[test] fn unterminated_body() -> Result<()> { run("ContractDefinition", "unterminated_body") diff --git a/crates/solidity/testing/snapshots/cst_output/ContractDefinition/unicode_in_doc_comments/generated/0.4.11-failure.yml b/crates/solidity/testing/snapshots/cst_output/ContractDefinition/unicode_in_doc_comments/generated/0.4.11-failure.yml new file mode 100644 index 0000000000..b6d1399eae --- /dev/null +++ b/crates/solidity/testing/snapshots/cst_output/ContractDefinition/unicode_in_doc_comments/generated/0.4.11-failure.yml @@ -0,0 +1,22 @@ +# This file is generated automatically by infrastructure scripts. Please don't edit by hand. + +Source: > + 1 │ // ╒════════════════════════════════════════════════════════════════╕ │ 0..201 + 2 │ // │ More Info: https://github.com/NomicFoundation/slang/issues/742 │ │ 202..275 + 3 │ // ╘════════════════════════════════════════════════════════════════╛ │ 276..477 + 4 │ unexpected │ 478..488 + +Errors: # 1 total + - > + Error: Expected ContractKeyword. + ╭─[crates/solidity/testing/snapshots/cst_output/ContractDefinition/unicode_in_doc_comments/input.sol:1:1] + │ + 1 │ ╭─▶ // ╒════════════════════════════════════════════════════════════════╕ + ┆ ┆ + 4 │ ├─▶ unexpected + │ │ + │ ╰──────────────── Error occurred here. + ───╯ + +Tree: + - (SKIPPED): "// ╒══════════════════════════════════════════════..." # (0..489) diff --git a/crates/solidity/testing/snapshots/cst_output/ContractDefinition/unicode_in_doc_comments/input.sol b/crates/solidity/testing/snapshots/cst_output/ContractDefinition/unicode_in_doc_comments/input.sol new file mode 100644 index 0000000000..8b920efa9b --- /dev/null +++ b/crates/solidity/testing/snapshots/cst_output/ContractDefinition/unicode_in_doc_comments/input.sol @@ -0,0 +1,4 @@ +// ╒════════════════════════════════════════════════════════════════╕ +// │ More Info: https://github.com/NomicFoundation/slang/issues/742 │ +// ╘════════════════════════════════════════════════════════════════╛ +unexpected diff --git a/crates/solidity/testing/snapshots/cst_output/Expression/unicode_string_literal/generated/0.4.11-failure.yml b/crates/solidity/testing/snapshots/cst_output/Expression/unicode_string_literal/generated/0.4.11-failure.yml index b4ccb71a76..ec7f3e9cee 100644 --- a/crates/solidity/testing/snapshots/cst_output/Expression/unicode_string_literal/generated/0.4.11-failure.yml +++ b/crates/solidity/testing/snapshots/cst_output/Expression/unicode_string_literal/generated/0.4.11-failure.yml @@ -8,7 +8,9 @@ Errors: # 1 total Error: Expected Ampersand or AmpersandAmpersand or AmpersandEqual or Asterisk or AsteriskAsterisk or AsteriskEqual or BangEqual or Bar or BarBar or BarEqual or Caret or CaretEqual or Equal or EqualEqual or GreaterThan or GreaterThanEqual or GreaterThanGreaterThan or GreaterThanGreaterThanEqual or GreaterThanGreaterThanGreaterThan or GreaterThanGreaterThanGreaterThanEqual or LessThan or LessThanEqual or LessThanLessThan or LessThanLessThanEqual or Minus or MinusEqual or Percent or PercentEqual or Plus or PlusEqual or Slash or SlashEqual. ╭─[crates/solidity/testing/snapshots/cst_output/Expression/unicode_string_literal/input.sol:1:8] │ - 1 │ ╭─▶ unicode"This Emoji: 😃" + 1 │ unicode"This Emoji: 😃" + │ ───────┬──────── + │ ╰────────── Error occurred here. ───╯ Tree: diff --git a/crates/solidity/testing/snapshots/cst_output/UnicodeStringLiterals/multiple/generated/0.4.11-failure.yml b/crates/solidity/testing/snapshots/cst_output/UnicodeStringLiterals/multiple/generated/0.4.11-failure.yml index dd7d87ac8d..9720706bb0 100644 --- a/crates/solidity/testing/snapshots/cst_output/UnicodeStringLiterals/multiple/generated/0.4.11-failure.yml +++ b/crates/solidity/testing/snapshots/cst_output/UnicodeStringLiterals/multiple/generated/0.4.11-failure.yml @@ -8,7 +8,9 @@ Errors: # 1 total Error: Expected end of file. ╭─[crates/solidity/testing/snapshots/cst_output/UnicodeStringLiterals/multiple/input.sol:1:1] │ - 1 │ ╭─▶ unicode"happy 😃" unicode'sad 😔' + 1 │ unicode"happy 😃" unicode'sad 😔' + │ ────────────────┬──────────────── + │ ╰────────────────── Error occurred here. ───╯ Tree: diff --git a/crates/solidity/testing/snapshots/cst_output/UnicodeStringLiterals/single/generated/0.4.11-failure.yml b/crates/solidity/testing/snapshots/cst_output/UnicodeStringLiterals/single/generated/0.4.11-failure.yml index 25d3238156..28c749df3b 100644 --- a/crates/solidity/testing/snapshots/cst_output/UnicodeStringLiterals/single/generated/0.4.11-failure.yml +++ b/crates/solidity/testing/snapshots/cst_output/UnicodeStringLiterals/single/generated/0.4.11-failure.yml @@ -8,7 +8,9 @@ Errors: # 1 total Error: Expected end of file. ╭─[crates/solidity/testing/snapshots/cst_output/UnicodeStringLiterals/single/input.sol:1:1] │ - 1 │ ╭─▶ unicode"emoji 😃" + 1 │ unicode"emoji 😃" + │ ────────┬──────── + │ ╰────────── Error occurred here. ───╯ Tree: diff --git a/crates/solidity/testing/snapshots/cst_output/UnicodeStringLiterals/single_trailing_ident/generated/0.4.11-failure.yml b/crates/solidity/testing/snapshots/cst_output/UnicodeStringLiterals/single_trailing_ident/generated/0.4.11-failure.yml index 63f0bf031f..23ddd6445b 100644 --- a/crates/solidity/testing/snapshots/cst_output/UnicodeStringLiterals/single_trailing_ident/generated/0.4.11-failure.yml +++ b/crates/solidity/testing/snapshots/cst_output/UnicodeStringLiterals/single_trailing_ident/generated/0.4.11-failure.yml @@ -8,7 +8,9 @@ Errors: # 1 total Error: Expected end of file. ╭─[crates/solidity/testing/snapshots/cst_output/UnicodeStringLiterals/single_trailing_ident/input.sol:1:1] │ - 1 │ ╭─▶ unicode"emoji 😃"happy + 1 │ unicode"emoji 😃"happy + │ ───────────┬─────────── + │ ╰───────────── Error occurred here. ───╯ Tree: diff --git a/crates/solidity/testing/snapshots/cst_output/UnicodeStringLiterals/single_trailing_ident/generated/0.7.0-failure.yml b/crates/solidity/testing/snapshots/cst_output/UnicodeStringLiterals/single_trailing_ident/generated/0.7.0-failure.yml index bc4aecf7f2..c02fa4e499 100644 --- a/crates/solidity/testing/snapshots/cst_output/UnicodeStringLiterals/single_trailing_ident/generated/0.7.0-failure.yml +++ b/crates/solidity/testing/snapshots/cst_output/UnicodeStringLiterals/single_trailing_ident/generated/0.7.0-failure.yml @@ -6,9 +6,11 @@ Source: > Errors: # 1 total - > Error: Expected UnicodeStringLiteral. - ╭─[crates/solidity/testing/snapshots/cst_output/UnicodeStringLiterals/single_trailing_ident/input.sol:1:20] + ╭─[crates/solidity/testing/snapshots/cst_output/UnicodeStringLiterals/single_trailing_ident/input.sol:1:17] │ - 1 │ ╭─▶ unicode"emoji 😃"happy + 1 │ unicode"emoji 😃"happy + │ ───┬── + │ ╰──── Error occurred here. ───╯ Tree: diff --git a/crates/solidity/testing/utils/src/cst_snapshots/mod.rs b/crates/solidity/testing/utils/src/cst_snapshots/mod.rs index 09e8f5dbb5..2feb2ff10d 100644 --- a/crates/solidity/testing/utils/src/cst_snapshots/mod.rs +++ b/crates/solidity/testing/utils/src/cst_snapshots/mod.rs @@ -1,4 +1,4 @@ -use std::cmp::max; +use std::cmp::{max, min}; use std::collections::HashSet; use std::fmt::Write; use std::ops::Range; @@ -149,29 +149,29 @@ fn render_key(cursor: &mut CursorWithNames) -> String { } fn render_value(cursor: &mut CursorWithNames, source: &str) -> String { - let range = cursor.text_range().utf8(); - let preview = render_preview(source, &range); + let utf8_range = cursor.text_range().utf8(); + let char_range = cursor.text_range().char(); + let preview = render_preview(source, &char_range); match cursor.node() { - Node::Rule(rule) if rule.children.is_empty() => format!("[] # ({range:?})"), - Node::Rule(_) => format!("# {preview} ({range:?})"), - Node::Token(_) => format!("{preview} # ({range:?})"), + Node::Rule(rule) if rule.children.is_empty() => format!("[] # ({utf8_range:?})"), + Node::Rule(_) => format!("# {preview} ({utf8_range:?})"), + Node::Token(_) => format!("{preview} # ({utf8_range:?})"), } } -fn render_preview(source: &str, range: &Range) -> String { - let length = range.len(); +fn render_preview(source: &str, char_range: &Range) -> String { + let length = char_range.len(); // Trim long values: let max_length = 50; - let contents = source - .bytes() - .skip(range.start) - .take(length.clamp(0, max_length)) + let mut contents: String = source + .chars() + .skip(char_range.start) + .take(min(length, max_length)) .collect(); // Add terminator if trimmed: - let mut contents = String::from_utf8(contents).unwrap(); if length > max_length { contents.push_str("..."); }