Skip to content

Commit 3aa40f4

Browse files
committed
Centralize diagnostic code constants
1 parent 3c34021 commit 3aa40f4

7 files changed

Lines changed: 86 additions & 53 deletions

File tree

src/analyzer.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::checks;
2-
use crate::diagnostic::Diagnostic;
2+
use crate::diagnostic::{Diagnostic, code};
33
use crate::hir::{FunctionSig, Hir, HirTypeKind};
44
use crate::lexer::{Token, lex};
55
use crate::syntax::ast::{Callee, EffectDecl, Item};
@@ -42,7 +42,7 @@ impl Analyzer<'_> {
4242
let span = self.tokens.first().map(|token| token.span.clone()).unwrap();
4343
self.diagnostics.push(
4444
Diagnostic::error(
45-
"RS0001",
45+
code::MISSING_FILE_MODE,
4646
"RSScript files must declare exactly one file mode.",
4747
span,
4848
"missing mode",
@@ -64,7 +64,7 @@ impl Analyzer<'_> {
6464
if function.return_ty.is_none() {
6565
self.diagnostics.push(
6666
Diagnostic::error(
67-
"RS0002",
67+
code::MISSING_RETURN_TYPE,
6868
format!("function `{}` must declare an explicit return type.", function.name),
6969
function.span.clone(),
7070
"missing return type",
@@ -78,7 +78,7 @@ impl Analyzer<'_> {
7878
if param.ty.name.is_empty() {
7979
self.diagnostics.push(
8080
Diagnostic::error(
81-
"RS0003",
81+
code::MISSING_PARAMETER_TYPE,
8282
format!(
8383
"parameter `{}` in `{}` must declare an explicit type.",
8484
param.name, function.name
@@ -106,7 +106,7 @@ impl Analyzer<'_> {
106106
|| matches!(effect, EffectDecl::Retains(_));
107107
if !valid {
108108
self.diagnostics.push(Diagnostic::warning(
109-
"RS0004",
109+
code::UNKNOWN_EFFECT,
110110
format!("unknown effect `{effect_name}` in `{}`.", function.name),
111111
function.span.clone(),
112112
"unknown effect",
@@ -128,7 +128,7 @@ impl Analyzer<'_> {
128128
if self.hir.type_kind(&field.ty.name) == Some(HirTypeKind::Resource) {
129129
self.diagnostics.push(
130130
Diagnostic::error(
131-
"RS0701",
131+
code::RESOURCE_FIELD,
132132
format!("resource `{}` cannot be stored in `{}`.", field.ty.name, decl.name),
133133
field.span.clone(),
134134
"resource field",

src/checks/body.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::collections::{HashMap, HashSet};
22

33
use crate::analyzer::Analyzer;
4-
use crate::diagnostic::Diagnostic;
4+
use crate::diagnostic::{Diagnostic, code};
55
use crate::syntax::ast::{
66
Block, CallArg, Callee, DataEffect, Expr, FunctionDecl, Item, LetKind, Stmt,
77
};
@@ -76,7 +76,7 @@ fn check_stmt_semantics(
7676
{
7777
analyzer.diagnostics.push(
7878
Diagnostic::error(
79-
"RS0301",
79+
code::MANAGED_TO_LOCAL,
8080
format!(
8181
"managed value cannot be converted to local binding `{}`.",
8282
stmt.name
@@ -397,7 +397,7 @@ fn check_moved_uses_in_stmt(analyzer: &mut Analyzer<'_>, statement: &Stmt, state
397397
if let Some(move_span) = state.moved.get(&name) {
398398
analyzer.diagnostics.push(
399399
Diagnostic::error(
400-
"RS0401",
400+
code::USE_AFTER_MANAGE,
401401
format!("`{name}` was moved into the managed runtime by `manage {name}`."),
402402
span,
403403
"used after manage",
@@ -423,7 +423,7 @@ fn check_managed_closure_captures(analyzer: &mut Analyzer<'_>, body: &Block, sta
423423
if state.locals.contains(&name) {
424424
analyzer.diagnostics.push(
425425
Diagnostic::error(
426-
"RS0801",
426+
code::LOCAL_CAPTURED_BY_MANAGED_CLOSURE,
427427
format!("managed closure captures local value `{name}`."),
428428
span,
429429
"local captured here",
@@ -451,7 +451,7 @@ fn check_take_of_handle_field(analyzer: &mut Analyzer<'_>, expr: &Expr, state: &
451451
{
452452
analyzer.diagnostics.push(
453453
Diagnostic::error(
454-
"RS0901",
454+
code::TAKE_HANDLE_FIELD,
455455
format!("cannot `take` handle field `{name}`."),
456456
span.clone(),
457457
"take of handle field",
@@ -641,7 +641,7 @@ fn resource_escape_diagnostic(
641641
) {
642642
analyzer.diagnostics.push(
643643
Diagnostic::error(
644-
"RS0702",
644+
code::RESOURCE_ESCAPE,
645645
format!("resource `{binding}` cannot escape its `with` block."),
646646
span,
647647
"resource escapes",
@@ -657,7 +657,7 @@ fn resource_capture_diagnostic(
657657
) {
658658
analyzer.diagnostics.push(
659659
Diagnostic::error(
660-
"RS0702",
660+
code::RESOURCE_ESCAPE,
661661
format!("resource `{binding}` cannot be captured by a managed closure."),
662662
span,
663663
"resource captured",
@@ -690,7 +690,7 @@ fn check_fresh_return(
690690
{
691691
analyzer.diagnostics.push(
692692
Diagnostic::warning(
693-
"RS0602",
693+
code::FRESHNESS_UNKNOWN,
694694
format!(
695695
"freshness of return value in `{}` could not be proven.",
696696
function.name
@@ -712,7 +712,7 @@ fn check_fresh_return(
712712
if !constructor_is_struct && !call_returns_fresh {
713713
analyzer.diagnostics.push(
714714
Diagnostic::warning(
715-
"RS0602",
715+
code::FRESHNESS_UNKNOWN,
716716
format!(
717717
"freshness of return value in `{}` could not be proven.",
718718
function.name
@@ -730,7 +730,7 @@ fn check_fresh_return(
730730
Expr::Field { span, .. } | Expr::Closure { span, .. } | Expr::Unknown(span) => {
731731
analyzer.diagnostics.push(
732732
Diagnostic::warning(
733-
"RS0602",
733+
code::FRESHNESS_UNKNOWN,
734734
format!(
735735
"freshness of return value in `{}` could not be proven.",
736736
function.name
@@ -754,7 +754,7 @@ fn fresh_return_diagnostic(
754754
) {
755755
analyzer.diagnostics.push(
756756
Diagnostic::error(
757-
"RS0601",
757+
code::FRESH_RETURN_NOT_CLEAN,
758758
format!(
759759
"fresh function `{}` returns managed value `{name}`.",
760760
function.name

src/checks/calls.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::collections::{HashMap, HashSet};
22

33
use crate::analyzer::Analyzer;
4-
use crate::diagnostic::Diagnostic;
4+
use crate::diagnostic::{Diagnostic, code};
55
use crate::syntax::ast::{Block, CallArg, Callee, DataEffect, Expr, Item, LetKind, Stmt};
66

77
pub(crate) fn check(analyzer: &mut Analyzer<'_>) {
@@ -83,7 +83,7 @@ fn check_call_args(
8383
if arg.name.is_none() {
8484
analyzer.diagnostics.push(
8585
Diagnostic::error(
86-
"RS0201",
86+
code::UNNAMED_ARGUMENT,
8787
format!("call to `{call_name}` uses an unnamed argument."),
8888
arg.span.clone(),
8989
"argument must be named",
@@ -122,7 +122,7 @@ fn check_call_args(
122122
if expr_data_effect(&arg.value) != Some(*expected) {
123123
analyzer.diagnostics.push(
124124
Diagnostic::error(
125-
"RS0202",
125+
code::MISSING_DATA_EFFECT,
126126
format!("argument `{name}` for `{call_name}` is missing `{expected}`."),
127127
arg.value.span().clone(),
128128
"missing data effect",
@@ -154,7 +154,7 @@ fn check_call_args(
154154
{
155155
analyzer.diagnostics.push(
156156
Diagnostic::error(
157-
"RS0501",
157+
code::LOCAL_VALUE_RETAINED,
158158
format!("retaining API `{call_name}` cannot retain local value `{var}`."),
159159
span.clone(),
160160
"local value retained",

src/checks/forbidden.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::analyzer::Analyzer;
2-
use crate::diagnostic::Diagnostic;
2+
use crate::diagnostic::{Diagnostic, code};
33
use crate::lexer::TokenKind;
44

55
pub(crate) fn check(analyzer: &mut Analyzer<'_>) {
@@ -30,7 +30,7 @@ fn check_operator_overload_attempts(analyzer: &mut Analyzer<'_>) {
3030
if !left_number && !right_number && likely_type_name {
3131
analyzer.diagnostics.push(
3232
Diagnostic::error(
33-
"RS1001",
33+
code::OPERATOR_OVERLOAD_ATTEMPT,
3434
"operators cannot be overloaded for user-defined types.",
3535
analyzer.tokens[i].span.clone(),
3636
"operator on non-builtin-looking value",

src/checks/mode.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::analyzer::Analyzer;
2-
use crate::diagnostic::Diagnostic;
2+
use crate::diagnostic::{Diagnostic, code};
33
use crate::syntax::ast::{
44
DataEffect, Expr, FileMode as SyntaxFileMode, Item, LetKind, Stmt, TypeRef,
55
};
@@ -142,7 +142,7 @@ fn check_type_ref(analyzer: &mut Analyzer<'_>, ty: &TypeRef) {
142142

143143
fn mode_violation(analyzer: &mut Analyzer<'_>, summary: &str, span: crate::diagnostic::Span) {
144144
analyzer.diagnostics.push(
145-
Diagnostic::error("RS0101", summary, span, "mode violation").with_fix(
145+
Diagnostic::error(code::FILE_MODE_VIOLATION, summary, span, "mode violation").with_fix(
146146
"change_mode",
147147
"Change the file declaration to `mode: uses-local`.",
148148
"manual",

src/diagnostic.rs

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,37 @@
11
use serde::Serialize;
22

3+
pub mod code {
4+
pub const MISSING_FILE_MODE: &str = "RS0001";
5+
pub const MISSING_RETURN_TYPE: &str = "RS0002";
6+
pub const MISSING_PARAMETER_TYPE: &str = "RS0003";
7+
pub const UNKNOWN_EFFECT: &str = "RS0004";
8+
pub const FILE_MODE_VIOLATION: &str = "RS0101";
9+
pub const UNNAMED_ARGUMENT: &str = "RS0201";
10+
pub const MISSING_DATA_EFFECT: &str = "RS0202";
11+
pub const MANAGED_TO_LOCAL: &str = "RS0301";
12+
pub const USE_AFTER_MANAGE: &str = "RS0401";
13+
pub const LOCAL_VALUE_RETAINED: &str = "RS0501";
14+
pub const FRESH_RETURN_NOT_CLEAN: &str = "RS0601";
15+
pub const FRESHNESS_UNKNOWN: &str = "RS0602";
16+
pub const RESOURCE_FIELD: &str = "RS0701";
17+
pub const RESOURCE_ESCAPE: &str = "RS0702";
18+
pub const LOCAL_CAPTURED_BY_MANAGED_CLOSURE: &str = "RS0801";
19+
pub const TAKE_HANDLE_FIELD: &str = "RS0901";
20+
pub const OPERATOR_OVERLOAD_ATTEMPT: &str = "RS1001";
21+
22+
pub const REVIEW_MODE_CHANGED: &str = "RSR001";
23+
pub const REVIEW_FUNCTION_REMOVED: &str = "RSR002";
24+
pub const REVIEW_FUNCTION_ADDED: &str = "RSR003";
25+
pub const REVIEW_PARAMS_CHANGED: &str = "RSR004";
26+
pub const REVIEW_RETURN_CHANGED: &str = "RSR005";
27+
pub const REVIEW_EFFECTS_CHANGED: &str = "RSR006";
28+
pub const REVIEW_TYPE_REMOVED: &str = "RSR007";
29+
pub const REVIEW_TYPE_ADDED: &str = "RSR008";
30+
pub const REVIEW_TYPE_KIND_CHANGED: &str = "RSR009";
31+
pub const REVIEW_TYPE_FIELDS_CHANGED: &str = "RSR010";
32+
pub const REVIEW_BOUNDARY_CHANGED: &str = "RSR011";
33+
}
34+
335
#[derive(Debug, Clone, PartialEq, Eq)]
436
pub enum Severity {
537
Error,
@@ -153,87 +185,87 @@ pub fn format_diagnostics_human(diagnostics: &[Diagnostic]) -> String {
153185

154186
static DIAGNOSTIC_EXPLANATIONS: &[DiagnosticExplanation] = &[
155187
DiagnosticExplanation {
156-
code: "RS0001",
188+
code: code::MISSING_FILE_MODE,
157189
title: "missing file mode",
158190
explanation: "Every RSScript source file must declare `mode: managed` or `mode: uses-local` so reviewers can see whether local ownership features are allowed.",
159191
},
160192
DiagnosticExplanation {
161-
code: "RS0002",
193+
code: code::MISSING_RETURN_TYPE,
162194
title: "missing return type",
163195
explanation: "Function signatures must spell out their return type. The checker applies this review rule broadly so API contracts do not rely on inference.",
164196
},
165197
DiagnosticExplanation {
166-
code: "RS0003",
198+
code: code::MISSING_PARAMETER_TYPE,
167199
title: "missing parameter type",
168200
explanation: "Parameters must have explicit types so call effects, freshness, and resource rules can be checked against a stable signature.",
169201
},
170202
DiagnosticExplanation {
171-
code: "RS0004",
203+
code: code::UNKNOWN_EFFECT,
172204
title: "unknown effect",
173205
explanation: "The effect list contains an effect name outside the currently recognized MVP surface.",
174206
},
175207
DiagnosticExplanation {
176-
code: "RS0101",
208+
code: code::FILE_MODE_VIOLATION,
177209
title: "file mode violation",
178210
explanation: "`mode: managed` files cannot use local-only features such as `local`, `manage`, `take`, or `ResourcePool<T>`.",
179211
},
180212
DiagnosticExplanation {
181-
code: "RS0201",
213+
code: code::UNNAMED_ARGUMENT,
182214
title: "unnamed argument",
183215
explanation: "RSScript requires named call arguments so signature and review diffs remain readable.",
184216
},
185217
DiagnosticExplanation {
186-
code: "RS0202",
218+
code: code::MISSING_DATA_EFFECT,
187219
title: "missing call-site data effect",
188220
explanation: "Arguments for non-Copy parameters must use an explicit `read`, `mut`, or `take` effect matching the callee signature.",
189221
},
190222
DiagnosticExplanation {
191-
code: "RS0301",
223+
code: code::MANAGED_TO_LOCAL,
192224
title: "managed-to-local conversion",
193225
explanation: "Managed values cannot be rebound as local values. Create the value locally at its origin if local ownership is required.",
194226
},
195227
DiagnosticExplanation {
196-
code: "RS0401",
228+
code: code::USE_AFTER_MANAGE,
197229
title: "use after manage",
198230
explanation: "`manage value` moves a local value into the managed runtime. The original local binding cannot be used afterwards on any reachable path.",
199231
},
200232
DiagnosticExplanation {
201-
code: "RS0501",
233+
code: code::LOCAL_VALUE_RETAINED,
202234
title: "local value retained",
203235
explanation: "APIs marked `effects(retains(param))` may store the argument beyond the call. Passing a clean local value directly would let local ownership escape.",
204236
},
205237
DiagnosticExplanation {
206-
code: "RS0601",
238+
code: code::FRESH_RETURN_NOT_CLEAN,
207239
title: "fresh return is not clean",
208240
explanation: "A `fresh` function may only return a newly created value, a known fresh call, or a clean local value that has not escaped through manage, take, retain, or capture.",
209241
},
210242
DiagnosticExplanation {
211-
code: "RS0602",
243+
code: code::FRESHNESS_UNKNOWN,
212244
title: "freshness unknown",
213245
explanation: "The MVP checker could not prove the returned value is fresh. Current proof support trusts clean locals, struct constructors, and known fresh calls.",
214246
},
215247
DiagnosticExplanation {
216-
code: "RS0701",
248+
code: code::RESOURCE_FIELD,
217249
title: "resource field",
218250
explanation: "Resource values cannot be stored directly in ordinary class or struct fields. Use `with` or an approved resource container such as `ResourcePool<T>`.",
219251
},
220252
DiagnosticExplanation {
221-
code: "RS0702",
253+
code: code::RESOURCE_ESCAPE,
222254
title: "resource escape",
223255
explanation: "A resource introduced by `with` must not escape the block through return, manage, retention, or managed closure capture.",
224256
},
225257
DiagnosticExplanation {
226-
code: "RS0801",
258+
code: code::LOCAL_CAPTURED_BY_MANAGED_CLOSURE,
227259
title: "local captured by managed closure",
228260
explanation: "A closure bound with `let` is managed and may outlive clean local values. Use a local/noescape callback shape instead.",
229261
},
230262
DiagnosticExplanation {
231-
code: "RS0901",
263+
code: code::TAKE_HANDLE_FIELD,
232264
title: "take of handle field",
233265
explanation: "Handle fields are managed references. They cannot be consumed with `take` as if they were inline local fields.",
234266
},
235267
DiagnosticExplanation {
236-
code: "RS1001",
268+
code: code::OPERATOR_OVERLOAD_ATTEMPT,
237269
title: "operator overload attempt",
238270
explanation: "The MVP language surface rejects likely user-defined operator overloads to keep review semantics explicit.",
239271
},

0 commit comments

Comments
 (0)