Add context to deprecation warnings (#473)

Previously, warnings upon encountering a deprecated use `=` in
assignments, exports, and aliases would print a message without any
indication of where the offending `=` was. This diff adds a proper
`Warning` enum, and uses it to report context, as is done with
compilation and runtime errors.
This commit is contained in:
Casey Rodarmor 2019-09-21 18:53:30 -07:00 committed by GitHub
parent 1cb90f4e65
commit 1230af138c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 99 additions and 35 deletions

View File

@ -46,7 +46,7 @@ pub(crate) use crate::{
recipe_context::RecipeContext, recipe_resolver::RecipeResolver, runtime_error::RuntimeError, recipe_context::RecipeContext, recipe_resolver::RecipeResolver, runtime_error::RuntimeError,
search_error::SearchError, shebang::Shebang, state::State, string_literal::StringLiteral, search_error::SearchError, shebang::Shebang, state::State, string_literal::StringLiteral,
token::Token, token_kind::TokenKind, use_color::UseColor, variables::Variables, token::Token, token_kind::TokenKind, use_color::UseColor, variables::Variables,
verbosity::Verbosity, verbosity::Verbosity, warning::Warning,
}; };
pub(crate) type CompilationResult<'a, T> = Result<T, CompilationError<'a>>; pub(crate) type CompilationResult<'a, T> = Result<T, CompilationError<'a>>;

View File

