ezno_checker/
diagnostics.rs

1//! Contains type checking errors, warnings and related structures
2use crate::{
3	context::{environment::Label, AssignmentError, InformationChain},
4	diagnostics,
5	features::{modules::CouldNotOpenFile, CannotDeleteFromError},
6	types::{
7		calling::FunctionCallingError,
8		printing::print_type_with_type_arguments,
9		properties::{assignment::SetPropertyError, PropertyKey},
10		GenericChain, GenericChainLink,
11	},
12};
13use source_map::{SourceId, SpanWithSource};
14use std::{
15	fmt::{self, Debug, Display},
16	iter,
17	path::PathBuf,
18};
19
20#[derive(Debug, Clone, Copy)]
21#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize), serde(rename_all = "lowercase"))]
22#[cfg_attr(target_family = "wasm", derive(tsify::Tsify))]
23pub enum DiagnosticKind {
24	Error,
25	Warning,
26	Info,
27}
28
29/// Contains information
30#[derive(Debug)]
31#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize), serde(untagged))]
32#[cfg_attr(target_family = "wasm", derive(tsify::Tsify))]
33pub enum Diagnostic {
34	/// Does not have positional information
35	Global {
36		reason: String,
37		kind: DiagnosticKind,
38	},
39	Position {
40		reason: String,
41		position: SpanWithSource,
42		kind: DiagnosticKind,
43	},
44	PositionWithAdditionalLabels {
45		reason: String,
46		position: SpanWithSource,
47		labels: Vec<(String, SpanWithSource)>,
48		kind: DiagnosticKind,
49	},
50}
51
52/// Temporary dead zone. Between the variable identifier being hoisted and the value being assigned
53#[allow(clippy::upper_case_acronyms)]
54pub struct VariableUsedInTDZ {
55	pub variable_name: String,
56	pub position: SpanWithSource,
57}
58
59pub struct InvalidRegExp {
60	pub error: String,
61	pub position: SpanWithSource,
62}
63
64pub struct NotInLoopOrCouldNotFindLabel {
65	pub label: Label,
66	pub position: SpanWithSource,
67}
68
69impl Diagnostic {
70	pub fn sources(&self) -> impl Iterator<Item = SourceId> + '_ {
71		use either::{Left, Right};
72		match self {
73			Diagnostic::Global { .. } => Left(Left(iter::empty())),
74			Diagnostic::Position { position: span, .. } => Left(Right(iter::once(span.source))),
75			Diagnostic::PositionWithAdditionalLabels { position: pos, labels, .. } => {
76				Right(iter::once(pos.source).chain(labels.iter().map(|(_, span)| span.source)))
77			}
78		}
79	}
80
81	#[must_use]
82	pub fn reason(&self) -> &str {
83		match self {
84			Diagnostic::Global { reason, .. }
85			| Diagnostic::Position { reason, .. }
86			| Diagnostic::PositionWithAdditionalLabels { reason, .. } => reason,
87		}
88	}
89
90	#[must_use]
91	pub fn reason_and_position(self) -> (String, Option<SpanWithSource>) {
92		match self {
93			Diagnostic::Global { reason, .. } => (reason, None),
94			Diagnostic::Position { reason, position, .. }
95			| Diagnostic::PositionWithAdditionalLabels { reason, position, .. } => (reason, Some(position)),
96		}
97	}
98
99	#[must_use]
100	pub fn kind(&self) -> DiagnosticKind {
101		match self {
102			Diagnostic::Global { kind, .. }
103			| Diagnostic::Position { kind, .. }
104			| Diagnostic::PositionWithAdditionalLabels { kind, .. } => *kind,
105		}
106	}
107}
108
109/// TODO this is one variant, others should pipe strait to stdout or put it on a channel etc
110#[derive(Default)]
111#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize), serde(transparent))]
112#[cfg_attr(target_family = "wasm", derive(tsify::Tsify))]
113pub struct DiagnosticsContainer {
114	diagnostics: Vec<Diagnostic>,
115	// Quick way to check whether a error was added
116	#[cfg_attr(feature = "serde-serialize", serde(skip_serializing))]
117	contains_error: bool,
118}
119
120// TODO the add methods are the same...
121impl DiagnosticsContainer {
122	#[must_use]
123	pub fn new() -> Self {
124		Self { diagnostics: Default::default(), contains_error: false }
125	}
126
127	pub fn add_error<T: Into<Diagnostic>>(&mut self, error: T) {
128		self.contains_error = true;
129		self.diagnostics.push(error.into());
130	}
131
132	pub fn add_warning<T: Into<Diagnostic>>(&mut self, warning: T) {
133		self.diagnostics.push(warning.into());
134	}
135
136	pub fn add_info<T: Into<Diagnostic>>(&mut self, info: T) {
137		self.diagnostics.push(info.into());
138	}
139
140	#[must_use]
141	pub fn contains_error(&self) -> bool {
142		self.contains_error
143	}
144
145	pub fn sources(&self) -> impl Iterator<Item = SourceId> + '_ {
146		self.diagnostics.iter().flat_map(diagnostics::Diagnostic::sources)
147	}
148
149	#[doc(hidden)]
150	#[must_use]
151	pub fn get_diagnostics(self) -> Vec<Diagnostic> {
152		self.diagnostics
153	}
154
155	pub fn into_result(self) -> Result<Self, Self> {
156		if self.contains_error {
157			Err(self)
158		} else {
159			Ok(self)
160		}
161	}
162
163	#[must_use]
164	pub fn count(&self) -> usize {
165		self.diagnostics.len()
166	}
167}
168
169impl IntoIterator for DiagnosticsContainer {
170	type Item = Diagnostic;
171
172	type IntoIter = std::vec::IntoIter<Diagnostic>;
173
174	fn into_iter(self) -> Self::IntoIter {
175		self.diagnostics.into_iter()
176	}
177}
178
179use crate::types::{printing::print_type, TypeId, TypeStore};
180
181/// TODO could be more things, for instance a property missing etc
182pub struct TypeStringRepresentation(String);
183
184pub enum PropertyKeyRepresentation {
185	Type(String),
186	StringKey(String),
187}
188
189impl PropertyKeyRepresentation {
190	pub fn new(
191		under: &PropertyKey,
192		environment: &impl InformationChain,
193		types: &TypeStore,
194	) -> PropertyKeyRepresentation {
195		match under.clone() {
196			PropertyKey::String(s) => PropertyKeyRepresentation::StringKey(s.to_string()),
197			PropertyKey::Type(t) => {
198				PropertyKeyRepresentation::Type(print_type(t, types, environment, false))
199			}
200		}
201	}
202}
203
204impl TypeStringRepresentation {
205	#[must_use]
206	pub fn from_type_id(
207		id: TypeId,
208		ctx: &impl InformationChain,
209		types: &TypeStore,
210		debug_mode: bool,
211	) -> Self {
212		let value = print_type(id, types, ctx, debug_mode);
213		Self(value)
214	}
215
216	#[must_use]
217	pub fn from_type_id_with_generics(
218		id: TypeId,
219		type_arguments: GenericChain,
220		ctx: &impl InformationChain,
221		types: &TypeStore,
222		debug_mode: bool,
223	) -> Self {
224		let value = print_type_with_type_arguments(id, type_arguments, types, ctx, debug_mode);
225		Self(value)
226	}
227
228	/// TODO working it out
229	pub(crate) fn from_property_constraint(
230		property_constraint: crate::types::logical::Logical<crate::PropertyValue>,
231		generics: GenericChain,
232		ctx: &impl InformationChain,
233		types: &TypeStore,
234		debug_mode: bool,
235	) -> TypeStringRepresentation {
236		match property_constraint {
237			crate::types::logical::Logical::Pure(constraint) => match constraint.inner_simple() {
238				crate::PropertyValue::Value(v) => {
239					let value =
240						print_type_with_type_arguments(*v, generics, types, ctx, debug_mode);
241					Self(value)
242				}
243				crate::PropertyValue::GetterAndSetter { .. }
244				| crate::PropertyValue::Getter(_)
245				| crate::PropertyValue::Setter(_) => Self("getter/setter".to_owned()),
246				crate::PropertyValue::Deleted => Self("never".to_owned()),
247				crate::PropertyValue::ConditionallyExists { .. }
248				| crate::PropertyValue::Configured { .. } => unreachable!(),
249			},
250			crate::types::logical::Logical::Or { condition, left, right } => {
251				let left_right = (*left, *right);
252				// if let (Ok(left), Ok(right)) = left_right {
253				// 	let mut left =
254				// 		Self::from_property_constraint(left, None, ctx, types, debug_mode);
255				// 	let right = Self::from_property_constraint(right, None, ctx, types, debug_mode);
256
257				// 	crate::utilities::notify!("Here?");
258				// 	left.0.push_str(" | ");
259				// 	left.0.push_str(&right.0);
260				// 	Self(left.0)
261				// } else {
262				crate::utilities::notify!("Printing {:?} base on {:?}", left_right, condition);
263				Self("TODO or".to_owned())
264				// }
265			}
266			crate::types::logical::Logical::Implies { on, antecedent } => {
267				if generics.is_some() {
268					crate::utilities::notify!("TODO chaining");
269				}
270				let generics = Some(GenericChainLink::PartiallyAppliedGenericArgumentsLink {
271					parent_link: None,
272					value: &antecedent,
273					from: TypeId::UNIMPLEMENTED_ERROR_TYPE,
274				});
275				Self::from_property_constraint(*on, generics, ctx, types, debug_mode)
276			}
277			crate::types::logical::Logical::BasedOnKey { .. } => {
278				Self("TODO based on key?".to_owned())
279			}
280		}
281	}
282}
283
284impl Display for TypeStringRepresentation {
285	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
286		f.write_str(&self.0)
287	}
288}
289
290pub(crate) struct NoEnvironmentSpecified;
291
292impl From<NoEnvironmentSpecified> for Diagnostic {
293	fn from(_error: NoEnvironmentSpecified) -> Self {
294		Diagnostic::Global { reason: "No environment".to_owned(), kind: DiagnosticKind::Error }
295	}
296}
297
298/// Reasons for errors, intermediate type for generating [Diagnostic]s
299/// e.g. cannot Call, cannot equate, duplicate key etc
300pub(crate) enum TypeCheckError<'a> {
301	FunctionCallingError(FunctionCallingError),
302	JSXCallingError(FunctionCallingError),
303	TemplateLiteralCallingError(FunctionCallingError),
304	GetterCallingError(FunctionCallingError),
305	SetterCallingError(FunctionCallingError),
306	/// From calling super
307	SuperCallError(FunctionCallingError),
308	/// When accessing
309	PropertyDoesNotExist {
310		on: TypeStringRepresentation,
311		property: PropertyKeyRepresentation,
312		position: SpanWithSource,
313		possibles: Vec<&'a str>,
314	},
315	/// When accessing
316	CyclicTypeAlias {
317		position: SpanWithSource,
318	},
319	#[allow(dead_code)]
320	NotInLoopOrCouldNotFindLabel(NotInLoopOrCouldNotFindLabel),
321	#[allow(dead_code)]
322	RestParameterAnnotationShouldBeArrayType(SpanWithSource),
323	CouldNotFindVariable {
324		variable: &'a str,
325		possibles: Vec<&'a str>,
326		position: SpanWithSource,
327	},
328	CouldNotFindType(&'a str, Vec<&'a str>, SpanWithSource),
329	/// For all `=`, including from declarations
330	AssignmentError(AssignmentError),
331	SetPropertyError(SetPropertyError),
332	ReturnedTypeDoesNotMatch {
333		expected_return_type: TypeStringRepresentation,
334		returned_type: TypeStringRepresentation,
335		/// Can be `None` if it is inferred parameters
336		annotation_position: SpanWithSource,
337		returned_position: SpanWithSource,
338	},
339	/// This could be a syntax error but that is difficult to type...
340	NonTopLevelExport(SpanWithSource),
341	FieldNotExported {
342		file: &'a str,
343		importing: &'a str,
344		position: SpanWithSource,
345		possibles: Vec<&'a str>,
346	},
347	/// For the `satisfies` keyword
348	NotSatisfied {
349		at: SpanWithSource,
350		expected: TypeStringRepresentation,
351		found: TypeStringRepresentation,
352	},
353	/// Catch type is not compatible with thrown type
354	CatchTypeDoesNotMatch {
355		at: SpanWithSource,
356		expected: TypeStringRepresentation,
357		found: TypeStringRepresentation,
358	},
359	/// Something the checker does not supported
360	Unsupported {
361		thing: &'static str,
362		at: SpanWithSource,
363	},
364	InvalidDefaultParameter {
365		at: SpanWithSource,
366		expected: TypeStringRepresentation,
367		found: TypeStringRepresentation,
368	},
369	/// TODO temp, needs more info
370	#[allow(dead_code)]
371	FunctionDoesNotMeetConstraint {
372		function_constraint: TypeStringRepresentation,
373		function_type: TypeStringRepresentation,
374		position: SpanWithSource,
375	},
376	CannotRedeclareVariable {
377		name: String,
378		position: SpanWithSource,
379	},
380	/// This is for structure generics (type annotations)
381	GenericArgumentDoesNotMeetRestriction {
382		parameter_restriction: TypeStringRepresentation,
383		argument: TypeStringRepresentation,
384		position: SpanWithSource,
385	},
386	/// This is for structure generics (type annotations)
387	GenericArgumentCountMismatch {
388		expected_count: usize,
389		count: usize,
390		position: SpanWithSource,
391	},
392	#[allow(dead_code)]
393	NotTopLevelImport(SpanWithSource),
394	DuplicateImportName {
395		import_position: SpanWithSource,
396		existing_position: SpanWithSource,
397	},
398	#[allow(dead_code)]
399	DoubleDefaultExport(SpanWithSource),
400	NoDefaultExport {
401		position: SpanWithSource,
402		partial_import_path: &'a str,
403	},
404	CannotOpenFile {
405		file: CouldNotOpenFile,
406		import_position: Option<SpanWithSource>,
407		possibles: Vec<&'a str>,
408		partial_import_path: &'a str,
409	},
410	/// WIP
411	#[allow(dead_code)]
412	VariableNotDefinedInContext {
413		variable: &'a str,
414		expected_context: &'a str,
415		current_context: String,
416		position: SpanWithSource,
417	},
418	TypeNeedsTypeArguments(&'a str, SpanWithSource),
419	TypeAlreadyDeclared {
420		name: String,
421		position: SpanWithSource,
422	},
423	#[allow(clippy::upper_case_acronyms)]
424	VariableUsedInTDZ(VariableUsedInTDZ),
425	InvalidMathematicalOrBitwiseOperation {
426		operator: crate::features::operations::MathematicalOrBitwiseOperation,
427		lhs: TypeStringRepresentation,
428		rhs: TypeStringRepresentation,
429		position: SpanWithSource,
430	},
431	// Only for `<` `>` etc
432	InvalidEqualityOperation {
433		operator: crate::features::operations::EqualityAndInequality,
434		lhs: TypeStringRepresentation,
435		rhs: TypeStringRepresentation,
436		position: SpanWithSource,
437	},
438	InvalidUnaryOperation {
439		operator: crate::features::operations::UnaryOperation,
440		operand: TypeStringRepresentation,
441		position: SpanWithSource,
442	},
443	#[allow(dead_code)]
444	InvalidCast {
445		position: SpanWithSource,
446		from: TypeStringRepresentation,
447		to: TypeStringRepresentation,
448	},
449	/// TODO Position = Function body position. Could it be better
450	/// TODO maybe warning?
451	#[allow(dead_code)]
452	UnreachableVariableClosedOver(String, SpanWithSource),
453	IncompatibleOverloadParameter {
454		parameter_position: SpanWithSource,
455		overloaded_parameter_position: SpanWithSource,
456		parameter: TypeStringRepresentation,
457		overloaded_parameter: TypeStringRepresentation,
458	},
459	IncompatibleOverloadReturnType {
460		base_position: SpanWithSource,
461		overload_position: SpanWithSource,
462		base: TypeStringRepresentation,
463		overload: TypeStringRepresentation,
464	},
465	FunctionWithoutBodyNotAllowedHere {
466		position: SpanWithSource,
467	},
468	CannotDeleteProperty(CannotDeleteFromError),
469	InvalidRegExp(InvalidRegExp),
470}
471
472#[allow(clippy::useless_format)]
473#[must_use]
474pub fn get_possibles_message(possibles: &[&str]) -> String {
475	match possibles {
476		[] => format!(""),
477		[a] => format!("Did you mean '{a}'?"),
478		[a @ .., b] => {
479			let mut iter = a.iter();
480			let first = format!("'{first}'", first = iter.next().unwrap());
481			format!(
482				"Did you mean {items} or '{b}'?",
483				items = iter.fold(first, |acc, item| format!("{acc}, '{item}'"))
484			)
485		}
486	}
487}
488
489fn map_error_empty<U, T: Default>(n: Vec<U>, cb: impl FnOnce(Vec<U>) -> T) -> T {
490	if n.is_empty() {
491		<T as Default>::default()
492	} else {
493		cb(n)
494	}
495}
496
497impl From<TypeCheckError<'_>> for Diagnostic {
498	fn from(error: TypeCheckError<'_>) -> Self {
499		let kind = super::DiagnosticKind::Error;
500		match error {
501			TypeCheckError::CouldNotFindVariable { variable, possibles, position } => {
502				Diagnostic::PositionWithAdditionalLabels {
503					reason: format!("Could not find variable '{variable}' in scope"),
504					labels: map_error_empty(possibles, |possibles| vec![(
505						get_possibles_message(&possibles),
506						position,
507					)]),
508					position,
509					kind,
510				}
511			}
512			TypeCheckError::CouldNotFindType(reference, possibles, position) => Diagnostic::PositionWithAdditionalLabels {
513				reason: format!("Could not find type '{reference}'"),
514				position,
515				labels: map_error_empty(possibles, |possibles| vec![(
516					get_possibles_message(&possibles),
517					position,
518				)]),
519				kind,
520			},
521			TypeCheckError::PropertyDoesNotExist { property, on, position, possibles } => {
522				Diagnostic::PositionWithAdditionalLabels {
523					reason: match property {
524						PropertyKeyRepresentation::Type(ty) => format!("No property of type {ty} on {on}"),
525						PropertyKeyRepresentation::StringKey(property) => format!("No property '{property}' on {on}"),
526					},
527					position,
528					labels: map_error_empty(possibles, |possibles| vec![(
529						get_possibles_message(&possibles),
530						position,
531					)]),
532					kind,
533				}
534			}
535			TypeCheckError::FunctionCallingError(error) => function_calling_error_diagnostic(error, kind, ""),
536			TypeCheckError::JSXCallingError(error) => function_calling_error_diagnostic(error, kind, " (in JSX)"),
537			TypeCheckError::GetterCallingError(error) => function_calling_error_diagnostic(error, kind, " (in getter)"),
538			TypeCheckError::SetterCallingError(error) => function_calling_error_diagnostic(error, kind, " (in setter)"),
539			TypeCheckError::TemplateLiteralCallingError(error) => {
540				function_calling_error_diagnostic(error, kind, " (in template literal)")
541			},
542			TypeCheckError::SuperCallError(error) => function_calling_error_diagnostic(error, kind, " (in super call)"),
543			TypeCheckError::AssignmentError(error) => match error {
544				AssignmentError::DoesNotMeetConstraint {
545					variable_type,
546					variable_position,
547					value_type,
548					value_position,
549				} => Diagnostic::PositionWithAdditionalLabels {
550					reason: format!(
551						"Type {value_type} is not assignable to type {variable_type}",
552					),
553					position: value_position,
554					labels: vec![(
555						format!("Variable declared with type {variable_type}"),
556						variable_position,
557					)],
558					kind,
559				},
560				AssignmentError::PropertyConstraint {
561					property_constraint: property_type,
562					value_type,
563					assignment_position,
564				} => Diagnostic::Position {
565					reason: format!(
566						"Type {value_type} does not meet property constraint {property_type}"
567					),
568					position: assignment_position,
569					kind,
570				},
571				AssignmentError::Constant(position) => Diagnostic::Position {
572					reason: "Cannot assign to constant".into(),
573					position,
574					kind,
575				},
576				AssignmentError::VariableNotFound { variable, assignment_position } => {
577					Diagnostic::Position {
578						reason: format!("Cannot assign to unknown variable '{variable}'"),
579						position: assignment_position,
580						kind,
581					}
582				}
583				AssignmentError::VariableUsedInTDZ(VariableUsedInTDZ { variable_name, position }) => {
584					Diagnostic::Position {
585						reason: format!("Cannot assign to '{variable_name}' before declaration"),
586						position,
587						kind,
588					}
589				}
590			},
591			TypeCheckError::ReturnedTypeDoesNotMatch {
592				annotation_position,
593				returned_position,
594				expected_return_type,
595				returned_type,
596			} => Diagnostic::PositionWithAdditionalLabels {
597				reason: format!(
598					"Cannot return {returned_type} because the function is expected to return {expected_return_type}"
599				),
600				labels: vec![(
601					format!("Function annotated to return {expected_return_type} here"),
602					annotation_position,
603				)],
604				position: returned_position,
605				kind,
606			},
607			TypeCheckError::InvalidDefaultParameter {
608				expected,
609				found,
610				at,
611			} => Diagnostic::Position {
612				reason: format!( "Cannot use a default value of type {found} for parameter of type {expected}"),
613				position: at,
614				kind,
615			},
616			TypeCheckError::CatchTypeDoesNotMatch {
617				expected,
618				found,
619				at,
620			} => Diagnostic::Position {
621				reason: format!( "Cannot catch type {found} because the try block throws {expected}" ),
622				position: at,
623				kind,
624			},
625			TypeCheckError::NonTopLevelExport(position) => Diagnostic::Position {
626				reason: "Cannot export at not top level".to_owned(),
627				position,
628				kind,
629			},
630			TypeCheckError::FieldNotExported { file, importing, position, possibles } => {
631				Diagnostic::PositionWithAdditionalLabels {
632					reason: format!("{importing} not exported from {file}"),
633					position,
634					kind,
635					labels: map_error_empty(possibles, |possibles| vec![(
636						get_possibles_message(&possibles),
637						position,
638					)]),
639				}
640			}
641			TypeCheckError::RestParameterAnnotationShouldBeArrayType(pos) => {
642				Diagnostic::Position {
643					reason: "Rest parameter annotation should be array type".to_owned(),
644					position: pos,
645					kind,
646				}
647			}
648			TypeCheckError::Unsupported { thing, at } => Diagnostic::Position {
649				reason: format!("Unsupported: {thing}"),
650				position: at,
651				kind,
652			},
653			TypeCheckError::FunctionDoesNotMeetConstraint {
654				function_constraint,
655				function_type,
656				position,
657			} => Diagnostic::Position {
658				reason: format!(
659					"{function_constraint} constraint on function does not match synthesised form {function_type}",
660				),
661				position,
662				kind,
663			},
664			TypeCheckError::NotSatisfied { at, expected, found } => Diagnostic::Position {
665				reason: format!("Expected {expected}, found {found}"),
666				position: at,
667				kind,
668			},
669			TypeCheckError::CannotRedeclareVariable { name, position } => {
670				Diagnostic::Position {
671					reason: format!("Cannot redeclare variable '{name}'"),
672					position,
673					kind,
674				}
675			}
676			TypeCheckError::GenericArgumentDoesNotMeetRestriction {
677				argument,
678				parameter_restriction,
679				position,
680			} => Diagnostic::Position {
681				reason: format!(
682					"Generic argument {argument} does not match {parameter_restriction}"
683				),
684				position,
685				kind,
686			},
687			TypeCheckError::GenericArgumentCountMismatch {
688				count,
689				expected_count,
690				position,
691			} => {
692				let reason = if expected_count == 0 {
693					"Cannot pass a type argument to a non-generic type".to_owned()
694				} else if expected_count == 1 {
695					format!("Expected 1 type argument, but got {count}")
696				} else {
697					format!("Expected {expected_count} type arguments, but got {count}")
698				};
699				Diagnostic::Position {
700					position,
701					kind,
702					reason
703				}
704			},
705			TypeCheckError::NotTopLevelImport(position) => Diagnostic::Position {
706				reason: "Import must be in the top of the scope".to_owned(),
707				position,
708				kind,
709			},
710			TypeCheckError::DoubleDefaultExport(position) => Diagnostic::Position {
711				reason: "Cannot have more than one default export".to_owned(),
712				position,
713				kind,
714			},
715			TypeCheckError::DuplicateImportName { import_position: position, existing_position, ..} => Diagnostic::PositionWithAdditionalLabels {
716				reason: "Cannot import using conflicting name".to_string(),
717				position,
718				kind,
719				labels: vec![("Existing import with same name".to_string(), existing_position)],
720			},
721			TypeCheckError::NoDefaultExport { partial_import_path, position, ..} => Diagnostic::Position {
722				reason: format!("Cannot find default export from module '{partial_import_path}'"),
723				position,
724				kind
725			},
726			TypeCheckError::CannotOpenFile { file, import_position, possibles, partial_import_path } => if let Some(import_position) = import_position {
727				Diagnostic::PositionWithAdditionalLabels {
728					reason: format!("Cannot find {partial_import_path}"),
729					position: import_position,
730					kind,
731					labels: map_error_empty(possibles, |possibles| vec![(
732						get_possibles_message(&possibles),
733						import_position,
734					)])
735				}
736			} else {
737				Diagnostic::Global { reason: format!("Cannot find file {}", file.0.display()), kind }
738			},
739			TypeCheckError::VariableNotDefinedInContext {
740				variable,
741				expected_context,
742				current_context,
743				position,
744			} => Diagnostic::Position {
745				reason: format!("'{variable}' is only available on the {expected_context}, currently in {current_context}"),
746				position,
747				kind,
748			},
749			TypeCheckError::TypeNeedsTypeArguments(ty, position) => Diagnostic::Position {
750				reason: format!("Type {ty} requires type arguments"),
751				position,
752				kind,
753			},
754			TypeCheckError::TypeAlreadyDeclared { name, position } => Diagnostic::Position {
755				reason: format!("Type named '{name}' already declared"),
756				position,
757				kind,
758			},
759			TypeCheckError::VariableUsedInTDZ(VariableUsedInTDZ { position, variable_name }) => Diagnostic::Position {
760				reason: format!("Variable '{variable_name}' used before declaration"),
761				position,
762				kind,
763			},
764			TypeCheckError::InvalidMathematicalOrBitwiseOperation { operator, lhs, rhs, position } => Diagnostic::Position {
765				// TODO temp
766				reason: format!("Cannot {lhs} {operator:?} {rhs}"),
767				position,
768				kind,
769			},
770			TypeCheckError::InvalidUnaryOperation {
771				operator,
772				operand,
773				position,
774			} => {
775				Diagnostic::Position {
776					// TODO temp
777					reason: format!("Cannot {operator:?} {operand}"),
778					position,
779					kind,
780				}
781			},
782			TypeCheckError::InvalidEqualityOperation { operator, lhs, rhs, position } => Diagnostic::Position {
783				// TODO temp
784				reason: format!("Cannot {lhs} {operator:?} {rhs}"),
785				position,
786				kind,
787			},
788			TypeCheckError::NotInLoopOrCouldNotFindLabel(NotInLoopOrCouldNotFindLabel {
789				label: _,
790				position,
791			}) => {
792				Diagnostic::Position {
793					// TODO temp
794					reason: "Cannot use `break` or `continue` here or could not find label".to_owned(),
795					position,
796					kind,
797				}
798			}
799			TypeCheckError::InvalidCast { position, from, to } => {
800				Diagnostic::Position {
801					reason: format!("Cannot cast {from} to {to}"),
802					position,
803					kind,
804				}
805			},
806			TypeCheckError::UnreachableVariableClosedOver(name, function_position) => {
807				Diagnostic::Position {
808					reason: format!("Function contains unreachable closed over variable '{name}'"),
809					position: function_position,
810					kind,
811				}
812			},
813			TypeCheckError::IncompatibleOverloadParameter { parameter_position, overloaded_parameter_position, parameter, overloaded_parameter } => Diagnostic::PositionWithAdditionalLabels {
814				reason: format!(
815					"Overload with parameter of {overloaded_parameter} does not meet base parameter {parameter}"
816				),
817				labels: vec![(
818					format!("Function has base type {parameter} here"),
819					parameter_position,
820				)],
821				position: overloaded_parameter_position,
822				kind,
823			},
824			TypeCheckError::IncompatibleOverloadReturnType { base_position, overload_position, base, overload } => Diagnostic::PositionWithAdditionalLabels {
825				reason: format!(
826					"Cannot return {overload} in overload because base function is expected to return {base}"
827				),
828				labels: vec![(
829					format!("Function annotated to return {base} here"),
830					base_position,
831				)],
832				position: overload_position,
833				kind,
834			},
835			TypeCheckError::FunctionWithoutBodyNotAllowedHere { position } => {
836				Diagnostic::Position {
837					reason: "Function without body not allowed here".to_owned(),
838					position,
839					kind,
840				}
841			}
842			TypeCheckError::CyclicTypeAlias { position } => {
843				Diagnostic::Position {
844					reason: "Circular type reference".to_owned(),
845					position,
846					kind,
847				}
848			}
849			TypeCheckError::CannotDeleteProperty(CannotDeleteFromError::Constraint { constraint, position }) => {
850				Diagnostic::Position {
851					reason: format!("Cannot delete from object constrained to {constraint}"),
852					position,
853					kind,
854				}
855			}
856			TypeCheckError::CannotDeleteProperty(CannotDeleteFromError::NonConfigurable {
857				position,
858			}) => {
859				Diagnostic::Position {
860					reason: "Cannot delete from non-configurable property".to_owned(),
861					position,
862					kind,
863				}
864			}
865			TypeCheckError::SetPropertyError(error) => match error {
866				SetPropertyError::NotWriteable {
867					property,
868					position,
869				} => Diagnostic::Position {
870					reason: match property {
871						PropertyKeyRepresentation::Type(ty) => format!("Cannot write to property of type {ty}"),
872						PropertyKeyRepresentation::StringKey(property) => format!("Cannot write to property '{property}'")
873					},
874					position,
875					kind,
876				},
877				SetPropertyError::DoesNotMeetConstraint {
878					property_constraint,
879					value_type,
880					reason: _,
881					position,
882				} => Diagnostic::Position {
883					reason: format!(
884						"Type {value_type} does not meet property constraint {property_constraint}"
885					),
886					position,
887					kind,
888				},
889				SetPropertyError::AssigningToGetter {
890					property,
891					position,
892				} => Diagnostic::Position {
893					reason: match property {
894						PropertyKeyRepresentation::Type(ty) => format!("Cannot write to property of type {ty} as it is a getter"),
895						PropertyKeyRepresentation::StringKey(property) => format!("Cannot write to property '{property}' as it is a getter")
896					},
897					position,
898					kind,
899				},
900				SetPropertyError::AssigningToNonExistent {
901					property,
902					position,
903				} => Diagnostic::Position {
904					reason: match property {
905						PropertyKeyRepresentation::Type(ty) => format!("Cannot write to non-existent property of type {ty}"),
906						PropertyKeyRepresentation::StringKey(property) => format!("Cannot write to non-existent property '{property}'")
907					},
908					position,
909					kind,
910				}
911			},
912			TypeCheckError::InvalidRegExp(InvalidRegExp { error, position }) => Diagnostic::Position {
913				reason: format!("Invalid regular expression: {error}"),
914				position,
915				kind,
916			},
917		}
918	}
919}
920
921pub enum TypeCheckWarning {
922	AwaitUsedOnNonPromise(SpanWithSource),
923	/// TODO could be an error at some point
924	DeadBranch {
925		expression_span: SpanWithSource,
926		expression_value: bool,
927	},
928	ExcessProperty {
929		position: SpanWithSource,
930		expected_type: TypeStringRepresentation,
931		excess_property_name: String,
932	},
933	IgnoringAsExpression(SpanWithSource),
934	Unimplemented {
935		item: &'static str,
936		position: SpanWithSource,
937	},
938	UselessExpression {
939		expression_span: SpanWithSource,
940		// TODO other branch information
941	},
942	MergingInterfaceInSameContext {
943		position: SpanWithSource,
944	},
945	TypesDoNotIntersect {
946		left: TypeStringRepresentation,
947		right: TypeStringRepresentation,
948		position: SpanWithSource,
949	},
950	InvalidOrUnimplementedDefinitionFileItem(SpanWithSource),
951	/// TODO WIP
952	ConditionalExceptionInvoked {
953		value: TypeStringRepresentation,
954		/// Should be set
955		call_site: SpanWithSource,
956	},
957	Unreachable(SpanWithSource),
958	DisjointEquality {
959		lhs: TypeStringRepresentation,
960		rhs: TypeStringRepresentation,
961		result: bool,
962		position: SpanWithSource,
963	},
964	ItemMustBeUsedWithFlag {
965		item: &'static str,
966		position: SpanWithSource,
967	},
968}
969
970impl From<TypeCheckWarning> for Diagnostic {
971	fn from(warning: TypeCheckWarning) -> Self {
972		let kind = super::DiagnosticKind::Warning;
973
974		match warning {
975			TypeCheckWarning::AwaitUsedOnNonPromise(position) => Diagnostic::Position {
976				reason: "Unnecessary await expression / type is not promise".to_owned(),
977				position,
978				kind,
979			},
980			TypeCheckWarning::DeadBranch { expression_span, expression_value } => {
981				Diagnostic::Position {
982					reason: format!("Expression is always {expression_value:?}"),
983					position: expression_span,
984					kind,
985				}
986			}
987			TypeCheckWarning::ExcessProperty { position, expected_type, excess_property_name } => {
988				Diagnostic::Position {
989					reason: format!(
990						"'{excess_property_name}' is not a property of {expected_type}"
991					),
992					position,
993					kind,
994				}
995			}
996			TypeCheckWarning::IgnoringAsExpression(position) => Diagnostic::Position {
997				reason: "'as' expressions are ignore by the checker".to_owned(),
998				position,
999				kind,
1000			},
1001			TypeCheckWarning::Unimplemented { item, position } => {
1002				Diagnostic::Position { reason: format!("Unsupported: {item}"), position, kind }
1003			}
1004			TypeCheckWarning::ItemMustBeUsedWithFlag { item, position } => Diagnostic::Position {
1005				reason: format!("{item} must be used with 'extras' option"),
1006				position,
1007				kind,
1008			},
1009			TypeCheckWarning::UselessExpression { expression_span } => Diagnostic::Position {
1010				reason: "Expression is always true".to_owned(),
1011				position: expression_span,
1012				kind,
1013			},
1014			TypeCheckWarning::MergingInterfaceInSameContext { position } => Diagnostic::Position {
1015				reason: "Merging interfaces in the same context".to_owned(),
1016				position,
1017				kind,
1018			},
1019			TypeCheckWarning::TypesDoNotIntersect { left, right, position } => {
1020				Diagnostic::Position {
1021					reason: format!("No intersection between types {left} and {right}"),
1022					position,
1023					kind,
1024				}
1025			}
1026			TypeCheckWarning::InvalidOrUnimplementedDefinitionFileItem(position) => {
1027				Diagnostic::Position {
1028					reason: "Invalid (or unimplemented) item in definition file skipped".to_owned(),
1029					position,
1030					kind,
1031				}
1032			}
1033			TypeCheckWarning::Unreachable(position) => {
1034				Diagnostic::Position { reason: "Unreachable statement".to_owned(), position, kind }
1035			}
1036			TypeCheckWarning::ConditionalExceptionInvoked { value, call_site } => {
1037				Diagnostic::Position {
1038					reason: format!("Conditional '{value}' was thrown in function"),
1039					position: call_site,
1040					kind,
1041				}
1042			}
1043			TypeCheckWarning::DisjointEquality { lhs, rhs, position, result } => {
1044				Diagnostic::Position {
1045					reason: format!(
1046						"This equality is always {result} as {lhs} and {rhs} have no overlap"
1047					),
1048					position,
1049					kind,
1050				}
1051			}
1052		}
1053	}
1054}
1055
1056/// Only for internal things
1057///
1058/// WIP
1059pub struct InfoDiagnostic(pub String, pub SpanWithSource);
1060
1061#[derive(Debug)]
1062pub struct CannotFindTypeError<'a>(pub &'a str);
1063
1064#[derive(Debug)]
1065pub struct TypeIsNotIndexable<'a>(pub &'a TypeId);
1066
1067pub(crate) struct TypeDefinitionModuleNotFound(pub PathBuf);
1068
1069impl From<TypeDefinitionModuleNotFound> for Diagnostic {
1070	fn from(val: TypeDefinitionModuleNotFound) -> Self {
1071		Diagnostic::Global {
1072			reason: format!(
1073				"Could not find type definition module at '{}'",
1074				val.0.as_path().display()
1075			),
1076			kind: DiagnosticKind::Error,
1077		}
1078	}
1079}
1080
1081pub(crate) struct EntryPointNotFound(pub PathBuf);
1082
1083impl From<EntryPointNotFound> for Diagnostic {
1084	fn from(val: EntryPointNotFound) -> Self {
1085		Diagnostic::Global {
1086			reason: format!("Could not entry point module at '{}'", val.0.as_path().display()),
1087			kind: DiagnosticKind::Error,
1088		}
1089	}
1090}
1091
1092#[derive(Debug)]
1093pub struct CannotRedeclareVariable<'a> {
1094	pub name: &'a str,
1095}
1096
1097/// `context` is the what kind of a function call the error happened in. (for example tagged template literals or JSX)
1098fn function_calling_error_diagnostic(
1099	error: FunctionCallingError,
1100	kind: crate::DiagnosticKind,
1101	context: &str,
1102) -> Diagnostic {
1103	match error {
1104		FunctionCallingError::InvalidArgumentType {
1105			parameter_type,
1106			argument_type,
1107			argument_position,
1108			parameter_position,
1109			restriction,
1110		} => {
1111			if let Some((restriction_pos, restriction)) = restriction {
1112				Diagnostic::PositionWithAdditionalLabels {
1113					reason: format!("Argument of type {argument_type} is not assignable to parameter of type {restriction}{context}"),
1114					position: argument_position,
1115					labels: vec![(
1116						format!("{parameter_type} was specialised with type {restriction}"),
1117						restriction_pos,
1118					)],
1119					kind,
1120				}
1121			} else {
1122				Diagnostic::PositionWithAdditionalLabels {
1123					reason: format!( "Argument of type {argument_type} is not assignable to parameter of type {parameter_type}{context}"),
1124					position: argument_position,
1125					labels: vec![(
1126						format!("Parameter has type {parameter_type}"),
1127						parameter_position,
1128					)],
1129					kind,
1130				}
1131			}
1132		}
1133		FunctionCallingError::MissingArgument { parameter_position, call_site } => {
1134			Diagnostic::PositionWithAdditionalLabels {
1135				reason: format!("Missing argument{context}"),
1136				position: call_site,
1137				kind,
1138				labels: vec![(
1139					"(non-optional) Parameter declared here".into(),
1140					parameter_position,
1141				)],
1142			}
1143		}
1144		FunctionCallingError::ExcessArguments { count: _, position } => {
1145			Diagnostic::Position { reason: format!("Excess argument{context}"), position, kind }
1146		}
1147		FunctionCallingError::ExcessTypeArguments { expected_count, count, position } => {
1148			let reason = if expected_count == 0 {
1149				format!("Cannot pass a type argument to a non-generic function{context}")
1150			} else if expected_count == 1 {
1151				format!("Expected 1 type argument, but got {count}{context}")
1152			} else {
1153				format!("Expected {expected_count} type arguments, but got {count}{context}")
1154			};
1155			Diagnostic::Position {
1156				position,
1157				kind,
1158				reason
1159			}
1160		}
1161		FunctionCallingError::NotCallable { calling, call_site } => Diagnostic::Position {
1162			reason: format!("Cannot call type {calling}{context}"),
1163			position: call_site,
1164			kind,
1165		},
1166		FunctionCallingError::ReferenceRestrictionDoesNotMatch {
1167			reference: _,
1168			requirement: _,
1169			found: _,
1170		} => todo!(),
1171		// Diagnostic::Position {
1172		// 	reason: format!(
1173		// 		"Calling function requires {} to be {}, found {}",
1174		// 		identifier, requirement, found
1175		// 	),
1176		// 	position: call_site,
1177		// 	kind,
1178		// },
1179		FunctionCallingError::CyclicRecursion(_, call_site) => Diagnostic::Position {
1180			reason: format!("Encountered recursion{context}"),
1181			position: call_site,
1182			kind,
1183		},
1184		FunctionCallingError::NoLogicForIdentifier(name, position) => Diagnostic::Position {
1185			reason: format!("No logic for constant function {name}{context}"),
1186			kind,
1187			position,
1188		},
1189		FunctionCallingError::NeedsToBeCalledWithNewKeyword(position) => Diagnostic::Position {
1190			reason: "Class constructor must be called with new".to_owned(),
1191			kind,
1192			position,
1193		},
1194		FunctionCallingError::VariableUsedInTDZ { error: VariableUsedInTDZ { position, variable_name }, call_site } => {
1195			Diagnostic::PositionWithAdditionalLabels {
1196				reason: format!("Variable '{variable_name}' used before declaration{context}"),
1197				position: call_site,
1198				kind,
1199				labels: vec![("Variable referenced here".to_owned(), position)],
1200			}
1201		}
1202		FunctionCallingError::SetPropertyConstraint {
1203			property_type,
1204			value_type,
1205			assignment_position,
1206			call_site,
1207		} => Diagnostic::PositionWithAdditionalLabels {
1208			reason: format!("Invalid assignment through parameter{context}"),
1209			position: call_site,
1210			kind,
1211			labels: vec![(
1212				format!("Type {value_type} does not meet property constraint {property_type}"),
1213				assignment_position,
1214			)],
1215		},
1216		FunctionCallingError::MismatchedThis { call_site, expected, found } => {
1217			Diagnostic::Position {
1218				reason: format!("The 'this' context of the function is expected to be {expected}, found {found}{context}"),
1219				position: call_site,
1220				kind,
1221			}
1222		}
1223		FunctionCallingError::CannotCatch { catch, thrown, thrown_position } => {
1224			Diagnostic::Position {
1225				reason: format!("Cannot throw {thrown} in block that expects {catch}{context}"),
1226				position: thrown_position,
1227				kind,
1228			}
1229		}
1230		FunctionCallingError::DeleteConstraint { constraint, delete_position, call_site: _ } => {
1231			Diagnostic::Position {
1232				reason: format!("Cannot delete from object constrained to {constraint}"),
1233				position: delete_position,
1234				kind,
1235			}
1236		}
1237		FunctionCallingError::NotConfigurable {
1238			property,
1239			call_site,
1240		} => {
1241			Diagnostic::Position {
1242				reason: match property {
1243					PropertyKeyRepresentation::Type(ty) => format!("Property of type '{ty}' not configurable"),
1244					PropertyKeyRepresentation::StringKey(property) => format!("Property '{property}' not configurable"),
1245				},
1246				position: call_site,
1247				kind,
1248			}
1249		}
1250		FunctionCallingError::InvalidRegExp(InvalidRegExp { error, position }) => Diagnostic::Position {
1251			reason: format!("Invalid regular expression: {error}"),
1252			position,
1253			kind,
1254		}
1255	}
1256}