Skip to content

Commit 542b114

Browse files
olwangclaude
andcommitted
Merge refactor/diag: consolidate diagnostic-construction helpers
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2 parents 9597bbe + b076236 commit 542b114

9 files changed

Lines changed: 691 additions & 959 deletions

File tree

crates/rsscript/src/analyzer.rs

Lines changed: 64 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -2955,45 +2955,39 @@ impl Analyzer<'_> {
29552955
"Remove `pure`, or change the parameter to `read` if the function does not mutate it.",
29562956
),
29572957
};
2958-
self.diagnostics.push(
2959-
Diagnostic::error(
2960-
code::INVALID_PURE_EFFECT,
2961-
format!(
2962-
"`{}` is declared pure but parameter `{}` uses `{}`.",
2963-
function.name,
2964-
param.name,
2965-
param.effect.map_or("unknown", data_effect_name)
2966-
),
2967-
param.span.clone(),
2968-
label,
2969-
)
2970-
.with_cause(cause)
2971-
.with_fix("remove_pure_or_use_read", fix, "manual"),
2972-
);
2958+
self.diagnostics.push(checks::diagnostic_helpers::error_cause_manual_fix(
2959+
code::INVALID_PURE_EFFECT,
2960+
format!(
2961+
"`{}` is declared pure but parameter `{}` uses `{}`.",
2962+
function.name,
2963+
param.name,
2964+
param.effect.map_or("unknown", data_effect_name)
2965+
),
2966+
param.span.clone(),
2967+
label,
2968+
cause,
2969+
"remove_pure_or_use_read",
2970+
fix,
2971+
));
29732972
}
29742973

