1use cairo_lang_defs::diagnostic_utils::StableLocation;
2use cairo_lang_diagnostics::{
3 DiagnosticAdded, DiagnosticEntry, DiagnosticNote, DiagnosticsBuilder, Severity, error_code,
4};
5use cairo_lang_filesystem::ids::SpanInFile;
6use cairo_lang_semantic as semantic;
7use cairo_lang_semantic::expr::inference::InferenceError;
8use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
9use salsa::Database;
10
11use crate::Location;
12
13pub type LoweringDiagnostics<'db> = DiagnosticsBuilder<'db, LoweringDiagnostic<'db>>;
14pub trait LoweringDiagnosticsBuilder<'db> {
15 fn report(
16 &mut self,
17 stable_ptr: impl Into<SyntaxStablePtrId<'db>>,
18 kind: LoweringDiagnosticKind<'db>,
19 ) -> DiagnosticAdded {
20 self.report_by_location(Location::new(StableLocation::new(stable_ptr.into())), kind)
21 }
22 fn report_by_location(
23 &mut self,
24 location: Location<'db>,
25 kind: LoweringDiagnosticKind<'db>,
26 ) -> DiagnosticAdded;
27}
28impl<'db> LoweringDiagnosticsBuilder<'db> for LoweringDiagnostics<'db> {
29 fn report_by_location(
30 &mut self,
31 location: Location<'db>,
32 kind: LoweringDiagnosticKind<'db>,
33 ) -> DiagnosticAdded {
34 self.add(LoweringDiagnostic { location, kind })
35 }
36}
37
38#[derive(Clone, Debug, Eq, Hash, PartialEq, salsa::Update)]
39pub struct LoweringDiagnostic<'db> {
40 pub location: Location<'db>,
41 pub kind: LoweringDiagnosticKind<'db>,
42}
43
44impl<'db> DiagnosticEntry<'db> for LoweringDiagnostic<'db> {
45 fn format(&self, _db: &'db dyn Database) -> String {
46 match &self.kind {
47 LoweringDiagnosticKind::Unreachable { .. } => "Unreachable code".into(),
48 LoweringDiagnosticKind::VariableMoved { .. } => "Variable was previously moved.".into(),
49 LoweringDiagnosticKind::VariableNotDropped { .. } => "Variable not dropped.".into(),
50 LoweringDiagnosticKind::DesnappingANonCopyableType { .. } => {
51 "Cannot desnap a non copyable type.".into()
52 }
53 LoweringDiagnosticKind::MatchError(match_err) => match_err.format(),
54 LoweringDiagnosticKind::CannotInlineFunctionThatMightCallItself => {
55 "Cannot inline a function that might call itself.".into()
56 }
57 LoweringDiagnosticKind::MemberPathLoop => {
58 "Currently, loops must change the entire variable.".into()
59 }
60 LoweringDiagnosticKind::UnexpectedError => {
61 "Unexpected error has occurred, Please submit a full bug report. \
62 See https://github.com/starkware-libs/cairo/issues/new/choose for instructions.\
63 "
64 .into()
65 }
66 LoweringDiagnosticKind::NoPanicFunctionCycle => {
67 "Call cycle of `nopanic` functions is not allowed.".into()
68 }
69 LoweringDiagnosticKind::RefutablePattern => "Refutable pattern in irrefutable \
70 binding. Refutable patterns are only \
71 supported in `match`, `if let`, \
72 `while let`, and `let ... else`."
73 .into(),
74 LoweringDiagnosticKind::Unsupported => "Unsupported feature.".into(),
75 LoweringDiagnosticKind::FixedSizeArrayNonCopyableType => {
76 "Fixed size array inner type must implement the `Copy` trait when the array size \
77 is greater than 1."
78 .into()
79 }
80 LoweringDiagnosticKind::EmptyRepeatedElementFixedSizeArray => {
81 "Fixed size array repeated element size must be greater than 0.".into()
82 }
83 }
84 }
85
86 fn severity(&self) -> Severity {
87 match self.kind {
88 LoweringDiagnosticKind::Unreachable { .. }
89 | LoweringDiagnosticKind::MatchError(MatchError {
90 kind: _,
91 error: MatchDiagnostic::UnreachableMatchArm,
92 }) => Severity::Warning,
93 _ => Severity::Error,
94 }
95 }
96
97 fn notes(&self, _db: &dyn Database) -> &[DiagnosticNote<'_>] {
98 &self.location.notes
99 }
100
101 fn location(&self, db: &'db dyn Database) -> SpanInFile<'db> {
102 if let LoweringDiagnosticKind::Unreachable { block_end_ptr } = &self.kind {
103 return self.location.stable_location.span_in_file_until(db, *block_end_ptr);
104 }
105 self.location.stable_location.span_in_file(db)
106 }
107
108 fn error_code(&self) -> Option<cairo_lang_diagnostics::ErrorCode> {
109 Some(match &self.kind {
110 LoweringDiagnosticKind::Unreachable { .. } => error_code!(E3000),
111 LoweringDiagnosticKind::VariableMoved { .. } => error_code!(E3001),
112 LoweringDiagnosticKind::VariableNotDropped { .. } => error_code!(E3002),
113 LoweringDiagnosticKind::DesnappingANonCopyableType { .. } => error_code!(E3003),
114 LoweringDiagnosticKind::MatchError(_) => error_code!(E3004),
115 LoweringDiagnosticKind::CannotInlineFunctionThatMightCallItself => error_code!(E3005),
116 LoweringDiagnosticKind::MemberPathLoop => error_code!(E3006),
117 LoweringDiagnosticKind::UnexpectedError => error_code!(E3007),
118 LoweringDiagnosticKind::NoPanicFunctionCycle => error_code!(E3008),
119 LoweringDiagnosticKind::RefutablePattern => error_code!(E3010),
120 LoweringDiagnosticKind::Unsupported => error_code!(E3011),
121 LoweringDiagnosticKind::FixedSizeArrayNonCopyableType => error_code!(E3012),
122 LoweringDiagnosticKind::EmptyRepeatedElementFixedSizeArray => error_code!(E3013),
123 })
124 }
125
126 fn is_same_kind(&self, other: &Self) -> bool {
127 other.kind == self.kind
128 }
129}
130
131impl<'db> MatchError<'db> {
132 fn format(&self) -> String {
133 match (&self.error, &self.kind) {
134 (MatchDiagnostic::UnsupportedMatchedType(matched_type), MatchKind::Match) => {
135 format!("Unsupported matched type. Type: `{matched_type}`.")
136 }
137 (MatchDiagnostic::UnsupportedMatchedType(matched_type), MatchKind::IfLet) => {
138 format!("Unsupported type in if-let. Type: `{matched_type}`.")
139 }
140 (MatchDiagnostic::UnsupportedMatchedType(matched_type), MatchKind::WhileLet(_, _)) => {
141 format!("Unsupported type in while-let. Type: `{matched_type}`.")
142 }
143 (MatchDiagnostic::UnsupportedMatchedValueTuple, MatchKind::Match) => {
144 "Unsupported matched value. Currently, match on tuples only supports enums as \
145 tuple members."
146 .into()
147 }
148 (MatchDiagnostic::UnsupportedMatchedValueTuple, MatchKind::IfLet) => {
149 "Unsupported value in if-let. Currently, if-let on tuples only supports enums as \
150 tuple members."
151 .into()
152 }
153 (MatchDiagnostic::UnsupportedMatchedValueTuple, MatchKind::WhileLet(_, _)) => {
154 "Unsupported value in while-let. Currently, while-let on tuples only supports \
155 enums as tuple members."
156 .into()
157 }
158 (MatchDiagnostic::UnsupportedMatchArmNotAVariant, _) => {
159 "Unsupported pattern - not a variant.".into()
160 }
161 (MatchDiagnostic::UnsupportedMatchArmNotATuple, _) => {
162 "Unsupported pattern - not a tuple.".into()
163 }
164 (MatchDiagnostic::UnsupportedMatchArmNotALiteral, MatchKind::Match) => {
165 "Unsupported match arm - not a literal.".into()
166 }
167 (MatchDiagnostic::UnsupportedMatchArmNonSequential, MatchKind::Match) => {
168 "Unsupported match - numbers must be sequential starting from 0.".into()
169 }
170 (
171 MatchDiagnostic::UnsupportedMatchArmNotALiteral
172 | MatchDiagnostic::UnsupportedMatchArmNonSequential,
173 MatchKind::IfLet | MatchKind::WhileLet(_, _),
174 ) => unreachable!("Numeric values are not supported in if/while-let conditions."),
175 (MatchDiagnostic::NonExhaustiveMatch(variant), MatchKind::Match) => {
176 format!("Match is non-exhaustive: `{variant}` not covered.")
177 }
178 (MatchDiagnostic::NonExhaustiveMatch(_), MatchKind::IfLet) => {
179 unreachable!("If-let is not required to be exhaustive.")
180 }
181 (MatchDiagnostic::NonExhaustiveMatch(_), MatchKind::WhileLet(_, _)) => {
182 unreachable!("While-let is not required to be exhaustive.")
183 }
184 (MatchDiagnostic::UnreachableMatchArm, MatchKind::Match) => {
185 "Unreachable pattern arm.".into()
186 }
187 (MatchDiagnostic::UnreachableMatchArm, MatchKind::IfLet) => {
188 "Unreachable clause.".into()
189 }
190 (MatchDiagnostic::UnreachableMatchArm, MatchKind::WhileLet(_, _)) => {
191 unreachable!("While-let does not have two arms.")
192 }
193 (MatchDiagnostic::UnsupportedNumericInLetCondition, MatchKind::Match) => {
194 unreachable!("Numeric values are supported in match conditions.")
195 }
196 (MatchDiagnostic::UnsupportedNumericInLetCondition, MatchKind::IfLet) => {
197 "Numeric values are not supported in if-let conditions.".into()
198 }
199 (MatchDiagnostic::UnsupportedNumericInLetCondition, MatchKind::WhileLet(_, _)) => {
200 "Numeric values are not supported in while-let conditions.".into()
201 }
202 }
203 }
204}
205
206#[derive(Clone, Debug, Eq, Hash, PartialEq, salsa::Update)]
207pub enum LoweringDiagnosticKind<'db> {
208 Unreachable { block_end_ptr: SyntaxStablePtrId<'db> },
209 VariableMoved { inference_error: InferenceError<'db> },
210 VariableNotDropped { drop_err: InferenceError<'db>, destruct_err: InferenceError<'db> },
211 MatchError(MatchError<'db>),
212 DesnappingANonCopyableType { inference_error: InferenceError<'db> },
213 UnexpectedError,
214 CannotInlineFunctionThatMightCallItself,
215 MemberPathLoop,
216 NoPanicFunctionCycle,
217 FixedSizeArrayNonCopyableType,
218 EmptyRepeatedElementFixedSizeArray,
219 RefutablePattern,
220 Unsupported,
221}
222
223#[derive(Clone, Debug, Eq, Hash, PartialEq, salsa::Update)]
226pub struct MatchError<'db> {
227 pub kind: MatchKind<'db>,
228 pub error: MatchDiagnostic,
229}
230
231#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
233pub enum MatchKind<'db> {
234 Match,
235 IfLet,
236 WhileLet(semantic::ExprId, SyntaxStablePtrId<'db>),
237}
238
239unsafe impl<'db> salsa::Update for MatchKind<'db> {
240 unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
241 let old_value = unsafe { &mut *old_pointer };
242 match (old_value, &new_value) {
243 (MatchKind::Match, MatchKind::Match) | (MatchKind::IfLet, MatchKind::IfLet) => false,
244 (MatchKind::WhileLet(expr, end_ptr), MatchKind::WhileLet(new_expr, new_end_ptr)) => {
245 if unsafe { SyntaxStablePtrId::maybe_update(end_ptr, *new_end_ptr) } {
246 *expr = *new_expr;
247 true
248 } else if expr != new_expr {
249 *end_ptr = *new_end_ptr;
250 *expr = *new_expr;
251 true
252 } else {
253 false
254 }
255 }
256 (old_value, new_value) => {
257 *old_value = *new_value;
258 true
259 }
260 }
261 }
262}
263
264#[derive(Clone, Debug, Eq, Hash, PartialEq, salsa::Update)]
265pub enum MatchDiagnostic {
266 UnsupportedMatchedType(String),
268 UnsupportedMatchedValueTuple,
269 UnsupportedMatchArmNotAVariant,
270 UnsupportedMatchArmNotATuple,
271
272 UnreachableMatchArm,
273 NonExhaustiveMatch(String),
274
275 UnsupportedMatchArmNotALiteral,
276 UnsupportedMatchArmNonSequential,
277 UnsupportedNumericInLetCondition,
278}