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}