29752974
for effect in function
29762975
.effects
29772976
.iter()
29782977
.filter(|effect| matches!(effect, EffectDecl::Retains(_)))
29792978
{
2980-
self.diagnostics.push(
2981-
Diagnostic::error(
2982-
code::INVALID_PURE_EFFECT,
2983-
format!(
2984-
"`{}` is declared pure but also retains a parameter.",
2985-
function.name
2986-
),
2987-
function.span.clone(),
2988-
"retention in pure function",
2989-
)
2990-
.with_cause("A `pure` function must not retain parameters after returning.")
2991-
.with_fix(
2992-
"remove_pure_or_retains",
2993-
format!("Remove `pure` or remove `{}`.", effect_display(effect)),
2994-
"manual",
2979+
self.diagnostics.push(checks::diagnostic_helpers::error_cause_manual_fix(
2980+
code::INVALID_PURE_EFFECT,
2981+
format!(
2982+
"`{}` is declared pure but also retains a parameter.",
2983+
function.name
29952984
),
2996-
);
2985+
function.span.clone(),
2986+
"retention in pure function",
2987+
"A `pure` function must not retain parameters after returning.",
2988+
"remove_pure_or_retains",
2989+
format!("Remove `pure` or remove `{}`.", effect_display(effect)),
2990+
));
29972991
}
29982992
}
29992993
}
@@ -4728,67 +4722,46 @@ impl Analyzer<'_> {
47284722
callee: &Callee,
47294723
span: &crate::diagnostic::Span,
47304724
) {
4731-
self.diagnostics.push(
4732-
Diagnostic::error(
4733-
code::INVALID_PURE_EFFECT,
4734-
format!(
4735-
"`{function_name}` is declared pure but calls non-pure function `{}`.",
4736-
callee_display(callee)
4737-
),
4738-
span.clone(),
4739-
"non-pure call in pure function",
4740-
)
4741-
.with_cause(
4742-
"A `pure` function may only call constructors, enum variants, or functions also declared `effects(pure)`.",
4743-
)
4744-
.with_fix(
4745-
"remove_pure_or_call_pure",
4746-
"Remove `pure`, or call only APIs whose signatures are declared `effects(pure)`.",
4747-
"manual",
4725+
self.diagnostics.push(checks::diagnostic_helpers::error_cause_manual_fix(
4726+
code::INVALID_PURE_EFFECT,
4727+
format!(
4728+
"`{function_name}` is declared pure but calls non-pure function `{}`.",
4729+
callee_display(callee)
47484730
),
4749-
);
4731+
span.clone(),
4732+
"non-pure call in pure function",
4733+
"A `pure` function may only call constructors, enum variants, or functions also declared `effects(pure)`.",
4734+
"remove_pure_or_call_pure",
4735+
"Remove `pure`, or call only APIs whose signatures are declared `effects(pure)`.",
4736+
));
47504737
}
47514738

47524739
fn pure_manage_diagnostic(&mut self, function_name: &str, span: &crate::diagnostic::Span) {
4753-
self.diagnostics.push(
4754-
Diagnostic::error(
4755-
code::INVALID_PURE_EFFECT,
4756-
format!("`{function_name}` is declared pure but uses `manage`."),
4757-
span.clone(),
4758-
"manage in pure function",
4759-
)
4760-
.with_cause(
4761-
"`manage` consumes a local value and changes its ownership boundary; `pure` functions may observe inputs but must not consume local values.",
4762-
)
4763-
.with_fix(
4764-
"remove_manage_or_pure",
4765-
"Move the `manage` operation outside the pure function, or remove `pure`.",
4766-
"manual",
4767-
),
4768-
);
4740+
self.diagnostics.push(checks::diagnostic_helpers::error_cause_manual_fix(
4741+
code::INVALID_PURE_EFFECT,
4742+
format!("`{function_name}` is declared pure but uses `manage`."),
4743+
span.clone(),
4744+
"manage in pure function",
4745+
"`manage` consumes a local value and changes its ownership boundary; `pure` functions may observe inputs but must not consume local values.",
4746+
"remove_manage_or_pure",
4747+
"Move the `manage` operation outside the pure function, or remove `pure`.",
4748+
));
47694749
}
47704750

47714751
fn pure_with_resource_diagnostic(
47724752
&mut self,
47734753
function_name: &str,
47744754
span: &crate::diagnostic::Span,
47754755
) {
4776-
self.diagnostics.push(
4777-
Diagnostic::error(
4778-
code::INVALID_PURE_EFFECT,
4779-
format!("`{function_name}` is declared pure but opens a resource scope."),
4780-
span.clone(),
4781-
"resource scope in pure function",
4782-
)
4783-
.with_cause(
4784-
"`with` introduces deterministic resource lifetime behavior; `pure` functions may observe inputs but must not open resource scopes.",
4785-
)
4786-
.with_fix(
4787-
"remove_with_or_pure",
4788-
"Move resource handling outside the pure function, or remove `pure`.",
4789-
"manual",
4790-
),
4791-
);
4756+
self.diagnostics.push(checks::diagnostic_helpers::error_cause_manual_fix(
4757+
code::INVALID_PURE_EFFECT,
4758+
format!("`{function_name}` is declared pure but opens a resource scope."),
4759+
span.clone(),
4760+
"resource scope in pure function",
4761+
"`with` introduces deterministic resource lifetime behavior; `pure` functions may observe inputs but must not open resource scopes.",
4762+
"remove_with_or_pure",
4763+
"Move resource handling outside the pure function, or remove `pure`.",
4764+
));
47924765
}
47934766

47944767
fn pure_resource_return_diagnostic(
@@ -4797,24 +4770,17 @@ impl Analyzer<'_> {
47974770
span: crate::diagnostic::Span,
47984771
resource_name: &str,
47994772
) {
4800-
self.diagnostics.push(
4801-
Diagnostic::error(
4802-
code::INVALID_PURE_EFFECT,
4803-
format!(
4804-
"`{function_name}` is declared pure but returns resource `{resource_name}`."
4805-
),
4806-
span,
4807-
"resource return in pure function",
4808-
)
4809-
.with_cause(
4810-
"Returning a resource creates a lifetime boundary; `pure` functions must not open or return resources.",
4811-
)
4812-
.with_fix(
4813-
"remove_resource_return_or_pure",
4814-
"Return an ordinary value, or remove `pure` from the resource-producing function.",
4815-
"manual",
4773+
self.diagnostics.push(checks::diagnostic_helpers::error_cause_manual_fix(
4774+
code::INVALID_PURE_EFFECT,
4775+
format!(
4776+
"`{function_name}` is declared pure but returns resource `{resource_name}`."
48164777
),
4817-
);
4778+
span,
4779+
"resource return in pure function",
4780+
"Returning a resource creates a lifetime boundary; `pure` functions must not open or return resources.",
4781+
"remove_resource_return_or_pure",
4782+
"Return an ordinary value, or remove `pure` from the resource-producing function.",
4783+
));
48184784
}
48194785

48204786
fn resource_return_type_name<'a>(&self, ty: &'a TypeRef) -> Option<&'a str> {

crates/rsscript/src/checks/body/effects.rs

Lines changed: 49 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use super::*;
2+
use crate::checks::diagnostic_helpers::error_cause_manual_fix;
23

34
pub(super) fn check_manage_operand_is_local(
45
analyzer: &mut Analyzer<'_>,
@@ -28,20 +29,15 @@ pub(super) fn check_manage_operand_is_local(
2829
}
2930

3031
pub(super) fn read_view_mutation_diagnostic(analyzer: &mut Analyzer<'_>, name: &str, span: Span) {
31-
analyzer.diagnostics.push(
32-
Diagnostic::error(
33-
code::READ_VIEW_MUTATION,
34-
format!("`{name}` is a read view from a `for` loop and cannot be used as an exclusive value."),
35-
span,
36-
"read view mutation",
37-
)
38-
.with_cause("RSScript `for` iterates `List<T>` by read view for non-Copy struct elements, so the loop variable does not own the element.")
39-
.with_fix(
40-
"copy_before_mutating",
41-
"Create a fresh local copy before mutation, or use an explicit partitioning API that grants exclusive element ownership.",
42-
"manual",
43-
),
44-
);
32+
analyzer.diagnostics.push(error_cause_manual_fix(
33+
code::READ_VIEW_MUTATION,
34+
format!("`{name}` is a read view from a `for` loop and cannot be used as an exclusive value."),
35+
span,
36+
"read view mutation",
37+
"RSScript `for` iterates `List<T>` by read view for non-Copy struct elements, so the loop variable does not own the element.",
38+
"copy_before_mutating",
39+
"Create a fresh local copy before mutation, or use an explicit partitioning API that grants exclusive element ownership.",
40+
));
4541
}
4642

4743
pub(super) fn check_take_operand_is_local(
@@ -278,26 +274,21 @@ pub(super) fn fresh_return_target_type(return_ty: &TypeRef) -> &TypeRef {
278274
}
279275

280276
pub(super) fn managed_to_local_diagnostic(analyzer: &mut Analyzer<'_>, managed_to_local: ManagedToLocalUse) {
281-
analyzer.diagnostics.push(
282-
Diagnostic::error(
283-
code::MANAGED_TO_LOCAL,
284-
format!(
285-
"managed value cannot be converted to local binding `{}`.",
286-
managed_to_local.local_name
287-
),
288-
managed_to_local.span,
289-
"managed value used as local",
290-
)
291-
.with_cause(format!(
277+
analyzer.diagnostics.push(error_cause_manual_fix(
278+
code::MANAGED_TO_LOCAL,
279+
format!(
280+
"managed value cannot be converted to local binding `{}`.",
281+
managed_to_local.local_name
282+
),
283+
managed_to_local.span,
284+
"managed value used as local",
285+
format!(
292286
"`{}` is already managed; RSScript has no managed -> local conversion.",
293287
managed_to_local.managed_name
294-
))
295-
.with_fix(
296-
"create_local",
297-
"Create the value as `local` at its creation point.",
298-
"manual",
299288
),
300-
);
289+
"create_local",
290+
"Create the value as `local` at its creation point.",
291+
));
301292
}
302293

303294
pub(super) fn take_handle_field_diagnostic(analyzer: &mut Analyzer<'_>, field: &TakeHandleField) {
@@ -315,29 +306,24 @@ pub(super) fn take_handle_field_diagnostic(analyzer: &mut Analyzer<'_>, field: &
315306
}
316307

317308
pub(super) fn retained_local_diagnostic(analyzer: &mut Analyzer<'_>, retained: RetainedLocalUse) {
318-
analyzer.diagnostics.push(
319-
Diagnostic::error(
320-
code::LOCAL_VALUE_RETAINED,
321-
format!(
322-
"retaining API `{}` cannot retain local value `{}`.",
323-
retained.callee, retained.name
324-
),
325-
retained.span,
326-
"local value retained",
327-
)
328-
.with_cause(format!(
309+
analyzer.diagnostics.push(error_cause_manual_fix(
310+
code::LOCAL_VALUE_RETAINED,
311+
format!(
312+
"retaining API `{}` cannot retain local value `{}`.",
313+
retained.callee, retained.name
314+
),
315+
retained.span,
316+
"local value retained",
317+
format!(
329318
"`{}` declares `effects(retains({}))`.",
330319
retained.callee, retained.param
331-
))
332-
.with_fix(
333-
"manage_local",
334-
format!(
335-
"Pass `{}` through `manage {}` before retaining it.",
336-
retained.param, retained.name
337-
),
338-
"manual",
339320
),
340-
);
321+
"manage_local",
322+
format!(
323+
"Pass `{}` through `manage {}` before retaining it.",
324+
retained.param, retained.name
325+
),
326+
));
341327
}
342328

343329
pub(super) fn retained_closure_capture_diagnostic(
@@ -485,25 +471,20 @@ pub(super) fn try_error_type_mismatch_diagnostic(
485471
}
486472

487473
pub(super) fn moved_use_diagnostic(analyzer: &mut Analyzer<'_>, moved_use: MovedUse) {
488-
analyzer.diagnostics.push(
489-
Diagnostic::error(
490-
code::USE_AFTER_MANAGE,
491-
format!(
492-
"`{}` was moved into the managed runtime by `manage {}`.",
493-
moved_use.name, moved_use.name
494-
),
495-
moved_use.use_span,
496-
"used after manage",
497-
)
498-
.with_cause(format!(
474+
analyzer.diagnostics.push(error_cause_manual_fix(
475+
code::USE_AFTER_MANAGE,
476+
format!(
477+
"`{}` was moved into the managed runtime by `manage {}`.",
478+
moved_use.name, moved_use.name
479+
),
480+
moved_use.use_span,
481+
"used after manage",
482+
format!(
499483
"The move happened at {}:{}.",
500484
moved_use.move_span.line, moved_use.move_span.column
501-
))
502-
.with_fix(
503-
"move_use_before_manage",
504-
format!("Move this use before `manage {}`.", moved_use.name),
505-
"manual",
506485
),
507-
);
486+
"move_use_before_manage",
487+
format!("Move this use before `manage {}`.", moved_use.name),
488+
));
508489
}
509490

0 commit comments

Comments
 (0)