tsz_checker/error_reporter/
properties.rs1use crate::diagnostics::{Diagnostic, diagnostic_codes, diagnostic_messages, format_message};
4use crate::state::CheckerState;
5use tsz_parser::parser::NodeIndex;
6use tsz_scanner::SyntaxKind;
7use tsz_solver::TypeId;
8
9impl<'a> CheckerState<'a> {
10 pub fn error_property_missing_at(
16 &mut self,
17 prop_name: &str,
18 source: TypeId,
19 target: TypeId,
20 idx: NodeIndex,
21 ) {
22 if source == TypeId::ERROR
24 || target == TypeId::ERROR
25 || source == TypeId::ANY
26 || target == TypeId::ANY
27 || source == TypeId::UNKNOWN
28 || target == TypeId::UNKNOWN
29 {
30 return;
31 }
32
33 if let Some(loc) = self.get_source_location(idx) {
34 let mut builder = tsz_solver::SpannedDiagnosticBuilder::with_symbols(
35 self.ctx.types,
36 &self.ctx.binder.symbols,
37 self.ctx.file_name.as_str(),
38 )
39 .with_def_store(&self.ctx.definition_store);
40 let diag = builder.property_missing(prop_name, source, target, loc.start, loc.length());
41 self.ctx
42 .diagnostics
43 .push(diag.to_checker_diagnostic(&self.ctx.file_name));
44 }
45 }
46
47 pub fn error_property_not_exist_at(
51 &mut self,
52 prop_name: &str,
53 type_id: TypeId,
54 idx: NodeIndex,
55 ) {
56 use tsz_solver::type_queries;
57
58 if type_id == TypeId::ERROR
62 || type_id == TypeId::ANY
63 || type_queries::is_error_type(self.ctx.types, type_id)
64 {
65 return;
66 }
67
68 if type_queries::is_union_type(self.ctx.types, type_id)
73 && type_queries::contains_type_parameters_db(self.ctx.types, type_id)
74 {
75 return;
76 }
77
78 if let Some(loc) = self.get_source_location(idx) {
79 let suppress_did_you_mean =
80 self.has_syntax_parse_errors() || self.class_extends_any_base(type_id);
81
82 let suggestion = if suppress_did_you_mean {
85 None
86 } else {
87 self.find_similar_property(prop_name, type_id)
88 };
89
90 let mut builder = tsz_solver::SpannedDiagnosticBuilder::with_symbols(
91 self.ctx.types,
92 &self.ctx.binder.symbols,
93 self.ctx.file_name.as_str(),
94 )
95 .with_def_store(&self.ctx.definition_store);
96
97 let diag = if let Some(ref suggestion) = suggestion {
98 builder.property_not_exist_did_you_mean(
99 prop_name,
100 type_id,
101 suggestion,
102 loc.start,
103 loc.length(),
104 )
105 } else {
106 builder.property_not_exist(prop_name, type_id, loc.start, loc.length())
107 };
108 self.ctx
110 .push_diagnostic(diag.to_checker_diagnostic(&self.ctx.file_name));
111 }
112 }
113
114 pub fn error_excess_property_at(&mut self, prop_name: &str, target: TypeId, idx: NodeIndex) {
116 if self.ctx.compiler_options.suppress_excess_property_errors {
118 return;
119 }
120 if target == TypeId::ERROR || target == TypeId::ANY || target == TypeId::UNKNOWN {
122 return;
123 }
124
125 if let Some(loc) = self.get_source_location(idx) {
126 let mut builder = tsz_solver::SpannedDiagnosticBuilder::with_symbols(
127 self.ctx.types,
128 &self.ctx.binder.symbols,
129 self.ctx.file_name.as_str(),
130 )
131 .with_def_store(&self.ctx.definition_store);
132 let diag = builder.excess_property(prop_name, target, loc.start, loc.length());
133 self.ctx
135 .push_diagnostic(diag.to_checker_diagnostic(&self.ctx.file_name));
136 }
137 }
138
139 pub fn error_readonly_property_at(&mut self, prop_name: &str, idx: NodeIndex) {
141 if let Some(loc) = self.get_source_location(idx) {
142 let mut builder = tsz_solver::SpannedDiagnosticBuilder::with_symbols(
143 self.ctx.types,
144 &self.ctx.binder.symbols,
145 self.ctx.file_name.as_str(),
146 )
147 .with_def_store(&self.ctx.definition_store);
148 let diag = builder.readonly_property(prop_name, loc.start, loc.length());
149 self.ctx
150 .diagnostics
151 .push(diag.to_checker_diagnostic(&self.ctx.file_name));
152 }
153 }
154
155 pub fn error_readonly_index_signature_at(
157 &mut self,
158 object_type: tsz_solver::TypeId,
159 idx: NodeIndex,
160 ) {
161 if let Some(loc) = self.get_source_location(idx) {
162 let type_name = self.format_type(object_type);
163 let message = format_message(
164 diagnostic_messages::INDEX_SIGNATURE_IN_TYPE_ONLY_PERMITS_READING,
165 &[&type_name],
166 );
167 let diag = Diagnostic::error(
168 self.ctx.file_name.clone(),
169 loc.start,
170 loc.length(),
171 message,
172 diagnostic_codes::INDEX_SIGNATURE_IN_TYPE_ONLY_PERMITS_READING,
173 );
174 self.ctx.diagnostics.push(diag);
175 }
176 }
177
178 pub fn error_private_method_not_writable(&mut self, prop_name: &str, idx: NodeIndex) {
180 if let Some(loc) = self.get_source_location(idx) {
181 let message = format_message(
182 diagnostic_messages::CANNOT_ASSIGN_TO_PRIVATE_METHOD_PRIVATE_METHODS_ARE_NOT_WRITABLE,
183 &[prop_name],
184 );
185 let diag = Diagnostic::error(
186 self.ctx.file_name.clone(),
187 loc.start,
188 loc.length(),
189 message,
190 diagnostic_codes::CANNOT_ASSIGN_TO_PRIVATE_METHOD_PRIVATE_METHODS_ARE_NOT_WRITABLE,
191 );
192 self.ctx.diagnostics.push(diag);
193 }
194 }
195
196 pub(crate) fn error_no_index_signature_at(
198 &mut self,
199 index_type: TypeId,
200 object_type: TypeId,
201 idx: NodeIndex,
202 ) {
203 if self.ctx.compiler_options.suppress_implicit_any_index_errors {
205 return;
206 }
207 if !self.ctx.no_implicit_any() {
209 return;
210 }
211 if index_type == TypeId::ANY || index_type == TypeId::ERROR || index_type == TypeId::UNKNOWN
213 {
214 return;
215 }
216 if object_type == TypeId::ANY
217 || object_type == TypeId::ERROR
218 || object_type == TypeId::UNKNOWN
219 {
220 return;
221 }
222 if self.is_element_access_on_this_or_super_with_any_base(idx) {
223 return;
224 }
225
226 if let Some(atom) =
227 tsz_solver::type_queries::get_string_literal_value(self.ctx.types, index_type)
228 {
229 let prop_name = self.ctx.types.resolve_atom_ref(atom);
230 let prop_name_str: &str = &prop_name;
231 let suppress_did_you_mean =
232 self.has_syntax_parse_errors() || self.class_extends_any_base(object_type);
233
234 let suggestion = if suppress_did_you_mean {
235 None
236 } else {
237 self.find_similar_property(prop_name_str, object_type)
238 };
239
240 if suggestion.is_some() {
241 self.error_property_not_exist_at(prop_name_str, object_type, idx);
243 return;
244 }
245 }
246
247 let mut formatter = self.ctx.create_type_formatter();
248 let index_str = formatter.format(index_type);
249 let object_str = formatter.format(object_type);
250 let message = format!(
251 "Element implicitly has an 'any' type because expression of type '{index_str}' can't be used to index type '{object_str}'."
252 );
253
254 self.error_at_node(idx, &message, diagnostic_codes::ELEMENT_IMPLICITLY_HAS_AN_ANY_TYPE_BECAUSE_EXPRESSION_OF_TYPE_CANT_BE_USED_TO_IN);
255 }
256
257 fn is_element_access_on_this_or_super_with_any_base(&mut self, idx: NodeIndex) -> bool {
259 use tsz_parser::parser::syntax_kind_ext;
260
261 let Some(ext) = self.ctx.arena.get_extended(idx) else {
262 return false;
263 };
264 let Some(parent) = self.ctx.arena.get(ext.parent) else {
265 return false;
266 };
267 if parent.kind != syntax_kind_ext::ELEMENT_ACCESS_EXPRESSION {
268 return false;
269 }
270 let Some(access) = self.ctx.arena.get_access_expr(parent) else {
271 return false;
272 };
273 if access.name_or_argument != idx {
274 return false;
275 }
276 let Some(expr_node) = self.ctx.arena.get(access.expression) else {
277 return false;
278 };
279 let is_this_or_super = expr_node.kind == SyntaxKind::SuperKeyword as u16
280 || expr_node.kind == SyntaxKind::ThisKeyword as u16;
281 if !is_this_or_super {
282 return false;
283 }
284
285 let Some(class_info) = self.ctx.enclosing_class.clone() else {
286 return false;
287 };
288 let Some(class_decl) = self.ctx.arena.get_class_at(class_info.class_idx) else {
289 return false;
290 };
291 let Some(heritage_clauses) = &class_decl.heritage_clauses else {
292 return false;
293 };
294
295 for &clause_idx in &heritage_clauses.nodes {
296 let Some(clause) = self.ctx.arena.get_heritage_clause_at(clause_idx) else {
297 continue;
298 };
299 if clause.token != SyntaxKind::ExtendsKeyword as u16 {
300 continue;
301 }
302 let Some(&type_idx) = clause.types.nodes.first() else {
303 continue;
304 };
305 let expr_idx =
306 if let Some(expr_type_args) = self.ctx.arena.get_expr_type_args_at(type_idx) {
307 expr_type_args.expression
308 } else {
309 type_idx
310 };
311 if self.get_type_of_node(expr_idx) == TypeId::ANY {
312 return true;
313 }
314 }
315
316 false
317 }
318}