pub_just/
compile_error.rs

1use super::*;
2
3#[derive(Debug, PartialEq)]
4pub struct CompileError<'src> {
5  pub token: Token<'src>,
6  pub kind: Box<CompileErrorKind<'src>>,
7}
8
9impl<'src> CompileError<'src> {
10  pub fn context(&self) -> Token<'src> {
11    self.token
12  }
13
14  pub fn new(token: Token<'src>, kind: CompileErrorKind<'src>) -> CompileError<'src> {
15    Self {
16      token,
17      kind: kind.into(),
18    }
19  }
20}
21
22fn capitalize(s: &str) -> String {
23  let mut chars = s.chars();
24  match chars.next() {
25    None => String::new(),
26    Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
27  }
28}
29
30impl Display for CompileError<'_> {
31  fn fmt(&self, f: &mut Formatter) -> fmt::Result {
32    use CompileErrorKind::*;
33
34    match &*self.kind {
35      AttributeArgumentCountMismatch {
36        attribute,
37        found,
38        min,
39        max,
40      } => {
41        write!(
42          f,
43          "Attribute `{attribute}` got {found} {} but takes ",
44          Count("argument", *found),
45        )?;
46
47        if min == max {
48          let expected = min;
49          write!(f, "{expected} {}", Count("argument", *expected))
50        } else if found < min {
51          write!(f, "at least {min} {}", Count("argument", *min))
52        } else {
53          write!(f, "at most {max} {}", Count("argument", *max))
54        }
55      }
56      BacktickShebang => write!(f, "Backticks may not start with `#!`"),
57      CircularRecipeDependency { recipe, ref circle } => {
58        if circle.len() == 2 {
59          write!(f, "Recipe `{recipe}` depends on itself")
60        } else {
61          write!(
62            f,
63            "Recipe `{recipe}` has circular dependency `{}`",
64            circle.join(" -> ")
65          )
66        }
67      }
68      CircularVariableDependency {
69        variable,
70        ref circle,
71      } => {
72        if circle.len() == 2 {
73          write!(f, "Variable `{variable}` is defined in terms of itself")
74        } else {
75          write!(
76            f,
77            "Variable `{variable}` depends on its own value: `{}`",
78            circle.join(" -> "),
79          )
80        }
81      }
82      DependencyArgumentCountMismatch {
83        dependency,
84        found,
85        min,
86        max,
87      } => {
88        write!(
89          f,
90          "Dependency `{dependency}` got {found} {} but takes ",
91          Count("argument", *found),
92        )?;
93
94        if min == max {
95          let expected = min;
96          write!(f, "{expected} {}", Count("argument", *expected))
97        } else if found < min {
98          write!(f, "at least {min} {}", Count("argument", *min))
99        } else {
100          write!(f, "at most {max} {}", Count("argument", *max))
101        }
102      }
103      DuplicateAttribute { attribute, first } => write!(
104        f,
105        "Recipe attribute `{attribute}` first used on line {} is duplicated on line {}",
106        first.ordinal(),
107        self.token.line.ordinal(),
108      ),
109      DuplicateParameter { recipe, parameter } => {
110        write!(f, "Recipe `{recipe}` has duplicate parameter `{parameter}`")
111      }
112      DuplicateSet { setting, first } => write!(
113        f,
114        "Setting `{setting}` first set on line {} is redefined on line {}",
115        first.ordinal(),
116        self.token.line.ordinal(),
117      ),
118      DuplicateVariable { variable } => {
119        write!(f, "Variable `{variable}` has multiple definitions")
120      }
121      DuplicateUnexport { variable } => {
122        write!(f, "Variable `{variable}` is unexported multiple times")
123      }
124      ExpectedKeyword { expected, found } => {
125        let expected = List::or_ticked(expected);
126        if found.kind == TokenKind::Identifier {
127          write!(
128            f,
129            "Expected keyword {expected} but found identifier `{}`",
130            found.lexeme()
131          )
132        } else {
133          write!(f, "Expected keyword {expected} but found `{}`", found.kind)
134        }
135      }
136      ExportUnexported { variable } => {
137        write!(f, "Variable {variable} is both exported and unexported")
138      }
139      ExtraLeadingWhitespace => write!(f, "Recipe line has extra leading whitespace"),
140      ExtraneousAttributes { count } => {
141        write!(f, "Extraneous {}", Count("attribute", *count))
142      }
143      FunctionArgumentCountMismatch {
144        function,
145        found,
146        expected,
147      } => write!(
148        f,
149        "Function `{function}` called with {found} {} but takes {}",
150        Count("argument", *found),
151        expected.display(),
152      ),
153      Include => write!(
154        f,
155        "The `!include` directive has been stabilized as `import`"
156      ),
157      InconsistentLeadingWhitespace { expected, found } => write!(
158        f,
159        "Recipe line has inconsistent leading whitespace. Recipe started with `{}` but found \
160           line with `{}`",
161        ShowWhitespace(expected),
162        ShowWhitespace(found)
163      ),
164      Internal { ref message } => write!(
165        f,
166        "Internal error, this may indicate a bug in just: {message}\n\
167           consider filing an issue: https://github.com/casey/just/issues/new"
168      ),
169      InvalidAttribute {
170        item_name,
171        item_kind,
172        attribute,
173      } => write!(
174        f,
175        "{item_kind} `{item_name}` has invalid attribute `{}`",
176        attribute.name(),
177      ),
178      InvalidEscapeSequence { character } => write!(
179        f,
180        "`\\{}` is not a valid escape sequence",
181        match character {
182          '`' => r"\`".to_owned(),
183          '\\' => r"\".to_owned(),
184          '\'' => r"'".to_owned(),
185          '"' => r#"""#.to_owned(),
186          _ => character.escape_default().collect(),
187        }
188      ),
189      MismatchedClosingDelimiter {
190        open,
191        open_line,
192        close,
193      } => write!(
194        f,
195        "Mismatched closing delimiter `{}`. (Did you mean to close the `{}` on line {}?)",
196        close.close(),
197        open.open(),
198        open_line.ordinal(),
199      ),
200      MixedLeadingWhitespace { whitespace } => write!(
201        f,
202        "Found a mix of tabs and spaces in leading whitespace: `{}`\nLeading whitespace may \
203           consist of tabs or spaces, but not both",
204        ShowWhitespace(whitespace)
205      ),
206      ParameterFollowsVariadicParameter { parameter } => {
207        write!(f, "Parameter `{parameter}` follows variadic parameter")
208      }
209      ParsingRecursionDepthExceeded => write!(f, "Parsing recursion depth exceeded"),
210      Redefinition {
211        first,
212        first_type,
213        name,
214        second_type,
215      } => {
216        if first_type == second_type {
217          write!(
218            f,
219            "{} `{name}` first defined on line {} is redefined on line {}",
220            capitalize(first_type),
221            first.ordinal(),
222            self.token.line.ordinal(),
223          )
224        } else {
225          write!(
226            f,
227            "{} `{name}` defined on line {} is redefined as {} {second_type} on line {}",
228            capitalize(first_type),
229            first.ordinal(),
230            if *second_type == "alias" { "an" } else { "a" },
231            self.token.line.ordinal(),
232          )
233        }
234      }
235      ShebangAndScriptAttribute { recipe } => write!(
236        f,
237        "Recipe `{recipe}` has both shebang line and `[script]` attribute"
238      ),
239      ShellExpansion { err } => write!(f, "Shell expansion failed: {err}"),
240      RequiredParameterFollowsDefaultParameter { parameter } => write!(
241        f,
242        "Non-default parameter `{parameter}` follows default parameter"
243      ),
244      UndefinedVariable { variable } => write!(f, "Variable `{variable}` not defined"),
245      UnexpectedCharacter { expected } => write!(f, "Expected character `{expected}`"),
246      UnexpectedClosingDelimiter { close } => {
247        write!(f, "Unexpected closing delimiter `{}`", close.close())
248      }
249      UnexpectedEndOfToken { expected } => {
250        write!(f, "Expected character `{expected}` but found end-of-file")
251      }
252      UnexpectedToken {
253        ref expected,
254        found,
255      } => write!(f, "Expected {}, but found {found}", List::or(expected)),
256      UnicodeEscapeCharacter { character } => {
257        write!(f, "expected hex digit [0-9A-Fa-f] but found `{character}`")
258      }
259      UnicodeEscapeDelimiter { character } => write!(
260        f,
261        "expected unicode escape sequence delimiter `{{` but found `{character}`"
262      ),
263      UnicodeEscapeEmpty => write!(f, "unicode escape sequences must not be empty"),
264      UnicodeEscapeLength { hex } => write!(
265        f,
266        "unicode escape sequence starting with `\\u{{{hex}` longer than six hex digits"
267      ),
268      UnicodeEscapeRange { hex } => {
269        write!(
270          f,
271          "unicode escape sequence value `{hex}` greater than maximum valid code point `10FFFF`",
272        )
273      }
274      UnicodeEscapeUnterminated => write!(f, "unterminated unicode escape sequence"),
275      UnknownAliasTarget { alias, target } => {
276        write!(f, "Alias `{alias}` has an unknown target `{target}`")
277      }
278      UnknownAttribute { attribute } => write!(f, "Unknown attribute `{attribute}`"),
279      UnknownDependency { recipe, unknown } => {
280        write!(f, "Recipe `{recipe}` has unknown dependency `{unknown}`")
281      }
282      UnknownFunction { function } => write!(f, "Call to unknown function `{function}`"),
283      UnknownSetting { setting } => write!(f, "Unknown setting `{setting}`"),
284      UnknownStartOfToken => write!(f, "Unknown start of token:"),
285      UnpairedCarriageReturn => write!(f, "Unpaired carriage return"),
286      UnterminatedBacktick => write!(f, "Unterminated backtick"),
287      UnterminatedInterpolation => write!(f, "Unterminated interpolation"),
288      UnterminatedString => write!(f, "Unterminated string"),
289    }
290  }
291}