Skip to content

Commit 78ad2d9

Browse files
olwangclaude
andcommitted
rsscript: diagnostic provenance — name source + lowered symbol
A remapped backend (rustc) error already reported the RSS file, source span, generated Rust location, and rustc code. Now it also names the enclosing source symbol and its lowered Rust symbol. - RustSourceMapEntry gains optional `symbol`/`lowered_symbol` (serde-default, so existing source maps round-trip). - lower_function stamps every entry it produces with the function's source name and lowered Rust ident. - remap surfaces `RSScript symbol: <name> (lowered: <name>)` in the diagnostic. Test: extended the manage-return remap test to assert the symbol provenance. lib/frontend/lowering/package green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 70336a6 commit 78ad2d9

6 files changed

Lines changed: 67 additions & 19 deletions

File tree

crates/rsscript/src/diagnostic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ impl Severity {
152152
}
153153
}
154154

155-
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
155+
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
156156
pub struct Span {
157157
pub file: String,
158158
pub line: usize,

crates/rsscript/src/rust_lower/lowerer.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,7 @@ impl<'a> RustLowerer<'a> {
460460
kind: "field".to_string(),
461461
source: field.span.clone(),
462462
generated: generated_span_at_end(out, "src/lib.rs", "field"),
463+
..Default::default()
463464
});
464465
if field.is_weak {
465466
out.push_str(&format!(
@@ -638,6 +639,7 @@ impl<'a> RustLowerer<'a> {
638639
&& block_needs_async_executor(&function.body))
639640
.then(|| "__rsscript_async_executor".to_string());
640641
let generated_start = out.len();
642+
let source_map_start = self.source_map.len();
641643
let marker = self.record_source_marker(out, 0, "function", &function.span);
642644
let is_public = function.is_public || is_runnable_main(function);
643645
out.push_str(&format!(
@@ -691,6 +693,14 @@ impl<'a> RustLowerer<'a> {
691693
&marker.generated,
692694
generated_line_count(&out[generated_start..]),
693695
);
696+
// Stamp every source-map entry produced while lowering this function with
697+
// its source symbol and lowered Rust symbol, so a remapped backend error
698+
// can name the declaration (not just the line).
699+
let lowered_symbol = rust_function_ident(&function.name);
700+
for entry in &mut self.source_map[source_map_start..] {
701+
entry.symbol = Some(function.name.clone());
702+
entry.lowered_symbol = Some(lowered_symbol.clone());
703+
}
694704
self.param_effects = previous_param_effects;
695705
self.value_types = previous_value_types;
696706
self.managed_bindings = previous_managed_bindings;
@@ -1983,6 +1993,7 @@ impl<'a> RustLowerer<'a> {
19831993
kind: "with".to_string(),
19841994
source: stmt.span.clone(),
19851995
generated: generated.clone(),
1996+
..Default::default()
19861997
});
19871998
self.record_expr_source_map(&stmt.resource, generated);
19881999
self.record_block_source_map(&stmt.body, generated);
@@ -2045,6 +2056,7 @@ impl<'a> RustLowerer<'a> {
20452056
kind: "statement".to_string(),
20462057
source: stmt_span(statement).clone(),
20472058
generated: generated.clone(),
2059+
..Default::default()
20482060
});
20492061
self.record_statement_source_map(statement, generated);
20502062
}
@@ -2059,6 +2071,7 @@ impl<'a> RustLowerer<'a> {
20592071
kind: "binary".to_string(),
20602072
source: span.clone(),
20612073
generated: generated.clone(),
2074+
..Default::default()
20622075
});
20632076
self.record_expr_source_map(left, generated);
20642077
self.record_expr_source_map(right, generated);
@@ -2068,6 +2081,7 @@ impl<'a> RustLowerer<'a> {
20682081
kind: "field_path".to_string(),
20692082
source: span.clone(),
20702083
generated: generated.clone(),
2084+
..Default::default()
20712085
});
20722086
self.record_expr_source_map(base, generated);
20732087
}
@@ -2080,19 +2094,22 @@ impl<'a> RustLowerer<'a> {
20802094
kind: "call".to_string(),
20812095
source: span.clone(),
20822096
generated: generated.clone(),
2097+
..Default::default()
20832098
});
20842099
if self.is_native_boundary_call(callee) {
20852100
self.source_map.push(RustSourceMapEntry {
20862101
kind: "native_call".to_string(),
20872102
source: span.clone(),
20882103
generated: generated.clone(),
2104+
..Default::default()
20892105
});
20902106
}
20912107
for arg in args {
20922108
self.source_map.push(RustSourceMapEntry {
20932109
kind: "named_arg".to_string(),
20942110
source: arg.span.clone(),
20952111
generated: generated.clone(),
2112+
..Default::default()
20962113
});
20972114
self.record_expr_source_map(&arg.value, generated);
20982115
}
@@ -2103,6 +2120,7 @@ impl<'a> RustLowerer<'a> {
21032120
kind: "manage".to_string(),
21042121
source: span.clone(),
21052122
generated: generated.clone(),
2123+
..Default::default()
21062124
});
21072125
self.record_expr_source_map(value, generated);
21082126
}
@@ -2111,6 +2129,7 @@ impl<'a> RustLowerer<'a> {
21112129
kind: "spawn".to_string(),
21122130
source: span.clone(),
21132131
generated: generated.clone(),
2132+
..Default::default()
21142133
});
21152134
self.record_expr_source_map(value, generated);
21162135
}
@@ -2119,6 +2138,7 @@ impl<'a> RustLowerer<'a> {
21192138
kind: "await".to_string(),
21202139
source: span.clone(),
21212140
generated: generated.clone(),
2141+
..Default::default()
21222142
});
21232143
self.record_expr_source_map(value, generated);
21242144
}
@@ -2127,6 +2147,7 @@ impl<'a> RustLowerer<'a> {
21272147
kind: "try".to_string(),
21282148
source: span.clone(),
21292149
generated: generated.clone(),
2150+
..Default::default()
21302151
});
21312152
self.record_expr_source_map(value, generated);
21322153
}
@@ -2135,6 +2156,7 @@ impl<'a> RustLowerer<'a> {
21352156
kind: "closure".to_string(),
21362157
source: span.clone(),
21372158
generated: generated.clone(),
2159+
..Default::default()
21382160
});
21392161
self.record_block_source_map(body, generated);
21402162
}
@@ -2145,6 +2167,7 @@ impl<'a> RustLowerer<'a> {
21452167
kind: "match_pattern".to_string(),
21462168
source: match_pattern_span(&arm.pattern),
21472169
generated: generated.clone(),
2170+
..Default::default()
21482171
});
21492172
self.record_block_source_map(&arm.body, generated);
21502173
}
@@ -2154,12 +2177,14 @@ impl<'a> RustLowerer<'a> {
21542177
kind: "object_literal".to_string(),
21552178
source: span.clone(),
21562179
generated: generated.clone(),
2180+
..Default::default()
21572181
});
21582182
for field in fields {
21592183
self.source_map.push(RustSourceMapEntry {
21602184
kind: "object_literal_field".to_string(),
21612185
source: field.span.clone(),
21622186
generated: generated.clone(),
2187+
..Default::default()
21632188
});
21642189
self.record_expr_source_map(&field.value, generated);
21652190
}
@@ -2169,12 +2194,14 @@ impl<'a> RustLowerer<'a> {
21692194
kind: "map_literal".to_string(),
21702195
source: span.clone(),
21712196
generated: generated.clone(),
2197+
..Default::default()
21722198
});
21732199
for entry in entries {
21742200
self.source_map.push(RustSourceMapEntry {
21752201
kind: "map_literal_entry".to_string(),
21762202
source: entry.span.clone(),
21772203
generated: generated.clone(),
2204+
..Default::default()
21782205
});
21792206
self.record_expr_source_map(&entry.key, generated);
21802207
self.record_expr_source_map(&entry.value, generated);
@@ -2185,6 +2212,7 @@ impl<'a> RustLowerer<'a> {
21852212
kind: "array_literal".to_string(),
21862213
source: span.clone(),
21872214
generated: generated.clone(),
2215+
..Default::default()
21882216
});
21892217
for item in items {
21902218
self.record_expr_source_map(item, generated);
@@ -2194,17 +2222,20 @@ impl<'a> RustLowerer<'a> {
21942222
kind: "ident".to_string(),
21952223
source: span.clone(),
21962224
generated: generated.clone(),
2225+
..Default::default()
21972226
}),
21982227
Expr::Number(_, span) => self.source_map.push(RustSourceMapEntry {
21992228
kind: "number".to_string(),
22002229
source: span.clone(),
22012230
generated: generated.clone(),
2231+
..Default::default()
22022232
}),
22032233
Expr::String(_, span) | Expr::MultilineString(_, span) => {
22042234
self.source_map.push(RustSourceMapEntry {
22052235
kind: "string".to_string(),
22062236
source: span.clone(),
22072237
generated: generated.clone(),
2238+
..Default::default()
22082239
})
22092240
}
22102241
Expr::Unknown(_) => {}

