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::corelib::LiteralError;
8use cairo_lang_semantic::expr::inference::InferenceError;
9use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
10use salsa::Database;
11
12use crate::Location;
13
14pub type LoweringDiagnostics<'db> = DiagnosticsBuilder<'db, LoweringDiagnostic<'db>>;
15pub trait LoweringDiagnosticsBuilder<'db> {
16 fn report(
17 &mut self,
18 stable_ptr: impl Into<SyntaxStablePtrId<'db>>,
19 kind: LoweringDiagnosticKind<'db>,
20 ) -> DiagnosticAdded {
21 self.report_by_location(Location::new(StableLocation::new(stable_ptr.into())), kind)
22 }
23 fn report_by_location(
24 &mut self,
25 location: Location<'db>,
26 kind: LoweringDiagnosticKind<'db>,
27 ) -> DiagnosticAdded;
28}
29impl<'db> LoweringDiagnosticsBuilder<'db> for LoweringDiagnostics<'db> {
30 fn report_by_location(
31 &mut self,
32 location: Location<'db>,
33 kind: LoweringDiagnosticKind<'db>,
34 ) -> DiagnosticAdded {
35 self.add(LoweringDiagnostic { location, kind })
36 }
37}
38
39#[derive(Clone, Debug, Eq, Hash, PartialEq, salsa::Update)]
40pub struct LoweringDiagnostic<'db> {
41 pub location: Location<'db>,
42 pub kind: LoweringDiagnosticKind<'db>,
43}
44
45impl<'db> DiagnosticEntry<'db> for LoweringDiagnostic<'db> {
46 fn format(&self, db: &'db dyn Database) -> String {
47 match &self.kind {
48 LoweringDiagnosticKind::Unreachable { .. } => "Unreachable code".into(),
49 LoweringDiagnosticKind::VariableMoved { .. } => "Variable was previously moved.".into(),
50 LoweringDiagnosticKind::VariableNotDropped { .. } => "Variable not dropped.".into(),
51 LoweringDiagnosticKind::DesnappingANonCopyableType { .. } => {
52 "Cannot desnap a non copyable type.".into()
53 }
54 LoweringDiagnosticKind::MatchError(match_err) => match_err.format(),
55 LoweringDiagnosticKind::CannotInlineFunctionThatMightCallItself => {
56 "Cannot inline a function that might call itself.".into()
57 }
58 LoweringDiagnosticKind::MemberPathLoop => {
59 "Currently, loops must change the entire variable.".into()
60 }
61 LoweringDiagnosticKind::UnexpectedError => {
62 "Unexpected error has occurred, Please submit a full bug report. \
63 See https://github.com/starkware-libs/cairo/issues/new/choose for instructions.\
64 "
65 .into()
66 }
67 LoweringDiagnosticKind::NoPanicFunctionCycle => {
68 "Call cycle of `nopanic` functions is not allowed.".into()
69 }
70 LoweringDiagnosticKind::LiteralError(literal_error) => literal_error.format(db),
71 LoweringDiagnosticKind::UnsupportedPattern => {
72 "Inner patterns are not allowed in this context.".into()
73 }
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::LiteralError(_) => error_code!(E3009),
120 LoweringDiagnosticKind::UnsupportedPattern => error_code!(E3010),
121 LoweringDiagnosticKind::Unsupported => error_code!(E3011),
122 LoweringDiagnosticKind::FixedSizeArrayNonCopyableType => error_code!(E3012),
123 LoweringDiagnosticKind::EmptyRepeatedElementFixedSizeArray => error_code!(E3013),
124 })
125 }
126
127 fn is_same_kind(&self, other: &Self) -> bool {
128 other.kind == self.kind
129 }
130}
131
132impl<'db> MatchError<'db> {
133 fn format(&self) -> String {
134 match (&self.error, &self.kind) {
135 (MatchDiagnostic::UnsupportedMatchedType(matched_type), MatchKind::Match) => {
136 format!("Unsupported matched type. Type: `{matched_type}`.")
137 }
138 (MatchDiagnostic::UnsupportedMatchedType(matched_type), MatchKind::IfLet) => {
139 format!("Unsupported type in if-let. Type: `{matched_type}`.")
140 }
141 (MatchDiagnostic::UnsupportedMatchedType(matched_type), MatchKind::WhileLet(_, _)) => {
142 format!("Unsupported type in while-let. Type: `{matched_type}`.")
143 }
144 (MatchDiagnostic::UnsupportedMatchedValueTuple, MatchKind::Match) => {
145 "Unsupported matched value. Currently, match on tuples only supports enums as \
146 tuple members."
147 .into()
148 }
149 (MatchDiagnostic::UnsupportedMatchedValueTuple, MatchKind::IfLet) => {
150 "Unsupported value in if-let. Currently, if-let on tuples only supports enums as \
151 tuple members."
152 .into()
153 }
154 (MatchDiagnostic::UnsupportedMatchedValueTuple, MatchKind::WhileLet(_, _)) => {
155 "Unsupported value in while-let. Currently, while-let on tuples only supports \
156 enums as tuple members."
157 .into()
158 }
159 (MatchDiagnostic::UnsupportedMatchArmNotAVariant, _) => {
160 "Unsupported pattern - not a variant.".into()
161 }
162 (MatchDiagnostic::UnsupportedMatchArmNotATuple, _) => {
163 "Unsupported pattern - not a tuple.".into()
164 }
165 (MatchDiagnostic::UnsupportedMatchArmNotALiteral, MatchKind::Match) => {
166 "Unsupported match arm - not a literal.".into()
167 }
168 (MatchDiagnostic::UnsupportedMatchArmNonSequential, MatchKind::Match) => {
169 "Unsupported match - numbers must be sequential starting from 0.".into()
170 }
171 (
172 MatchDiagnostic::UnsupportedMatchArmNotALiteral
173 | MatchDiagnostic::UnsupportedMatchArmNonSequential,
174 MatchKind::IfLet | MatchKind::WhileLet(_, _),
175 ) => unreachable!("Numeric values are not supported in if/while-let conditions."),
176 (MatchDiagnostic::NonExhaustiveMatch(variant), MatchKind::Match) => {
177 format!("Match is non-exhaustive: `{variant}` not covered.")
178 }
179 (MatchDiagnostic::NonExhaustiveMatch(_), MatchKind::IfLet) => {
180 unreachable!("If-let is not required to be exhaustive.")
181 }
182 (MatchDiagnostic::NonExhaustiveMatch(_), MatchKind::WhileLet(_, _)) => {
183 unreachable!("While-let is not required to be exhaustive.")
184 }
185 (MatchDiagnostic::UnreachableMatchArm, MatchKind::Match) => {
186 "Unreachable pattern arm.".into()
187 }
188 (MatchDiagnostic::UnreachableMatchArm, MatchKind::IfLet) => {
189 "Unreachable clause.".into()
190 }
191 (MatchDiagnostic::UnreachableMatchArm, MatchKind::WhileLet(_, _)) => {
192 unreachable!("While-let does not have two arms.")
193 }
194 (MatchDiagnostic::UnsupportedNumericInLetCondition, MatchKind::Match) => {
195 unreachable!("Numeric values are supported in match conditions.")
196 }
197 (MatchDiagnostic::UnsupportedNumericInLetCondition, MatchKind::IfLet) => {
198 "Numeric values are not supported in if-let conditions.".into()
199 }
200 (MatchDiagnostic::UnsupportedNumericInLetCondition, MatchKind::WhileLet(_, _)) => {
201 "Numeric values are not supported in while-let conditions.".into()
202 }
203 }
204 }
205}
206
207#[derive(Clone, Debug, Eq, Hash, PartialEq, salsa::Update)]
208pub enum LoweringDiagnosticKind<'db> {
209 Unreachable { block_end_ptr: SyntaxStablePtrId<'db> },
210 VariableMoved { inference_error: InferenceError<'db> },
211 VariableNotDropped { drop_err: InferenceError<'db>, destruct_err: InferenceError<'db> },
212 MatchError(MatchError<'db>),
213 DesnappingANonCopyableType { inference_error: InferenceError<'db> },
214 UnexpectedError,
215 CannotInlineFunctionThatMightCallItself,
216 MemberPathLoop,
217 NoPanicFunctionCycle,
218 LiteralError(LiteralError<'db>),
219 FixedSizeArrayNonCopyableType,
220 EmptyRepeatedElementFixedSizeArray,
221 UnsupportedPattern,
222 Unsupported,
223}
224
225#[derive(Clone, Debug, Eq, Hash, PartialEq, salsa::Update)]
228pub struct MatchError<'db> {
229 pub kind: MatchKind<'db>,
230 pub error: MatchDiagnostic,
231}
232
233#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
235pub enum MatchKind<'db> {
236 Match,
237 IfLet,
238 WhileLet(semantic::ExprId, SyntaxStablePtrId<'db>),
239}
240
241unsafe impl<'db> salsa::Update for MatchKind<'db> {
242 unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
243 let old_value = unsafe { &mut *old_pointer };
244 match (old_value, &new_value) {
245 (MatchKind::Match, MatchKind::Match) | (MatchKind::IfLet, MatchKind::IfLet) => false,
246 (MatchKind::WhileLet(expr, end_ptr), MatchKind::WhileLet(new_expr, new_end_ptr)) => {
247 if unsafe { SyntaxStablePtrId::maybe_update(end_ptr, *new_end_ptr) } {
248 *expr = *new_expr;
249 true
250 } else if expr != new_expr {
251 *end_ptr = *new_end_ptr;
252 *expr = *new_expr;
253 true
254 } else {
255 false
256 }
257 }
258 (old_value, new_value) => {
259 *old_value = *new_value;
260 true
261 }
262 }
263 }
264}
265
266#[derive(Clone, Debug, Eq, Hash, PartialEq, salsa::Update)]
267pub enum MatchDiagnostic {
268 UnsupportedMatchedType(String),
270 UnsupportedMatchedValueTuple,
271 UnsupportedMatchArmNotAVariant,
272 UnsupportedMatchArmNotATuple,
273
274 UnreachableMatchArm,
275 NonExhaustiveMatch(String),
276
277 UnsupportedMatchArmNotALiteral,
278 UnsupportedMatchArmNonSequential,
279 UnsupportedNumericInLetCondition,
280}