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