Add canonicalization to source & use paths (#421)

Also added file path print to FileNotFound error
This commit is contained in:
Jakub Žádník 2021-12-03 21:49:11 +02:00 committed by GitHub
parent 405a4e58c7
commit ee45755ea9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 102 additions and 81 deletions

View file

@ -187,7 +187,7 @@ pub enum ParseError {
#[diagnostic(code(nu::parser::export_not_found), url(docsrs))] #[diagnostic(code(nu::parser::export_not_found), url(docsrs))]
ExportNotFound(#[label = "could not find imports"] Span), ExportNotFound(#[label = "could not find imports"] Span),
#[error("File not found")] #[error("File not found: {0}")]
#[diagnostic(code(nu::parser::file_not_found), url(docsrs))] #[diagnostic(code(nu::parser::file_not_found), url(docsrs))]
FileNotFound(String), FileNotFound(String),

View file

@ -1,3 +1,4 @@
use nu_path::canonicalize;
use nu_protocol::{ use nu_protocol::{
ast::{ ast::{
Block, Call, Expr, Expression, ImportPattern, ImportPatternHead, ImportPatternMember, Block, Call, Expr, Expression, ImportPattern, ImportPatternHead, ImportPatternMember,
@ -7,7 +8,6 @@ use nu_protocol::{
span, Exportable, Overlay, Span, SyntaxShape, Type, CONFIG_VARIABLE_ID, span, Exportable, Overlay, Span, SyntaxShape, Type, CONFIG_VARIABLE_ID,
}; };
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::path::Path;
use crate::{ use crate::{
lex, lite_parse, lex, lite_parse,
@ -679,44 +679,48 @@ pub fn parse_use(
// TODO: Do not close over when loading module from file // TODO: Do not close over when loading module from file
// It could be a file // It could be a file
if let Ok(module_filename) = String::from_utf8(import_pattern.head.name) { if let Ok(module_filename) = String::from_utf8(import_pattern.head.name) {
let module_path = Path::new(&module_filename); if let Ok(module_path) = canonicalize(&module_filename) {
let module_name = if let Some(stem) = module_path.file_stem() { let module_name = if let Some(stem) = module_path.file_stem() {
stem.to_string_lossy().to_string() stem.to_string_lossy().to_string()
} else { } else {
return ( return (
garbage_statement(spans), garbage_statement(spans),
Some(ParseError::ModuleNotFound(spans[1])), Some(ParseError::ModuleNotFound(spans[1])),
); );
}; };
if let Ok(contents) = std::fs::read(module_path) { if let Ok(contents) = std::fs::read(module_path) {
let span_start = working_set.next_span_start(); let span_start = working_set.next_span_start();
working_set.add_file(module_filename, &contents); working_set.add_file(module_filename, &contents);
let span_end = working_set.next_span_start(); let span_end = working_set.next_span_start();
let (block, overlay, err) = let (block, overlay, err) =
parse_module_block(working_set, Span::new(span_start, span_end)); parse_module_block(working_set, Span::new(span_start, span_end));
error = error.or(err); error = error.or(err);
let _ = working_set.add_block(block); let _ = working_set.add_block(block);
let _ = working_set.add_overlay(&module_name, overlay.clone()); let _ = working_set.add_overlay(&module_name, overlay.clone());
( (
ImportPattern { ImportPattern {
head: ImportPatternHead { head: ImportPatternHead {
name: module_name.into(), name: module_name.into(),
span: spans[1], span: spans[1],
},
members: import_pattern.members,
hidden: HashSet::new(),
}, },
members: import_pattern.members, overlay,
hidden: HashSet::new(), )
}, } else {
overlay, return (
) garbage_statement(spans),
Some(ParseError::ModuleNotFound(spans[1])),
);
}
} else { } else {
return ( error = error.or(Some(ParseError::FileNotFound(module_filename)));
garbage_statement(spans), (ImportPattern::new(), Overlay::new())
Some(ParseError::ModuleNotFound(spans[1])),
);
} }
} else { } else {
return ( return (
@ -990,6 +994,7 @@ pub fn parse_source(
working_set: &mut StateWorkingSet, working_set: &mut StateWorkingSet,
spans: &[Span], spans: &[Span],
) -> (Statement, Option<ParseError>) { ) -> (Statement, Option<ParseError>) {
let mut error = None;
let name = working_set.get_span_contents(spans[0]); let name = working_set.get_span_contents(spans[0]);
if name == b"source" { if name == b"source" {
@ -998,63 +1003,63 @@ pub fn parse_source(
// Some of the others (`parse_let`) use it, some of them (`parse_hide`) don't. // Some of the others (`parse_let`) use it, some of them (`parse_hide`) don't.
let (call, call_span, err) = let (call, call_span, err) =
parse_internal_call(working_set, spans[0], &spans[1..], decl_id); parse_internal_call(working_set, spans[0], &spans[1..], decl_id);
error = error.or(err);
// Command and one file name // Command and one file name
if spans.len() >= 2 { if spans.len() >= 2 {
let name_expr = working_set.get_span_contents(spans[1]); let name_expr = working_set.get_span_contents(spans[1]);
if let Ok(filename) = String::from_utf8(name_expr.to_vec()) { if let Ok(filename) = String::from_utf8(name_expr.to_vec()) {
let source_file = Path::new(&filename); if let Ok(path) = canonicalize(&filename) {
if let Ok(contents) = std::fs::read(&path) {
// This will load the defs from the file into the
// working set, if it was a successful parse.
let (block, err) = parse(
working_set,
path.file_name().and_then(|x| x.to_str()),
&contents,
false,
);
let path = source_file; if err.is_some() {
let contents = std::fs::read(path); // Unsuccessful parse of file
return (
Statement::Pipeline(Pipeline::from_vec(vec![Expression {
expr: Expr::Call(call),
span: span(&spans[1..]),
ty: Type::Unknown,
custom_completion: None,
}])),
// Return the file parse error
err,
);
} else {
// Save the block into the working set
let block_id = working_set.add_block(block);
if let Ok(contents) = contents { let mut call_with_block = call;
// This will load the defs from the file into the
// working set, if it was a successful parse.
let (block, err) = parse(
working_set,
path.file_name().and_then(|x| x.to_str()),
&contents,
false,
);
if err.is_some() { // Adding this expression to the positional creates a syntax highlighting error
// Unsuccessful parse of file // after writing `source example.nu`
return ( call_with_block.positional.push(Expression {
Statement::Pipeline(Pipeline::from_vec(vec![Expression { expr: Expr::Int(block_id as i64),
expr: Expr::Call(call), span: spans[1],
span: span(&spans[1..]),
ty: Type::Unknown, ty: Type::Unknown,
custom_completion: None, custom_completion: None,
}])), });
// Return the file parse error
err,
);
} else {
// Save the block into the working set
let block_id = working_set.add_block(block);
let mut call_with_block = call; return (
Statement::Pipeline(Pipeline::from_vec(vec![Expression {
// Adding this expression to the positional creates a syntax highlighting error expr: Expr::Call(call_with_block),
// after writing `source example.nu` span: call_span,
call_with_block.positional.push(Expression { ty: Type::Unknown,
expr: Expr::Int(block_id as i64), custom_completion: None,
span: spans[1], }])),
ty: Type::Unknown, None,
custom_completion: None, );
}); }
return (
Statement::Pipeline(Pipeline::from_vec(vec![Expression {
expr: Expr::Call(call_with_block),
span: call_span,
ty: Type::Unknown,
custom_completion: None,
}])),
None,
);
} }
} else {
error = error.or(Some(ParseError::FileNotFound(filename)));
} }
} else { } else {
return ( return (
@ -1070,7 +1075,7 @@ pub fn parse_source(
ty: Type::Unknown, ty: Type::Unknown,
custom_completion: None, custom_completion: None,
}])), }])),
err, error,
); );
} }
} }
@ -1090,7 +1095,6 @@ pub fn parse_register(
) -> (Statement, Option<ParseError>) { ) -> (Statement, Option<ParseError>) {
use std::{path::PathBuf, str::FromStr}; use std::{path::PathBuf, str::FromStr};
use nu_path::canonicalize;
use nu_plugin::plugin::{get_signature, PluginDeclaration}; use nu_plugin::plugin::{get_signature, PluginDeclaration};
use nu_protocol::Signature; use nu_protocol::Signature;

View file

@ -24,6 +24,17 @@ pub struct ImportPattern {
} }
impl ImportPattern { impl ImportPattern {
pub fn new() -> Self {
ImportPattern {
head: ImportPatternHead {
name: vec![],
span: Span::unknown(),
},
members: vec![],
hidden: HashSet::new(),
}
}
pub fn span(&self) -> Span { pub fn span(&self) -> Span {
let mut spans = vec![self.head.span]; let mut spans = vec![self.head.span];
@ -50,3 +61,9 @@ impl ImportPattern {
} }
} }
} }
impl Default for ImportPattern {
fn default() -> Self {
Self::new()
}
}