2017-11-16 23:30:08 -08:00
|
|
|
use common::*;
|
|
|
|
|
2018-12-08 14:29:41 -08:00
|
|
|
use misc::{maybe_s, show_whitespace, write_error_context, Or};
|
2017-11-16 23:30:08 -08:00
|
|
|
|
2017-11-17 17:28:06 -08:00
|
|
|
pub type CompilationResult<'a, T> = Result<T, CompilationError<'a>>;
|
|
|
|
|
2017-11-16 23:30:08 -08:00
|
|
|
#[derive(Debug, PartialEq)]
|
|
|
|
pub struct CompilationError<'a> {
|
2018-12-08 14:29:41 -08:00
|
|
|
pub text: &'a str,
|
|
|
|
pub index: usize,
|
|
|
|
pub line: usize,
|
2017-11-16 23:30:08 -08:00
|
|
|
pub column: usize,
|
2018-12-08 14:29:41 -08:00
|
|
|
pub width: Option<usize>,
|
|
|
|
pub kind: CompilationErrorKind<'a>,
|
2017-11-16 23:30:08 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
|
|
pub enum CompilationErrorKind<'a> {
|
2019-04-11 12:30:29 -07:00
|
|
|
AliasShadowsRecipe {
|
|
|
|
alias: &'a str,
|
|
|
|
recipe_line: usize,
|
|
|
|
},
|
2018-12-08 14:29:41 -08:00
|
|
|
CircularRecipeDependency {
|
|
|
|
recipe: &'a str,
|
|
|
|
circle: Vec<&'a str>,
|
|
|
|
},
|
|
|
|
CircularVariableDependency {
|
|
|
|
variable: &'a str,
|
|
|
|
circle: Vec<&'a str>,
|
|
|
|
},
|
|
|
|
DependencyHasParameters {
|
|
|
|
recipe: &'a str,
|
|
|
|
dependency: &'a str,
|
|
|
|
},
|
2019-04-11 12:30:29 -07:00
|
|
|
DuplicateAlias {
|
|
|
|
alias: &'a str,
|
|
|
|
first: usize,
|
|
|
|
},
|
2018-12-08 14:29:41 -08:00
|
|
|
DuplicateDependency {
|
|
|
|
recipe: &'a str,
|
|
|
|
dependency: &'a str,
|
|
|
|
},
|
|
|
|
DuplicateParameter {
|
|
|
|
recipe: &'a str,
|
|
|
|
parameter: &'a str,
|
|
|
|
},
|
|
|
|
DuplicateRecipe {
|
|
|
|
recipe: &'a str,
|
|
|
|
first: usize,
|
|
|
|
},
|
|
|
|
DuplicateVariable {
|
|
|
|
variable: &'a str,
|
|
|
|
},
|
2017-11-16 23:30:08 -08:00
|
|
|
ExtraLeadingWhitespace,
|
2018-12-08 14:29:41 -08:00
|
|
|
FunctionArgumentCountMismatch {
|
|
|
|
function: &'a str,
|
|
|
|
found: usize,
|
|
|
|
expected: usize,
|
|
|
|
},
|
|
|
|
InconsistentLeadingWhitespace {
|
|
|
|
expected: &'a str,
|
|
|
|
found: &'a str,
|
|
|
|
},
|
|
|
|
Internal {
|
|
|
|
message: String,
|
|
|
|
},
|
|
|
|
InvalidEscapeSequence {
|
|
|
|
character: char,
|
|
|
|
},
|
|
|
|
MixedLeadingWhitespace {
|
|
|
|
whitespace: &'a str,
|
|
|
|
},
|
|
|
|
ParameterFollowsVariadicParameter {
|
|
|
|
parameter: &'a str,
|
|
|
|
},
|
|
|
|
ParameterShadowsVariable {
|
|
|
|
parameter: &'a str,
|
|
|
|
},
|
|
|
|
RequiredParameterFollowsDefaultParameter {
|
|
|
|
parameter: &'a str,
|
|
|
|
},
|
|
|
|
UndefinedVariable {
|
|
|
|
variable: &'a str,
|
|
|
|
},
|
|
|
|
UnexpectedToken {
|
|
|
|
expected: Vec<TokenKind>,
|
|
|
|
found: TokenKind,
|
|
|
|
},
|
2019-04-11 12:30:29 -07:00
|
|
|
UnknownAliasTarget {
|
|
|
|
alias: &'a str,
|
|
|
|
target: &'a str,
|
|
|
|
},
|
2018-12-08 14:29:41 -08:00
|
|
|
UnknownDependency {
|
|
|
|
recipe: &'a str,
|
|
|
|
unknown: &'a str,
|
|
|
|
},
|
|
|
|
UnknownFunction {
|
|
|
|
function: &'a str,
|
|
|
|
},
|
2017-11-16 23:30:08 -08:00
|
|
|
UnknownStartOfToken,
|
2017-12-02 12:49:31 -08:00
|
|
|
UnterminatedInterpolation,
|
2017-11-16 23:30:08 -08:00
|
|
|
UnterminatedString,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Display for CompilationError<'a> {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
|
|
|
use CompilationErrorKind::*;
|
2018-12-08 14:29:41 -08:00
|
|
|
let error = Color::fmt(f).error();
|
2017-11-16 23:30:08 -08:00
|
|
|
let message = Color::fmt(f).message();
|
|
|
|
|
|
|
|
write!(f, "{} {}", error.paint("error:"), message.prefix())?;
|
|
|
|
|
|
|
|
match self.kind {
|
2019-04-11 12:30:29 -07:00
|
|
|
AliasShadowsRecipe { alias, recipe_line } => {
|
|
|
|
writeln!(
|
|
|
|
f,
|
|
|
|
"Alias `{}` defined on `{}` shadows recipe defined on `{}`",
|
|
|
|
alias,
|
|
|
|
self.line + 1,
|
|
|
|
recipe_line + 1,
|
|
|
|
)?;
|
|
|
|
}
|
2018-12-08 14:29:41 -08:00
|
|
|
CircularRecipeDependency { recipe, ref circle } => {
|
2017-11-16 23:30:08 -08:00
|
|
|
if circle.len() == 2 {
|
|
|
|
writeln!(f, "Recipe `{}` depends on itself", recipe)?;
|
|
|
|
} else {
|
2018-12-08 14:29:41 -08:00
|
|
|
writeln!(
|
|
|
|
f,
|
|
|
|
"Recipe `{}` has circular dependency `{}`",
|
|
|
|
recipe,
|
|
|
|
circle.join(" -> ")
|
|
|
|
)?;
|
2017-11-16 23:30:08 -08:00
|
|
|
}
|
|
|
|
}
|
2018-12-08 14:29:41 -08:00
|
|
|
CircularVariableDependency {
|
|
|
|
variable,
|
|
|
|
ref circle,
|
|
|
|
} => {
|
2017-11-16 23:30:08 -08:00
|
|
|
if circle.len() == 2 {
|
|
|
|
writeln!(f, "Variable `{}` is defined in terms of itself", variable)?;
|
|
|
|
} else {
|
2018-12-08 14:29:41 -08:00
|
|
|
writeln!(
|
|
|
|
f,
|
|
|
|
"Variable `{}` depends on its own value: `{}`",
|
|
|
|
variable,
|
|
|
|
circle.join(" -> ")
|
|
|
|
)?;
|
2017-11-16 23:30:08 -08:00
|
|
|
}
|
|
|
|
}
|
2018-06-30 19:19:13 -07:00
|
|
|
|
2018-12-08 14:29:41 -08:00
|
|
|
InvalidEscapeSequence { character } => {
|
2018-06-30 19:19:13 -07:00
|
|
|
let representation = match character {
|
2018-12-08 14:29:41 -08:00
|
|
|
'`' => r"\`".to_string(),
|
2018-06-30 19:19:13 -07:00
|
|
|
'\\' => r"\".to_string(),
|
|
|
|
'\'' => r"'".to_string(),
|
|
|
|
'"' => r#"""#.to_string(),
|
|
|
|
_ => character.escape_default().collect(),
|
|
|
|
};
|
|
|
|
writeln!(f, "`\\{}` is not a valid escape sequence", representation)?;
|
2017-11-16 23:30:08 -08:00
|
|
|
}
|
2018-12-08 14:29:41 -08:00
|
|
|
DuplicateParameter { recipe, parameter } => {
|
|
|
|
writeln!(
|
|
|
|
f,
|
|
|
|
"Recipe `{}` has duplicate parameter `{}`",
|
|
|
|
recipe, parameter
|
|
|
|
)?;
|
2017-11-16 23:30:08 -08:00
|
|
|
}
|
2018-12-08 14:29:41 -08:00
|
|
|
DuplicateVariable { variable } => {
|
2017-11-16 23:30:08 -08:00
|
|
|
writeln!(f, "Variable `{}` has multiple definitions", variable)?;
|
|
|
|
}
|
2018-12-08 14:29:41 -08:00
|
|
|
UnexpectedToken {
|
|
|
|
ref expected,
|
|
|
|
found,
|
|
|
|
} => {
|
2017-11-16 23:30:08 -08:00
|
|
|
writeln!(f, "Expected {}, but found {}", Or(expected), found)?;
|
|
|
|
}
|
2019-04-11 12:30:29 -07:00
|
|
|
DuplicateAlias { alias, first } => {
|
|
|
|
writeln!(
|
|
|
|
f,
|
|
|
|
"Alias `{}` first defined on line `{}` is redefined on line `{}`",
|
|
|
|
alias,
|
|
|
|
first + 1,
|
|
|
|
self.line + 1,
|
|
|
|
)?;
|
|
|
|
}
|
2018-12-08 14:29:41 -08:00
|
|
|
DuplicateDependency { recipe, dependency } => {
|
|
|
|
writeln!(
|
|
|
|
f,
|
|
|
|
"Recipe `{}` has duplicate dependency `{}`",
|
|
|
|
recipe, dependency
|
|
|
|
)?;
|
2017-11-16 23:30:08 -08:00
|
|
|
}
|
2018-12-08 14:29:41 -08:00
|
|
|
DuplicateRecipe { recipe, first } => {
|
|
|
|
writeln!(
|
|
|
|
f,
|
|
|
|
"Recipe `{}` first defined on line {} is redefined on line {}",
|
|
|
|
recipe,
|
|
|
|
first + 1,
|
|
|
|
self.line + 1
|
|
|
|
)?;
|
2017-11-16 23:30:08 -08:00
|
|
|
}
|
2018-12-08 14:29:41 -08:00
|
|
|
DependencyHasParameters { recipe, dependency } => {
|
|
|
|
writeln!(
|
|
|
|
f,
|
|
|
|
"Recipe `{}` depends on `{}` which requires arguments. \
|
|
|
|
Dependencies may not require arguments",
|
|
|
|
recipe, dependency
|
|
|
|
)?;
|
2017-11-16 23:30:08 -08:00
|
|
|
}
|
2018-12-08 14:29:41 -08:00
|
|
|
ParameterShadowsVariable { parameter } => {
|
|
|
|
writeln!(
|
|
|
|
f,
|
|
|
|
"Parameter `{}` shadows variable of the same name",
|
|
|
|
parameter
|
|
|
|
)?;
|
2017-11-16 23:30:08 -08:00
|
|
|
}
|
2018-12-08 14:29:41 -08:00
|
|
|
RequiredParameterFollowsDefaultParameter { parameter } => {
|
|
|
|
writeln!(
|
|
|
|
f,
|
|
|
|
"Non-default parameter `{}` follows default parameter",
|
|
|
|
parameter
|
|
|
|
)?;
|
2017-11-16 23:30:08 -08:00
|
|
|
}
|
2018-12-08 14:29:41 -08:00
|
|
|
ParameterFollowsVariadicParameter { parameter } => {
|
2017-11-16 23:30:08 -08:00
|
|
|
writeln!(f, "Parameter `{}` follows variadic parameter", parameter)?;
|
|
|
|
}
|
2018-12-08 14:29:41 -08:00
|
|
|
MixedLeadingWhitespace { whitespace } => {
|
|
|
|
writeln!(
|
|
|
|
f,
|
2017-11-16 23:30:08 -08:00
|
|
|
"Found a mix of tabs and spaces in leading whitespace: `{}`\n\
|
2018-12-08 14:29:41 -08:00
|
|
|
Leading whitespace may consist of tabs or spaces, but not both",
|
2017-11-16 23:30:08 -08:00
|
|
|
show_whitespace(whitespace)
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
ExtraLeadingWhitespace => {
|
|
|
|
writeln!(f, "Recipe line has extra leading whitespace")?;
|
|
|
|
}
|
2018-12-08 14:29:41 -08:00
|
|
|
FunctionArgumentCountMismatch {
|
|
|
|
function,
|
|
|
|
found,
|
|
|
|
expected,
|
|
|
|
} => {
|
2017-12-02 14:59:07 -08:00
|
|
|
writeln!(
|
|
|
|
f,
|
|
|
|
"Function `{}` called with {} argument{} but takes {}",
|
2018-12-08 14:29:41 -08:00
|
|
|
function,
|
|
|
|
found,
|
|
|
|
maybe_s(found),
|
|
|
|
expected
|
2017-12-02 14:59:07 -08:00
|
|
|
)?;
|
|
|
|
}
|
2018-12-08 14:29:41 -08:00
|
|
|
InconsistentLeadingWhitespace { expected, found } => {
|
|
|
|
writeln!(
|
|
|
|
f,
|
2017-11-16 23:30:08 -08:00
|
|
|
"Recipe line has inconsistent leading whitespace. \
|
|
|
|
Recipe started with `{}` but found line with `{}`",
|
2018-12-08 14:29:41 -08:00
|
|
|
show_whitespace(expected),
|
|
|
|
show_whitespace(found)
|
2017-11-16 23:30:08 -08:00
|
|
|
)?;
|
|
|
|
}
|
2019-04-11 12:30:29 -07:00
|
|
|
UnknownAliasTarget { alias, target } => {
|
|
|
|
writeln!(f, "Alias `{}` has an unknown target `{}`", alias, target)?;
|
|
|
|
}
|
2018-12-08 14:29:41 -08:00
|
|
|
UnknownDependency { recipe, unknown } => {
|
|
|
|
writeln!(
|
|
|
|
f,
|
|
|
|
"Recipe `{}` has unknown dependency `{}`",
|
|
|
|
recipe, unknown
|
|
|
|
)?;
|
2017-11-16 23:30:08 -08:00
|
|
|
}
|
2018-12-08 14:29:41 -08:00
|
|
|
UndefinedVariable { variable } => {
|
2017-11-16 23:30:08 -08:00
|
|
|
writeln!(f, "Variable `{}` not defined", variable)?;
|
|
|
|
}
|
2018-12-08 14:29:41 -08:00
|
|
|
UnknownFunction { function } => {
|
2017-12-02 05:37:10 -08:00
|
|
|
writeln!(f, "Call to unknown function `{}`", function)?;
|
|
|
|
}
|
2017-11-16 23:30:08 -08:00
|
|
|
UnknownStartOfToken => {
|
|
|
|
writeln!(f, "Unknown start of token:")?;
|
|
|
|
}
|
2017-12-02 12:49:31 -08:00
|
|
|
UnterminatedInterpolation => {
|
|
|
|
writeln!(f, "Unterminated interpolation")?;
|
|
|
|
}
|
2017-11-16 23:30:08 -08:00
|
|
|
UnterminatedString => {
|
|
|
|
writeln!(f, "Unterminated string")?;
|
|
|
|
}
|
2018-12-08 14:29:41 -08:00
|
|
|
Internal { ref message } => {
|
|
|
|
writeln!(
|
|
|
|
f,
|
|
|
|
"Internal error, this may indicate a bug in just: {}\n\
|
|
|
|
consider filing an issue: https://github.com/casey/just/issues/new",
|
|
|
|
message
|
|
|
|
)?;
|
2017-11-16 23:30:08 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
write!(f, "{}", message.suffix())?;
|
|
|
|
|
|
|
|
write_error_context(f, self.text, self.index, self.line, self.column, self.width)
|
|
|
|
}
|
|
|
|
}
|