From 1856646a9c65791c3818d1306c8d66c728c64d63 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Sat, 12 Nov 2016 14:24:33 -0800 Subject: [PATCH] Error -> CompileError (#97) Fixes #43 --- src/lib.rs | 116 ++++++++++++++++++++++++++-------------------------- src/unit.rs | 68 +++++++++++++++--------------- 2 files changed, 93 insertions(+), 91 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5cfa312..7c38836 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -424,7 +424,7 @@ fn resolve_recipes<'a>( recipes: &Map<&'a str, Recipe<'a>>, assignments: &Map<&'a str, Expression<'a>>, text: &'a str, -) -> Result<(), Error<'a>> { +) -> Result<(), CompileError<'a>> { let mut resolver = Resolver { seen: Set::new(), stack: vec![], @@ -455,7 +455,7 @@ fn resolve_recipes<'a>( // of the struct, and the second being the lifetime of the tokens // that it contains let error = variable.error(ErrorKind::UndefinedVariable{variable: name}); - return Err(Error { + return Err(CompileError { text: text, index: error.index, line: error.line, @@ -483,7 +483,7 @@ struct Resolver<'a: 'b, 'b> { } impl<'a, 'b> Resolver<'a, 'b> { - fn resolve(&mut self, recipe: &Recipe<'a>) -> Result<(), Error<'a>> { + fn resolve(&mut self, recipe: &Recipe<'a>) -> Result<(), CompileError<'a>> { if self.resolved.contains(recipe.name) { return Ok(()) } @@ -519,7 +519,7 @@ impl<'a, 'b> Resolver<'a, 'b> { fn resolve_assignments<'a>( assignments: &Map<&'a str, Expression<'a>>, assignment_tokens: &Map<&'a str, Token<'a>>, -) -> Result<(), Error<'a>> { +) -> Result<(), CompileError<'a>> { let mut resolver = AssignmentResolver { assignments: assignments, @@ -545,7 +545,7 @@ struct AssignmentResolver<'a: 'b, 'b> { } impl<'a: 'b, 'b> AssignmentResolver<'a, 'b> { - fn resolve_assignment(&mut self, name: &'a str) -> Result<(), Error<'a>> { + fn resolve_assignment(&mut self, name: &'a str) -> Result<(), CompileError<'a>> { if self.evaluated.contains(name) { return Ok(()); } @@ -562,7 +562,7 @@ impl<'a: 'b, 'b> AssignmentResolver<'a, 'b> { Ok(()) } - fn resolve_expression(&mut self, expression: &Expression<'a>) -> Result<(), Error<'a>> { + fn resolve_expression(&mut self, expression: &Expression<'a>) -> Result<(), CompileError<'a>> { match *expression { Expression::Variable{name, ref token} => { if self.evaluated.contains(name) { @@ -695,7 +695,7 @@ impl<'a, 'b> Evaluator<'a, 'b> { } #[derive(Debug, PartialEq)] -struct Error<'a> { +struct CompileError<'a> { text: &'a str, index: usize, line: usize, @@ -728,8 +728,8 @@ enum ErrorKind<'a> { UnterminatedString, } -fn internal_error(message: String) -> Error<'static> { - Error { +fn internal_error(message: String) -> CompileError<'static> { + CompileError { text: "", index: 0, line: 0, @@ -773,7 +773,7 @@ struct CookedString<'a> { cooked: String, } -fn cook_string<'a>(token: &Token<'a>) -> Result, Error<'a>> { +fn cook_string<'a>(token: &Token<'a>) -> Result, CompileError<'a>> { let raw = &token.lexeme[1..token.lexeme.len()-1]; if let RawString = token.kind { @@ -929,15 +929,16 @@ fn maybe_bold(colors: bool) -> ansi_term::Style { } } -impl<'a> Display for Error<'a> { +impl<'a> Display for CompileError<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + use ErrorKind::*; let red = maybe_red(f.alternate()); let bold = maybe_bold(f.alternate()); write!(f, "{} {}", red.paint("error:"), bold.prefix())?; match self.kind { - ErrorKind::CircularRecipeDependency{recipe, ref circle} => { + CircularRecipeDependency{recipe, ref circle} => { if circle.len() == 2 { write!(f, "recipe `{}` depends on itself", recipe)?; } else { @@ -945,7 +946,7 @@ impl<'a> Display for Error<'a> { recipe, circle.join(" -> "))?; } } - ErrorKind::CircularVariableDependency{variable, ref circle} => { + CircularVariableDependency{variable, ref circle} => { if circle.len() == 2 { writeln!(f, "variable `{}` depends on its own value: `{}`", variable, circle.join(" -> "))?; @@ -954,68 +955,68 @@ impl<'a> Display for Error<'a> { variable, circle.join(" -> "))?; } } - ErrorKind::InvalidEscapeSequence{character} => { + InvalidEscapeSequence{character} => { writeln!(f, "`\\{}` is not a valid escape sequence", character.escape_default().collect::())?; } - ErrorKind::DuplicateParameter{recipe, parameter} => { + DuplicateParameter{recipe, parameter} => { writeln!(f, "recipe `{}` has duplicate parameter `{}`", recipe, parameter)?; } - ErrorKind::DuplicateVariable{variable} => { + DuplicateVariable{variable} => { writeln!(f, "variable `{}` is has multiple definitions", variable)?; } - ErrorKind::UnexpectedToken{ref expected, found} => { + UnexpectedToken{ref expected, found} => { writeln!(f, "expected {} but found {}", Or(expected), found)?; } - ErrorKind::DuplicateDependency{recipe, dependency} => { + DuplicateDependency{recipe, dependency} => { writeln!(f, "recipe `{}` has duplicate dependency `{}`", recipe, dependency)?; } - ErrorKind::DuplicateRecipe{recipe, first} => { + DuplicateRecipe{recipe, first} => { writeln!(f, "recipe `{}` first defined on line {} is redefined on line {}", recipe, first, self.line)?; } - ErrorKind::DependencyHasParameters{recipe, dependency} => { + DependencyHasParameters{recipe, dependency} => { writeln!(f, "recipe `{}` depends on `{}` which requires arguments. \ dependencies may not require arguments", recipe, dependency)?; } - ErrorKind::ParameterShadowsVariable{parameter} => { + ParameterShadowsVariable{parameter} => { writeln!(f, "parameter `{}` shadows variable of the same name", parameter)?; } - ErrorKind::RequiredParameterFollowsDefaultParameter{parameter} => { + RequiredParameterFollowsDefaultParameter{parameter} => { writeln!(f, "non-default parameter `{}` follows default parameter", parameter)?; } - ErrorKind::MixedLeadingWhitespace{whitespace} => { + MixedLeadingWhitespace{whitespace} => { writeln!(f, "found a mix of tabs and spaces in leading whitespace: `{}`\n\ leading whitespace may consist of tabs or spaces, but not both", show_whitespace(whitespace) )?; } - ErrorKind::ExtraLeadingWhitespace => { + ExtraLeadingWhitespace => { writeln!(f, "recipe line has extra leading whitespace")?; } - ErrorKind::InconsistentLeadingWhitespace{expected, found} => { + InconsistentLeadingWhitespace{expected, found} => { writeln!(f, "inconsistant leading whitespace: recipe started with `{}` but found line with `{}`:", show_whitespace(expected), show_whitespace(found) )?; } - ErrorKind::OuterShebang => { + OuterShebang => { writeln!(f, "a shebang `#!` is reserved syntax outside of recipes")?; } - ErrorKind::UnknownDependency{recipe, unknown} => { + UnknownDependency{recipe, unknown} => { writeln!(f, "recipe `{}` has unknown dependency `{}`", recipe, unknown)?; } - ErrorKind::UndefinedVariable{variable} => { + UndefinedVariable{variable} => { writeln!(f, "variable `{}` not defined", variable)?; } - ErrorKind::UnknownStartOfToken => { + UnknownStartOfToken => { writeln!(f, "unknown start of token:")?; } - ErrorKind::UnterminatedString => { + UnterminatedString => { writeln!(f, "unterminated string")?; } - ErrorKind::InternalError{ref message} => { + InternalError{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)?; @@ -1213,6 +1214,7 @@ enum RunError<'a> { impl<'a> Display for RunError<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + use RunError::*; let red = maybe_red(f.alternate()); let bold = maybe_bold(f.alternate()); write!(f, "{} {}", red.paint("error:"), bold.prefix())?; @@ -1220,23 +1222,23 @@ impl<'a> Display for RunError<'a> { let mut error_token = None; match *self { - RunError::UnknownRecipes{ref recipes, ref suggestion} => { + UnknownRecipes{ref recipes, ref suggestion} => { write!(f, "Justfile does not contain recipe{} {}.", maybe_s(recipes.len()), Or(&ticks(&recipes)))?; if let Some(suggestion) = *suggestion { write!(f, "\nDid you mean `{}`?", suggestion)?; } }, - RunError::UnknownOverrides{ref overrides} => { + UnknownOverrides{ref overrides} => { write!(f, "Variable{} {} overridden on the command line but not present in justfile", maybe_s(overrides.len()), And(&overrides.iter().map(Tick).collect::>()))?; }, - RunError::NonLeadingRecipeWithParameters{recipe} => { + NonLeadingRecipeWithParameters{recipe} => { write!(f, "Recipe `{}` takes arguments and so must be the first and only recipe \ specified on the command line", recipe)?; }, - RunError::ArgumentCountMismatch{recipe, found, min, max} => { + ArgumentCountMismatch{recipe, found, min, max} => { if min == max { let expected = min; write!(f, "Recipe `{}` got {} argument{} but {}takes {}", @@ -1250,16 +1252,16 @@ impl<'a> Display for RunError<'a> { recipe, found, maybe_s(found), max)?; } }, - RunError::Code{recipe, code} => { + Code{recipe, code} => { write!(f, "Recipe `{}` failed with exit code {}", recipe, code)?; }, - RunError::Signal{recipe, signal} => { + Signal{recipe, signal} => { write!(f, "Recipe `{}` wast terminated by signal {}", recipe, signal)?; } - RunError::UnknownFailure{recipe} => { + UnknownFailure{recipe} => { write!(f, "Recipe `{}` failed for an unknown reason", recipe)?; }, - RunError::IoError{recipe, ref io_error} => { + IoError{recipe, ref io_error} => { match io_error.kind() { io::ErrorKind::NotFound => write!(f, "Recipe `{}` could not be run because just could not find `sh` the command:\n{}", @@ -1271,23 +1273,23 @@ impl<'a> Display for RunError<'a> { launching `sh`:\n{}", recipe, io_error), }?; }, - RunError::TmpdirIoError{recipe, ref io_error} => + TmpdirIoError{recipe, ref io_error} => write!(f, "Recipe `{}` could not be run because of an IO error while trying \ to create a temporary directory or write a file to that directory`:\n{}", recipe, io_error)?, - RunError::BacktickCode{code, ref token} => { + BacktickCode{code, ref token} => { write!(f, "backtick failed with exit code {}\n", code)?; error_token = Some(token); } - RunError::BacktickSignal{ref token, signal} => { + BacktickSignal{ref token, signal} => { write!(f, "backtick was terminated by signal {}", signal)?; error_token = Some(token); } - RunError::BacktickUnknownFailure{ref token} => { + BacktickUnknownFailure{ref token} => { write!(f, "backtick failed for an uknown reason")?; error_token = Some(token); } - RunError::BacktickIoError{ref token, ref io_error} => { + BacktickIoError{ref token, ref io_error} => { match io_error.kind() { io::ErrorKind::NotFound => write!( f, "backtick could not be run because just could not find `sh` the command:\n{}", @@ -1299,11 +1301,11 @@ impl<'a> Display for RunError<'a> { }?; error_token = Some(token); } - RunError::BacktickUtf8Error{ref token, ref utf8_error} => { + BacktickUtf8Error{ref token, ref utf8_error} => { write!(f, "backtick succeeded but stdout was not utf8: {}", utf8_error)?; error_token = Some(token); } - RunError::InternalError{ref message} => { + InternalError{ref message} => { write!(f, "internal error, this may indicate a bug in just: {} \ consider filing an issue: https://github.com/casey/just/issues/new", message)?; @@ -1332,8 +1334,8 @@ struct Token<'a> { } impl<'a> Token<'a> { - fn error(&self, kind: ErrorKind<'a>) -> Error<'a> { - Error { + fn error(&self, kind: ErrorKind<'a>) -> CompileError<'a> { + CompileError { text: self.text, index: self.index + self.prefix.len(), line: self.line, @@ -1397,7 +1399,7 @@ fn token(pattern: &str) -> Regex { re(&s) } -fn tokenize(text: &str) -> Result, Error> { +fn tokenize(text: &str) -> Result, CompileError> { lazy_static! { static ref BACKTICK: Regex = token(r"`[^`\n\r]*`" ); static ref COLON: Regex = token(r":" ); @@ -1439,7 +1441,7 @@ fn tokenize(text: &str) -> Result, Error> { macro_rules! error { ($kind:expr) => {{ - Err(Error { + Err(CompileError { text: text, index: index, line: line, @@ -1644,7 +1646,7 @@ fn tokenize(text: &str) -> Result, Error> { Ok(tokens) } -fn parse(text: &str) -> Result { +fn parse(text: &str) -> Result { let tokens = tokenize(text)?; let filtered: Vec<_> = tokens.into_iter().filter(|token| token.kind != Comment).collect(); let parser = Parser { @@ -1716,14 +1718,14 @@ impl<'a> Parser<'a> { } } - fn unexpected_token(&self, found: &Token<'a>, expected: &[TokenKind]) -> Error<'a> { + fn unexpected_token(&self, found: &Token<'a>, expected: &[TokenKind]) -> CompileError<'a> { found.error(ErrorKind::UnexpectedToken { expected: expected.to_vec(), found: found.kind, }) } - fn recipe(&mut self, name: Token<'a>) -> Result<(), Error<'a>> { + fn recipe(&mut self, name: Token<'a>) -> Result<(), CompileError<'a>> { if let Some(recipe) = self.recipes.get(name.lexeme) { return Err(name.error(ErrorKind::DuplicateRecipe { recipe: recipe.name, @@ -1851,7 +1853,7 @@ impl<'a> Parser<'a> { Ok(()) } - fn expression(&mut self, interpolation: bool) -> Result, Error<'a>> { + fn expression(&mut self, interpolation: bool) -> Result, CompileError<'a>> { let first = self.tokens.next().unwrap(); let lhs = match first.kind { Name => Expression::Variable {name: first.lexeme, token: first}, @@ -1881,7 +1883,7 @@ impl<'a> Parser<'a> { } } - fn assignment(&mut self, name: Token<'a>, export: bool) -> Result<(), Error<'a>> { + fn assignment(&mut self, name: Token<'a>, export: bool) -> Result<(), CompileError<'a>> { if self.assignments.contains_key(name.lexeme) { return Err(name.error(ErrorKind::DuplicateVariable {variable: name.lexeme})); } @@ -1894,7 +1896,7 @@ impl<'a> Parser<'a> { Ok(()) } - fn file(mut self) -> Result, Error<'a>> { + fn file(mut self) -> Result, CompileError<'a>> { loop { match self.tokens.next() { Some(token) => match token.kind { @@ -1918,7 +1920,7 @@ impl<'a> Parser<'a> { })), _ => return return Err(self.unexpected_token(&token, &[Name])), }, - None => return Err(Error { + None => return Err(CompileError { text: self.text, index: 0, line: 0, diff --git a/src/unit.rs b/src/unit.rs index f3df446..b36a194 100644 --- a/src/unit.rs +++ b/src/unit.rs @@ -1,7 +1,7 @@ extern crate tempdir; extern crate brev; -use super::{Token, Error, ErrorKind, Justfile, RunError, RunOptions}; +use super::{Token, CompileError, ErrorKind, Justfile, RunError, RunOptions}; use super::TokenKind::*; fn tokenize_success(text: &str, expected_summary: &str) { @@ -19,7 +19,7 @@ fn tokenize_success(text: &str, expected_summary: &str) { assert_eq!(text, roundtrip); } -fn tokenize_error(text: &str, expected: Error) { +fn tokenize_error(text: &str, expected: CompileError) { if let Err(error) = super::tokenize(text) { assert_eq!(error.text, expected.text); assert_eq!(error.index, expected.index); @@ -72,7 +72,7 @@ fn parse_summary(input: &str, output: &str) { } } -fn parse_error(text: &str, expected: Error) { +fn parse_error(text: &str, expected: CompileError) { if let Err(error) = super::parse(text) { assert_eq!(error.text, expected.text); assert_eq!(error.index, expected.index); @@ -192,7 +192,7 @@ fn tokenize_space_then_tab() { 1 \t2 "; - tokenize_error(text, Error { + tokenize_error(text, CompileError { text: text, index: 9, line: 3, @@ -209,7 +209,7 @@ fn tokenize_tabs_then_tab_space() { \t\t 1 \t 2 "; - tokenize_error(text, Error { + tokenize_error(text, CompileError { text: text, index: 12, line: 3, @@ -222,7 +222,7 @@ fn tokenize_tabs_then_tab_space() { #[test] fn tokenize_outer_shebang() { let text = "#!/usr/bin/env bash"; - tokenize_error(text, Error { + tokenize_error(text, CompileError { text: text, index: 0, line: 0, @@ -235,7 +235,7 @@ fn tokenize_outer_shebang() { #[test] fn tokenize_unknown() { let text = "~"; - tokenize_error(text, Error { + tokenize_error(text, CompileError { text: text, index: 0, line: 0, @@ -382,7 +382,7 @@ r#"a: #[test] fn missing_colon() { let text = "a b c\nd e f"; - parse_error(text, Error { + parse_error(text, CompileError { text: text, index: 5, line: 0, @@ -395,7 +395,7 @@ fn missing_colon() { #[test] fn missing_default_eol() { let text = "hello arg=\n"; - parse_error(text, Error { + parse_error(text, CompileError { text: text, index: 10, line: 0, @@ -408,7 +408,7 @@ fn missing_default_eol() { #[test] fn missing_default_eof() { let text = "hello arg="; - parse_error(text, Error { + parse_error(text, CompileError { text: text, index: 10, line: 0, @@ -421,7 +421,7 @@ fn missing_default_eof() { #[test] fn missing_default_colon() { let text = "hello arg=:"; - parse_error(text, Error { + parse_error(text, CompileError { text: text, index: 10, line: 0, @@ -434,7 +434,7 @@ fn missing_default_colon() { #[test] fn missing_default_backtick() { let text = "hello arg=`hello`"; - parse_error(text, Error { + parse_error(text, CompileError { text: text, index: 10, line: 0, @@ -447,7 +447,7 @@ fn missing_default_backtick() { #[test] fn required_after_default() { let text = "hello arg='foo' bar:"; - parse_error(text, Error { + parse_error(text, CompileError { text: text, index: 16, line: 0, @@ -460,7 +460,7 @@ fn required_after_default() { #[test] fn missing_eol() { let text = "a b c: z ="; - parse_error(text, Error { + parse_error(text, CompileError { text: text, index: 9, line: 0, @@ -478,7 +478,7 @@ fn eof_test() { #[test] fn duplicate_parameter() { let text = "a b b:"; - parse_error(text, Error { + parse_error(text, CompileError { text: text, index: 4, line: 0, @@ -491,7 +491,7 @@ fn duplicate_parameter() { #[test] fn parameter_shadows_varible() { let text = "foo = \"h\"\na foo:"; - parse_error(text, Error { + parse_error(text, CompileError { text: text, index: 12, line: 1, @@ -504,7 +504,7 @@ fn parameter_shadows_varible() { #[test] fn dependency_has_parameters() { let text = "foo arg:\nb: foo"; - parse_error(text, Error { + parse_error(text, CompileError { text: text, index: 12, line: 1, @@ -517,7 +517,7 @@ fn dependency_has_parameters() { #[test] fn duplicate_dependency() { let text = "a b c: b c z z"; - parse_error(text, Error { + parse_error(text, CompileError { text: text, index: 13, line: 0, @@ -530,7 +530,7 @@ fn duplicate_dependency() { #[test] fn duplicate_recipe() { let text = "a:\nb:\na:"; - parse_error(text, Error { + parse_error(text, CompileError { text: text, index: 6, line: 2, @@ -543,7 +543,7 @@ fn duplicate_recipe() { #[test] fn circular_recipe_dependency() { let text = "a: b\nb: a"; - parse_error(text, Error { + parse_error(text, CompileError { text: text, index: 8, line: 1, @@ -556,7 +556,7 @@ fn circular_recipe_dependency() { #[test] fn circular_variable_dependency() { let text = "a = b\nb = a"; - parse_error(text, Error { + parse_error(text, CompileError { text: text, index: 0, line: 0, @@ -569,7 +569,7 @@ fn circular_variable_dependency() { #[test] fn duplicate_variable() { let text = "a = \"0\"\na = \"0\""; - parse_error(text, Error { + parse_error(text, CompileError { text: text, index: 8, line: 1, @@ -582,7 +582,7 @@ fn duplicate_variable() { #[test] fn unterminated_string() { let text = r#"a = ""#; - parse_error(text, Error { + parse_error(text, CompileError { text: text, index: 3, line: 0, @@ -595,7 +595,7 @@ fn unterminated_string() { #[test] fn unterminated_string_with_escapes() { let text = r#"a = "\n\t\r\"\\"#; - parse_error(text, Error { + parse_error(text, CompileError { text: text, index: 3, line: 0, @@ -634,7 +634,7 @@ fn parameters() { #[test] fn self_recipe_dependency() { let text = "a: a"; - parse_error(text, Error { + parse_error(text, CompileError { text: text, index: 3, line: 0, @@ -647,7 +647,7 @@ fn self_recipe_dependency() { #[test] fn self_variable_dependency() { let text = "a = a"; - parse_error(text, Error { + parse_error(text, CompileError { text: text, index: 0, line: 0, @@ -660,7 +660,7 @@ fn self_variable_dependency() { #[test] fn unknown_dependency() { let text = "a: b"; - parse_error(text, Error { + parse_error(text, CompileError { text: text, index: 3, line: 0, @@ -673,7 +673,7 @@ fn unknown_dependency() { #[test] fn mixed_leading_whitespace() { let text = "a:\n\t echo hello"; - parse_error(text, Error { + parse_error(text, CompileError { text: text, index: 3, line: 1, @@ -724,7 +724,7 @@ fn extra_whitespace() { // we might want to make extra leading whitespace a line continuation in the future, // so make it a error for now let text = "a:\n blah\n blarg"; - parse_error(text, Error { + parse_error(text, CompileError { text: text, index: 10, line: 2, @@ -740,7 +740,7 @@ fn extra_whitespace() { #[test] fn interpolation_outside_of_recipe() { let text = "{{"; - parse_error(text, Error { + parse_error(text, CompileError { text: text, index: 0, line: 0, @@ -753,7 +753,7 @@ fn interpolation_outside_of_recipe() { #[test] fn unclosed_interpolation_delimiter() { let text = "a:\n echo {{ foo"; - parse_error(text, Error { + parse_error(text, CompileError { text: text, index: 15, line: 1, @@ -766,7 +766,7 @@ fn unclosed_interpolation_delimiter() { #[test] fn unknown_expression_variable() { let text = "x = yy"; - parse_error(text, Error { + parse_error(text, CompileError { text: text, index: 4, line: 0, @@ -779,7 +779,7 @@ fn unknown_expression_variable() { #[test] fn unknown_interpolation_variable() { let text = "x:\n {{ hello}}"; - parse_error(text, Error { + parse_error(text, CompileError { text: text, index: 9, line: 1, @@ -792,7 +792,7 @@ fn unknown_interpolation_variable() { #[test] fn unknown_second_interpolation_variable() { let text = "wtf=\"x\"\nx:\n echo\n foo {{wtf}} {{ lol }}"; - parse_error(text, Error { + parse_error(text, CompileError { text: text, index: 33, line: 3,