2022-06-18 21:56:31 -07:00
|
|
|
use super::*;
|
2017-11-16 23:30:08 -08:00
|
|
|
|
|
|
|
#[derive(Debug, PartialEq)]
|
2021-07-26 01:26:06 -07:00
|
|
|
pub(crate) struct CompileError<'src> {
|
2019-11-13 19:32:50 -08:00
|
|
|
pub(crate) token: Token<'src>,
|
2022-09-11 01:41:24 -07:00
|
|
|
pub(crate) kind: Box<CompileErrorKind<'src>>,
|
2017-11-16 23:30:08 -08:00
|
|
|
}
|
|
|
|
|
2021-07-26 01:26:06 -07:00
|
|
|
impl<'src> CompileError<'src> {
|
|
|
|
pub(crate) fn context(&self) -> Token<'src> {
|
|
|
|
self.token
|
|
|
|
}
|
2022-09-11 01:41:24 -07:00
|
|
|
|
|
|
|
pub(crate) fn new(token: Token<'src>, kind: CompileErrorKind<'src>) -> CompileError<'src> {
|
|
|
|
Self {
|
|
|
|
token,
|
2024-05-14 20:07:41 -07:00
|
|
|
kind: kind.into(),
|
2022-09-11 01:41:24 -07:00
|
|
|
}
|
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
}
|
Gargantuan refactor (#522)
- Instead of changing the current directory with `env::set_current_dir`
to be implicitly inherited by subprocesses, we now use
`Command::current_dir` to set it explicitly. This feels much better,
since we aren't dependent on the implicit state of the process's
current directory.
- Subcommand execution is much improved.
- Added a ton of tests for config parsing, config execution, working
dir, and search dir.
- Error messages are improved. Many more will be colored.
- The Config is now onwed, instead of borrowing from the arguments and
the `clap::ArgMatches` object. This is a huge ergonomic improvement,
especially in tests, and I don't think anyone will notice.
- `--edit` now uses `$VISUAL`, `$EDITOR`, or `vim`, in that order,
matching git, which I think is what most people will expect.
- Added a cute `tmptree!{}` macro, for creating temporary directories
populated with directories and files for tests.
- Admitted that grammer is LL(k) and I don't know what `k` is.
2019-11-09 21:43:20 -08:00
|
|
|
|
2023-12-27 20:27:15 -08:00
|
|
|
fn capitalize(s: &str) -> String {
|
|
|
|
let mut chars = s.chars();
|
|
|
|
match chars.next() {
|
|
|
|
None => String::new(),
|
|
|
|
Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-26 01:26:06 -07:00
|
|
|
impl Display for CompileError<'_> {
|
2019-04-11 15:23:14 -07:00
|
|
|
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
|
2021-07-26 01:26:06 -07:00
|
|
|
use CompileErrorKind::*;
|
2017-11-16 23:30:08 -08:00
|
|
|
|
2022-09-11 01:41:24 -07:00
|
|
|
match &*self.kind {
|
2024-01-12 18:44:13 -08:00
|
|
|
AliasInvalidAttribute { alias, attribute } => {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"Alias `{alias}` has invalid attribute `{}`",
|
|
|
|
attribute.name(),
|
|
|
|
)
|
|
|
|
}
|
2023-06-12 09:53:55 -07:00
|
|
|
AliasShadowsRecipe { alias, recipe_line } => write!(
|
|
|
|
f,
|
|
|
|
"Alias `{alias}` defined on line {} shadows recipe `{alias}` defined on line {}",
|
|
|
|
self.token.line.ordinal(),
|
|
|
|
recipe_line.ordinal(),
|
|
|
|
),
|
2024-05-25 00:32:25 -07:00
|
|
|
AttributeArgumentCountMismatch {
|
|
|
|
attribute,
|
|
|
|
found,
|
|
|
|
min,
|
|
|
|
max,
|
|
|
|
} => {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"Attribute `{attribute}` got {found} {} but takes ",
|
|
|
|
Count("argument", *found),
|
|
|
|
)?;
|
|
|
|
|
|
|
|
if min == max {
|
|
|
|
let expected = min;
|
|
|
|
write!(f, "{expected} {}", Count("argument", *expected))
|
|
|
|
} else if found < min {
|
|
|
|
write!(f, "at least {min} {}", Count("argument", *min))
|
|
|
|
} else {
|
|
|
|
write!(f, "at most {max} {}", Count("argument", *max))
|
|
|
|
}
|
|
|
|
}
|
2023-06-12 09:53:55 -07:00
|
|
|
BacktickShebang => write!(f, "Backticks may not start with `#!`"),
|
2021-09-16 06:44:40 -07:00
|
|
|
CircularRecipeDependency { recipe, ref circle } => {
|
2017-11-16 23:30:08 -08:00
|
|
|
if circle.len() == 2 {
|
2023-06-12 09:53:55 -07:00
|
|
|
write!(f, "Recipe `{recipe}` depends on itself")
|
2017-11-16 23:30:08 -08:00
|
|
|
} else {
|
2021-07-26 01:26:06 -07:00
|
|
|
write!(
|
2018-12-08 14:29:41 -08:00
|
|
|
f,
|
2023-06-12 09:53:55 -07:00
|
|
|
"Recipe `{recipe}` has circular dependency `{}`",
|
2018-12-08 14:29:41 -08:00
|
|
|
circle.join(" -> ")
|
2023-06-12 09:53:55 -07:00
|
|
|
)
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
|
|
|
}
|
2018-12-08 14:29:41 -08:00
|
|
|
CircularVariableDependency {
|
|
|
|
variable,
|
|
|
|
ref circle,
|
2021-09-16 06:44:40 -07:00
|
|
|
} => {
|
2017-11-16 23:30:08 -08:00
|
|
|
if circle.len() == 2 {
|
2023-06-12 09:53:55 -07:00
|
|
|
write!(f, "Variable `{variable}` is defined in terms of itself")
|
2017-11-16 23:30:08 -08:00
|
|
|
} else {
|
2021-07-26 01:26:06 -07:00
|
|
|
write!(
|
2018-12-08 14:29:41 -08:00
|
|
|
f,
|
2023-06-12 09:53:55 -07:00
|
|
|
"Variable `{variable}` depends on its own value: `{}`",
|
|
|
|
circle.join(" -> "),
|
|
|
|
)
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
DependencyArgumentCountMismatch {
|
|
|
|
dependency,
|
|
|
|
found,
|
|
|
|
min,
|
|
|
|
max,
|
|
|
|
} => {
|
|
|
|
write!(
|
|
|
|
f,
|
2023-06-12 09:53:55 -07:00
|
|
|
"Dependency `{dependency}` got {found} {} but takes ",
|
2021-07-26 01:26:06 -07:00
|
|
|
Count("argument", *found),
|
|
|
|
)?;
|
2018-06-30 19:19:13 -07:00
|
|
|
|
2021-07-26 01:26:06 -07:00
|
|
|
if min == max {
|
|
|
|
let expected = min;
|
2023-06-12 09:53:55 -07:00
|
|
|
write!(f, "{expected} {}", Count("argument", *expected))
|
2021-07-26 01:26:06 -07:00
|
|
|
} else if found < min {
|
2023-06-12 09:53:55 -07:00
|
|
|
write!(f, "at least {min} {}", Count("argument", *min))
|
2021-07-26 01:26:06 -07:00
|
|
|
} else {
|
2023-06-12 09:53:55 -07:00
|
|
|
write!(f, "at most {max} {}", Count("argument", *max))
|
2021-07-26 01:26:06 -07:00
|
|
|
}
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2023-06-12 09:53:55 -07:00
|
|
|
DuplicateAttribute { attribute, first } => write!(
|
|
|
|
f,
|
|
|
|
"Recipe attribute `{attribute}` first used on line {} is duplicated on line {}",
|
|
|
|
first.ordinal(),
|
|
|
|
self.token.line.ordinal(),
|
|
|
|
),
|
2021-07-26 01:26:06 -07:00
|
|
|
DuplicateParameter { recipe, parameter } => {
|
2023-06-12 09:53:55 -07:00
|
|
|
write!(f, "Recipe `{recipe}` has duplicate parameter `{parameter}`")
|
|
|
|
}
|
|
|
|
DuplicateSet { setting, first } => write!(
|
|
|
|
f,
|
|
|
|
"Setting `{setting}` first set on line {} is redefined on line {}",
|
|
|
|
first.ordinal(),
|
|
|
|
self.token.line.ordinal(),
|
|
|
|
),
|
2021-07-26 01:26:06 -07:00
|
|
|
DuplicateVariable { variable } => {
|
2023-06-12 09:53:55 -07:00
|
|
|
write!(f, "Variable `{variable}` has multiple definitions")
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2022-06-30 03:34:11 -07:00
|
|
|
ExpectedKeyword { expected, found } => {
|
2023-06-12 09:53:55 -07:00
|
|
|
let expected = List::or_ticked(expected);
|
2022-06-30 03:34:11 -07:00
|
|
|
if found.kind == TokenKind::Identifier {
|
|
|
|
write!(
|
|
|
|
f,
|
2023-06-12 09:53:55 -07:00
|
|
|
"Expected keyword {expected} but found identifier `{}`",
|
2022-06-30 03:34:11 -07:00
|
|
|
found.lexeme()
|
2023-06-12 09:53:55 -07:00
|
|
|
)
|
2022-06-30 03:34:11 -07:00
|
|
|
} else {
|
2023-06-12 09:53:55 -07:00
|
|
|
write!(f, "Expected keyword {expected} but found `{}`", found.kind)
|
2022-06-30 03:34:11 -07:00
|
|
|
}
|
|
|
|
}
|
2023-06-12 09:53:55 -07:00
|
|
|
ExtraLeadingWhitespace => write!(f, "Recipe line has extra leading whitespace"),
|
2018-12-08 14:29:41 -08:00
|
|
|
FunctionArgumentCountMismatch {
|
|
|
|
function,
|
|
|
|
found,
|
|
|
|
expected,
|
2023-06-12 09:53:55 -07:00
|
|
|
} => write!(
|
|
|
|
f,
|
|
|
|
"Function `{function}` called with {found} {} but takes {}",
|
|
|
|
Count("argument", *found),
|
|
|
|
expected.display(),
|
|
|
|
),
|
2023-12-24 09:14:17 -08:00
|
|
|
Include => write!(
|
|
|
|
f,
|
|
|
|
"The `!include` directive has been stabilized as `import`"
|
|
|
|
),
|
2023-06-12 09:53:55 -07:00
|
|
|
InconsistentLeadingWhitespace { expected, found } => write!(
|
|
|
|
f,
|
|
|
|
"Recipe line has inconsistent leading whitespace. Recipe started with `{}` but found \
|
2020-02-10 20:07:06 -08:00
|
|
|
line with `{}`",
|
2023-06-12 09:53:55 -07:00
|
|
|
ShowWhitespace(expected),
|
|
|
|
ShowWhitespace(found)
|
|
|
|
),
|
|
|
|
Internal { ref message } => write!(
|
|
|
|
f,
|
|
|
|
"Internal error, this may indicate a bug in just: {message}\n\
|
2023-01-26 18:49:03 -08:00
|
|
|
consider filing an issue: https://github.com/casey/just/issues/new"
|
2023-06-12 09:53:55 -07:00
|
|
|
),
|
|
|
|
InvalidEscapeSequence { character } => write!(
|
|
|
|
f,
|
|
|
|
"`\\{}` is not a valid escape sequence",
|
|
|
|
match character {
|
2021-07-26 01:26:06 -07:00
|
|
|
'`' => r"\`".to_owned(),
|
|
|
|
'\\' => r"\".to_owned(),
|
|
|
|
'\'' => r"'".to_owned(),
|
|
|
|
'"' => r#"""#.to_owned(),
|
|
|
|
_ => character.escape_default().collect(),
|
2023-06-12 09:53:55 -07:00
|
|
|
}
|
|
|
|
),
|
2020-10-27 23:51:17 -07:00
|
|
|
MismatchedClosingDelimiter {
|
|
|
|
open,
|
|
|
|
open_line,
|
|
|
|
close,
|
2023-06-12 09:53:55 -07:00
|
|
|
} => write!(
|
|
|
|
f,
|
|
|
|
"Mismatched closing delimiter `{}`. (Did you mean to close the `{}` on line {}?)",
|
|
|
|
close.close(),
|
|
|
|
open.open(),
|
|
|
|
open_line.ordinal(),
|
|
|
|
),
|
|
|
|
MixedLeadingWhitespace { whitespace } => write!(
|
|
|
|
f,
|
|
|
|
"Found a mix of tabs and spaces in leading whitespace: `{}`\nLeading whitespace may \
|
2021-07-26 01:26:06 -07:00
|
|
|
consist of tabs or spaces, but not both",
|
2023-06-12 09:53:55 -07:00
|
|
|
ShowWhitespace(whitespace)
|
|
|
|
),
|
2021-07-26 01:26:06 -07:00
|
|
|
ParameterFollowsVariadicParameter { parameter } => {
|
2023-06-12 09:53:55 -07:00
|
|
|
write!(f, "Parameter `{parameter}` follows variadic parameter")
|
|
|
|
}
|
|
|
|
ParsingRecursionDepthExceeded => write!(f, "Parsing recursion depth exceeded"),
|
2023-12-27 20:27:15 -08:00
|
|
|
Redefinition {
|
|
|
|
first,
|
|
|
|
first_type,
|
|
|
|
name,
|
|
|
|
second_type,
|
|
|
|
} => {
|
|
|
|
if first_type == second_type {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"{} `{name}` first defined on line {} is redefined on line {}",
|
|
|
|
capitalize(first_type),
|
|
|
|
first.ordinal(),
|
|
|
|
self.token.line.ordinal(),
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"{} `{name}` defined on line {} is redefined as {} {second_type} on line {}",
|
|
|
|
capitalize(first_type),
|
|
|
|
first.ordinal(),
|
|
|
|
if *second_type == "alias" { "an" } else { "a" },
|
|
|
|
self.token.line.ordinal(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
2024-05-18 22:41:38 -07:00
|
|
|
ShellExpansion { err } => write!(f, "Shell expansion failed: {err}"),
|
2023-06-12 09:53:55 -07:00
|
|
|
RequiredParameterFollowsDefaultParameter { parameter } => write!(
|
|
|
|
f,
|
|
|
|
"Non-default parameter `{parameter}` follows default parameter"
|
|
|
|
),
|
|
|
|
UndefinedVariable { variable } => write!(f, "Variable `{variable}` not defined"),
|
|
|
|
UnexpectedCharacter { expected } => write!(f, "Expected character `{expected}`"),
|
2021-07-26 01:26:06 -07:00
|
|
|
UnexpectedClosingDelimiter { close } => {
|
2023-06-12 09:53:55 -07:00
|
|
|
write!(f, "Unexpected closing delimiter `{}`", close.close())
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
UnexpectedEndOfToken { expected } => {
|
2023-06-12 09:53:55 -07:00
|
|
|
write!(f, "Expected character `{expected}` but found end-of-file")
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
UnexpectedToken {
|
|
|
|
ref expected,
|
|
|
|
found,
|
2023-06-12 09:53:55 -07:00
|
|
|
} => write!(f, "Expected {}, but found {found}", List::or(expected)),
|
2021-07-26 01:26:06 -07:00
|
|
|
UnknownAliasTarget { alias, target } => {
|
2023-06-12 09:53:55 -07:00
|
|
|
write!(f, "Alias `{alias}` has an unknown target `{target}`")
|
2022-10-25 16:32:36 -07:00
|
|
|
}
|
2023-06-12 09:53:55 -07:00
|
|
|
UnknownAttribute { attribute } => write!(f, "Unknown attribute `{attribute}`"),
|
2021-07-26 01:26:06 -07:00
|
|
|
UnknownDependency { recipe, unknown } => {
|
2023-06-12 09:53:55 -07:00
|
|
|
write!(f, "Recipe `{recipe}` has unknown dependency `{unknown}`")
|
|
|
|
}
|
|
|
|
UnknownFunction { function } => write!(f, "Call to unknown function `{function}`"),
|
|
|
|
UnknownSetting { setting } => write!(f, "Unknown setting `{setting}`"),
|
|
|
|
UnknownStartOfToken => write!(f, "Unknown start of token:"),
|
|
|
|
UnpairedCarriageReturn => write!(f, "Unpaired carriage return"),
|
|
|
|
UnterminatedBacktick => write!(f, "Unterminated backtick"),
|
|
|
|
UnterminatedInterpolation => write!(f, "Unterminated interpolation"),
|
|
|
|
UnterminatedString => write!(f, "Unterminated string"),
|
2017-11-16 23:30:08 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|