nickel_lang_core/parser/error.rs
1use codespan_reporting::diagnostic::Label;
2
3use crate::{
4 files::FileId,
5 identifier::{Ident, LocIdent},
6 position::RawSpan,
7};
8use std::ops::Range;
9
10#[derive(Clone, PartialEq, Eq, Debug)]
11pub enum LexicalError {
12 /// A closing brace '}' does not match an opening brace '{'.
13 UnmatchedCloseBrace(usize),
14 /// Invalid escape sequence in a string literal.
15 InvalidEscapeSequence(usize),
16 /// Invalid escape ASCII code in a string literal.
17 InvalidAsciiEscapeCode(usize),
18 /// A multiline string was closed with a delimiter which has a `%` count higher than the
19 /// opening delimiter.
20 StringDelimiterMismatch {
21 opening_delimiter: Range<usize>,
22 closing_delimiter: Range<usize>,
23 },
24 /// Generic lexer error
25 Generic(Range<usize>),
26}
27
28/// Error indicating that a construct is not allowed when trying to interpret a `UniRecord` as a
29/// record type in a strict way.
30///
31/// See `parser::uniterm::UniRecord::into_type_strict`.
32#[derive(Debug, Copy, Clone, PartialEq, Eq)]
33pub enum InvalidRecordTypeError {
34 /// The record type had an invalid field, for example because it had a contract,
35 /// an assigned value, or lacked a type annotation.
36 InvalidField(RawSpan),
37 /// The record had an ellipsis.
38 IsOpen(RawSpan),
39 /// The record has `include` statements.
40 HasInclude(RawSpan),
41 /// The record type had a field whose name used string interpolation.
42 InterpolatedField(RawSpan),
43 /// A field name was repeated.
44 RepeatedField { orig: RawSpan, dup: RawSpan },
45}
46
47impl InvalidRecordTypeError {
48 pub fn labels(&self) -> Vec<Label<FileId>> {
49 let label = |span: &RawSpan| {
50 Label::secondary(span.src_id, span.start.to_usize()..span.end.to_usize())
51 };
52 match self {
53 InvalidRecordTypeError::InvalidField(pos) => {
54 vec![label(pos).with_message("invalid field for a record type literal")]
55 }
56 InvalidRecordTypeError::IsOpen(pos) => {
57 vec![label(pos).with_message("cannot have ellipsis in a record type literal")]
58 }
59 InvalidRecordTypeError::HasInclude(pos) => {
60 vec![label(pos).with_message("cannot have `include` statements in a record type")]
61 }
62 InvalidRecordTypeError::InterpolatedField(pos) => {
63 vec![label(pos).with_message("this field uses interpolation")]
64 }
65 InvalidRecordTypeError::RepeatedField { orig, dup } => {
66 vec![
67 label(orig).with_message("first occurrence"),
68 label(dup).with_message("second occurrence"),
69 ]
70 }
71 }
72 }
73
74 pub fn notes(&self) -> Option<String> {
75 match self {
76 InvalidRecordTypeError::InvalidField(_) => Some(
77 "Value assignments such as `<field> = <expr>`, and metadata \
78 annotation (annotation, documentation, etc.) are forbidden."
79 .into(),
80 ),
81 InvalidRecordTypeError::InterpolatedField(_) => {
82 Some("String interpolation in field names is forbidden in record types".into())
83 }
84 InvalidRecordTypeError::RepeatedField { .. } => {
85 Some("Repeated field names are forbidden".into())
86 }
87 _ => None,
88 }
89 }
90}
91
92#[derive(Clone, PartialEq, Eq, Debug)]
93pub enum ParseError {
94 /// A specific lexical error
95 Lexical(LexicalError),
96 /// Unbound type variable(s)
97 UnboundTypeVariables(Vec<LocIdent>),
98 /// Illegal record type literal.
99 ///
100 /// This occurs when failing to convert from the uniterm syntax to a record type literal.
101 /// See [RFC002](../../rfcs/002-merge-types-terms-syntax.md) for more details.
102 InvalidRecordType {
103 record_span: RawSpan,
104 tail_span: Option<RawSpan>,
105 cause: InvalidRecordTypeError,
106 },
107 /// A recursive let pattern was encountered. They are not currently supported because we
108 /// decided it was too involved to implement them.
109 RecursiveLetPattern(RawSpan),
110 /// Let blocks can currently only contain plain bindings, not pattern bindings.
111 PatternInLetBlock(RawSpan),
112 /// A duplicate binding was encountered in a record destructuring pattern.
113 DuplicateIdentInRecordPattern {
114 /// The duplicate identifier.
115 ident: LocIdent,
116 /// The previous instance of the duplicated identifier.
117 prev_ident: LocIdent,
118 },
119 /// A duplicate binding was encountered in a let block.
120 DuplicateIdentInLetBlock {
121 /// The duplicate identifier.
122 ident: LocIdent,
123 /// The previous instance of the duplicated identifier.
124 prev_ident: LocIdent,
125 },
126 /// A type variable is used in ways that imply it has multiple different kinds.
127 ///
128 /// This can happen in several situations, for example:
129 /// - a variable is used as both a type variable and a row type variable,
130 /// e.g. in the signature `forall r. { ; r } -> r`,
131 /// - a variable is used as both a record and enum row variable, e.g. in the
132 /// signature `forall r. [| ; r |] -> { ; r }`.
133 TypeVariableKindMismatch { ty_var: LocIdent, span: RawSpan },
134 /// A record literal, which isn't a record type, has a field with a type annotation but without
135 /// a definition. While we could technically handle this situation, this is most probably an
136 /// error from the user, because this type annotation is useless and, maybe non-intuitively,
137 /// won't have any effect as part of a larger contract:
138 ///
139 /// ```nickel
140 /// let MixedContract = {foo : String, bar | Number} in
141 /// { foo = 1, bar = 2} | MixedContract
142 /// ```
143 ///
144 /// This example works, because the `foo : String` annotation doesn't propagate, and contract
145 /// application is mostly merging, which is probably not the intent. It might become a warning
146 /// in a future version, but we don't have warnings for now, so we rather forbid such
147 /// constructions.
148 TypedFieldWithoutDefinition {
149 /// The position of the field definition (the identifier only).
150 field_span: RawSpan,
151 /// The position of the type annotation.
152 annot_span: RawSpan,
153 },
154 /// The user provided a field path on the CLI, which is expected to be only composed of
155 /// literals, but the parsed field path contains string interpolation.
156 InterpolationInStaticPath { path_elem_span: RawSpan },
157 /// There was an attempt to use a feature that hasn't been enabled.
158 DisabledFeature { feature: String, span: RawSpan },
159 /// A term was used as a contract in type position, but this term has no chance to make any
160 /// sense as a contract. What terms make sense might evolve with time, but any given point in
161 /// time, there are a set of expressions that can be excluded syntactically. Currently, it's
162 /// mostly constants.
163 InvalidContract(RawSpan),
164 /// Unrecognized explicit import format tag
165 InvalidImportFormat { span: RawSpan },
166 /// An included field has several definitions. While we could just merge both at runtime like a
167 /// piecewise field definition, we entirely forbid this situation for now.
168 MultipleFieldDecls {
169 /// The identifier.
170 ident: Ident,
171 /// The identifier and the position of the include expression. The ident part is the same
172 /// as the ident part of `ident`.
173 include_span: RawSpan,
174 /// The span of the other declaration, which can be either a field
175 /// definition or an include expression as well.
176 other_span: RawSpan,
177 },
178}