crates/rsscript/src/rust_lower/source_map.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,20 +43,23 @@ pub fn remap_rustc_diagnostic_json(
4343
{
4444
let severity = rustc_severity(&rustc.level);
4545
let summary = format!("backend diagnostic mapped to RSScript: {}", rustc.message);
46+
let mut causes = vec![format!("rustc code: {backend_code}")];
47+
if let Some(symbol) = &entry.symbol {
48+
let lowered = entry.lowered_symbol.as_deref().unwrap_or(symbol);
49+
causes.push(format!("RSScript symbol: {symbol} (lowered: {lowered})"));
50+
}
51+
causes.push(format!(
52+
"generated Rust: {}:{}:{}",
53+
rustc_span.file_name, rustc_span.line_start, rustc_span.column_start
54+
));
55+
causes.push(rustc.message);
4656
let diagnostic = Diagnostic {
4757
code: code::RUSTC_DIAGNOSTIC_MAPPED.to_string(),
4858
severity,
4959
summary,
5060
span: entry.source.clone(),
5161
label: "backend diagnostic maps to this RSScript construct".to_string(),
52-
causes: vec![
53-
format!("rustc code: {backend_code}"),
54-
format!(
55-
"generated Rust: {}:{}:{}",
56-
rustc_span.file_name, rustc_span.line_start, rustc_span.column_start
57-
),
58-
rustc.message,
59-
],
62+
causes,
6063
fixes: Vec::new(),
6164
};
6265
return Ok(Some(RemappedRustcDiagnostic {
@@ -208,6 +211,7 @@ pub(super) fn push_source_marker(
208211
kind: kind.to_string(),
209212
source: span.clone(),
210213
generated,
214+
..Default::default()
211215
}
212216
}
213217

crates/rsscript/src/rust_lower/types.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,18 @@ pub struct LoweredRust {
2525
pub source_map: Vec<RustSourceMapEntry>,
2626
}
2727

28-
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
28+
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
2929
pub struct RustSourceMapEntry {
3030
pub kind: String,
3131
pub source: Span,
3232
pub generated: Span,
33+
/// The enclosing RSScript source symbol (e.g. `helpers.count`), stamped per
34+
/// function so a remapped backend error can name the declaration.
35+
#[serde(default, skip_serializing_if = "Option::is_none")]
36+
pub symbol: Option<String>,
37+
/// The lowered Rust symbol the enclosing function lowers to.
38+
#[serde(default, skip_serializing_if = "Option::is_none")]
39+
pub lowered_symbol: Option<String>,
3340
}
3441

3542
#[derive(Debug, Clone, PartialEq, Eq)]

crates/rsscript/tests/checker_lowering/stdlib.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1527,6 +1527,18 @@ pub fn make_session(id: Int) -> Session {
15271527
.iter()
15281528
.any(|cause| cause.contains("rustc code: E0308"))
15291529
);
1530+
// Provenance: the remapped error names the enclosing RSScript source symbol
1531+
// and its lowered Rust symbol.
1532+
assert!(
1533+
remapped
1534+
.diagnostic
1535+
.causes
1536+
.iter()
1537+
.any(|cause| cause.contains("RSScript symbol: make_session")
1538+
&& cause.contains("lowered: make_session")),
1539+
"expected source/lowered symbol provenance: {:?}",
1540+
remapped.diagnostic.causes
1541+
);
15301542
}
15311543

15321544
#[test]
@@ -1558,6 +1570,7 @@ fn rustc_diagnostics_do_not_map_to_nearest_previous_source_marker() {
15581570
column: 1,
15591571
length: 2,
15601572
},
1573+
..Default::default()
15611574
}];
15621575
let rustc_json = r#"{"message":"cannot find value","code":{"code":"E0425","explanation":null},"level":"error","spans":[{"file_name":"src/lib.rs","line_start":200,"line_end":200,"column_start":1,"column_end":2,"is_primary":true}]}"#;
15631576

docs/tinygrad-port-todo.md

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ parameters (Copy and non-Copy), type aliases (generic + non-generic, expanded
1212
at every comparison site), reserved `__rss_`/`__rsscript_` namespaces for
1313
compiler-generated helpers (Python-style dunders like `__hash__` stay legal), and
1414
struct field defaults (`name: T = expr`, filled at
15-
construction on both backends).
15+
construction on both backends), and diagnostic provenance (remapped backend
16+
errors name the RSS source symbol and its lowered Rust symbol).
1617

1718
Verified already-met against their written acceptance (closed without new code):
1819
- **Method/property getters** — modeled as zero-argument methods
@@ -35,11 +36,3 @@ Verified already-met against their written acceptance (closed without new code):
3536
function's signature and lowered as a function value.
3637
_Why:_ tinygrad passes named matcher/rewrite functions; without this the port
3738
wraps each in a closure or struct.
38-
39-
- [ ] **RSS-to-Rust diagnostic provenance: symbol names.** Remapped backend
40-
errors already report the RSS file, source span, generated Rust location, and
41-
rustc code. Still missing: the enclosing **source symbol** name and the
42-
**lowered Rust symbol** name.
43-
_Why:_ naming the declaration (not just the line) speeds triage.
44-
_Acceptance:_ a remapped Rust backend error names the RSS source symbol and its
45-
lowered Rust symbol.

0 commit comments

Comments
 (0)