@ -1,6 +1,6 @@
use crate::common::*; use crate::common::*;
use crate::misc::{maybe_s, show_whitespace, write_error_context, Or}; use crate::misc::{maybe_s, show_whitespace, write_message_context, Or};
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub(crate) struct CompilationError<'a> { pub(crate) struct CompilationError<'a> {
@ -211,8 +211,9 @@ impl<'a> Display for CompilationError<'a> {
write!(f, "{}", message.suffix())?; write!(f, "{}", message.suffix())?;
write_error_context( write_message_context(
f, f,
Color::fmt(f).error(),
self.text, self.text,
self.offset, self.offset,
self.line, self.line,

View File

@ -6,7 +6,7 @@ pub(crate) struct Justfile<'a> {
pub(crate) assignments: BTreeMap<&'a str, Expression<'a>>, pub(crate) assignments: BTreeMap<&'a str, Expression<'a>>,
pub(crate) exports: BTreeSet<&'a str>, pub(crate) exports: BTreeSet<&'a str>,
pub(crate) aliases: BTreeMap<&'a str, Alias<'a>>, pub(crate) aliases: BTreeMap<&'a str, Alias<'a>>,
pub(crate) deprecated_equals: bool, pub(crate) warnings: Vec<Warning<'a>>,
} }
impl<'a> Justfile<'a> where { impl<'a> Justfile<'a> where {

View File

@ -56,6 +56,7 @@ mod token_kind;
mod use_color; mod use_color;
mod variables; mod variables;
mod verbosity; mod verbosity;
mod warning;
pub use crate::run::run; pub use crate::run::run;

View File

@ -55,8 +55,9 @@ pub(crate) fn conjoin<T: Display>(
Ok(()) Ok(())
} }
pub(crate) fn write_error_context( pub(crate) fn write_message_context(
f: &mut Formatter, f: &mut Formatter,
color: Color,
text: &str, text: &str,
offset: usize, offset: usize,
line: usize, line: usize,
@ -66,7 +67,6 @@ pub(crate) fn write_error_context(
let width = if width == 0 { 1 } else { width }; let width = if width == 0 { 1 } else { width };
let line_number = line.ordinal(); let line_number = line.ordinal();
let red = Color::fmt(f).error();
match text.lines().nth(line) { match text.lines().nth(line) {
Some(line) => { Some(line) => {
let mut i = 0; let mut i = 0;
@ -102,10 +102,10 @@ pub(crate) fn write_error_context(
" {0:1$}{2}{3:^<4$}{5}", " {0:1$}{2}{3:^<4$}{5}",
"", "",
space_column, space_column,
red.prefix(), color.prefix(),
"", "",
space_width, space_width,
red.suffix() color.suffix()
)?; )?;
} }
None => { None => {

View File

@ -12,7 +12,7 @@ pub(crate) struct Parser<'a> {
exports: BTreeSet<&'a str>, exports: BTreeSet<&'a str>,
aliases: BTreeMap<&'a str, Alias<'a>>, aliases: BTreeMap<&'a str, Alias<'a>>,
alias_tokens: BTreeMap<&'a str, Token<'a>>, alias_tokens: BTreeMap<&'a str, Token<'a>>,
deprecated_equals: bool, warnings: Vec<Warning<'a>>,
} }
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
@ -32,7 +32,7 @@ impl<'a> Parser<'a> {
exports: empty(), exports: empty(),
aliases: empty(), aliases: empty(),
alias_tokens: empty(), alias_tokens: empty(),
deprecated_equals: false, warnings: Vec::new(),
text, text,
} }
} }
@ -421,8 +421,10 @@ impl<'a> Parser<'a> {
Name => { Name => {
if token.lexeme() == "export" { if token.lexeme() == "export" {
let next = self.tokens.next().unwrap(); let next = self.tokens.next().unwrap();
if next.kind == Name && self.accepted(Equals) { if next.kind == Name && self.peek(Equals) {
self.deprecated_equals = true; self.warnings.push(Warning::DeprecatedEquals {
equals: self.tokens.next().unwrap(),
});
self.assignment(next, true)?; self.assignment(next, true)?;
doc = None; doc = None;
} else if next.kind == Name && self.accepted(ColonEquals) { } else if next.kind == Name && self.accepted(ColonEquals) {
@ -435,8 +437,10 @@ impl<'a> Parser<'a> {
} }
} else if token.lexeme() == "alias" { } else if token.lexeme() == "alias" {
let next = self.tokens.next().unwrap(); let next = self.tokens.next().unwrap();
if next.kind == Name && self.accepted(Equals) { if next.kind == Name && self.peek(Equals) {
self.deprecated_equals = true; self.warnings.push(Warning::DeprecatedEquals {
equals: self.tokens.next().unwrap(),
});
self.alias(next)?; self.alias(next)?;
doc = None; doc = None;
} else if next.kind == Name && self.accepted(ColonEquals) { } else if next.kind == Name && self.accepted(ColonEquals) {
@ -447,8 +451,10 @@ impl<'a> Parser<'a> {
self.recipe(&token, doc, false)?; self.recipe(&token, doc, false)?;
doc = None; doc = None;
} }
} else if self.accepted(Equals) { } else if self.peek(Equals) {
self.deprecated_equals = true; self.warnings.push(Warning::DeprecatedEquals {
equals: self.tokens.next().unwrap(),
});
self.assignment(token, false)?; self.assignment(token, false)?;
doc = None; doc = None;
} else if self.accepted(ColonEquals) { } else if self.accepted(ColonEquals) {
@ -515,7 +521,7 @@ impl<'a> Parser<'a> {
assignments: self.assignments, assignments: self.assignments,
exports: self.exports, exports: self.exports,
aliases: self.aliases, aliases: self.aliases,
deprecated_equals: self.deprecated_equals, warnings: self.warnings,
}) })
} }
} }

View File

@ -318,22 +318,12 @@ pub fn run() {
} }
}); });
if justfile.deprecated_equals { for warning in &justfile.warnings {
let warning = color.warning().stderr(); if color.stderr().active() {
let message = color.message().stderr(); eprintln!("{:#}", warning);
} else {
eprintln!( eprintln!("{}", warning);
"{}", }
warning.paint(
"warning: `=` in assignments, exports, and aliases is being phased out on favor of `:=`"
)
);
eprintln!(
"{}",
message
.paint("Please see this issue for more details: https://github.com/casey/just/issues/379")
);
} }
if matches.is_present("SUMMARY") { if matches.is_present("SUMMARY") {

View File

@ -1,6 +1,6 @@
use crate::common::*; use crate::common::*;
use crate::misc::{maybe_s, ticks, write_error_context, And, Or, Tick}; use crate::misc::{maybe_s, ticks, write_message_context, And, Or, Tick};
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum RuntimeError<'a> { pub(crate) enum RuntimeError<'a> {
@ -387,8 +387,9 @@ impl<'a> Display for RuntimeError<'a> {
write!(f, "{}", message.suffix())?; write!(f, "{}", message.suffix())?;
if let Some(token) = error_token { if let Some(token) = error_token {
write_error_context( write_message_context(
f, f,
Color::fmt(f).error(),
token.text, token.text,
token.offset, token.offset,
token.line, token.line,

56
src/warning.rs Normal file
View File

@ -0,0 +1,56 @@
use crate::common::*;
use crate::misc::write_message_context;
use Warning::*;
#[derive(Debug)]
pub(crate) enum Warning<'a> {
DeprecatedEquals { equals: Token<'a> },
}
impl Warning<'_> {
fn context(&self) -> Option<&Token> {
match self {
DeprecatedEquals { equals } => Some(equals),
}
}
}
impl Display for Warning<'_> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let warning = Color::fmt(f).warning();
let message = Color::fmt(f).message();
write!(f, "{} {}", warning.paint("warning:"), message.prefix())?;
match self {
DeprecatedEquals { .. } => {
writeln!(
f,
"`=` in assignments, exports, and aliases is being phased out on favor of `:=`"
)?;
write!(
f,
"Please see this issue for more details: https://github.com/casey/just/issues/379"
)?;
}
}
write!(f, "{}", message.suffix())?;
if let Some(token) = self.context() {
writeln!(f)?;
write_message_context(
f,
Color::fmt(f).warning(),
token.text,
token.offset,
token.line,
token.column,
token.lexeme().len(),
)?;
}
Ok(())
}
}

View File

@ -2414,6 +2414,9 @@ default:
stdout: "bar\n", stdout: "bar\n",
stderr: "warning: `=` in assignments, exports, and aliases is being phased out on favor of `:=` stderr: "warning: `=` in assignments, exports, and aliases is being phased out on favor of `:=`
Please see this issue for more details: https://github.com/casey/just/issues/379 Please see this issue for more details: https://github.com/casey/just/issues/379
|
3 | foo = 'bar'
| ^
echo bar echo bar
", ",
status: EXIT_SUCCESS, status: EXIT_SUCCESS,
@ -2434,6 +2437,9 @@ default:
stdout: "bar\n", stdout: "bar\n",
stderr: "warning: `=` in assignments, exports, and aliases is being phased out on favor of `:=` stderr: "warning: `=` in assignments, exports, and aliases is being phased out on favor of `:=`
Please see this issue for more details: https://github.com/casey/just/issues/379 Please see this issue for more details: https://github.com/casey/just/issues/379
|
3 | export FOO = 'bar'
| ^
echo $FOO echo $FOO
", ",
status: EXIT_SUCCESS, status: EXIT_SUCCESS,
@ -2454,6 +2460,9 @@ default:
stdout: "default\n", stdout: "default\n",
stderr: "warning: `=` in assignments, exports, and aliases is being phased out on favor of `:=` stderr: "warning: `=` in assignments, exports, and aliases is being phased out on favor of `:=`
Please see this issue for more details: https://github.com/casey/just/issues/379 Please see this issue for more details: https://github.com/casey/just/issues/379
|
3 | alias foo = default
| ^
echo default echo default
", ",
status: EXIT_SUCCESS, status: EXIT_SUCCESS,