mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-04 20:28:02 +00:00
Resolve TODO around handling non-plain strings
This commit is contained in:
parent
e0ef01fa82
commit
61bc0b3464
7 changed files with 168 additions and 28 deletions
|
@ -2453,38 +2453,33 @@ fn canonicalize_pending_value_def<'a>(
|
|||
}
|
||||
}
|
||||
IngestedFile(loc_pattern, opt_loc_ann, path_literal) => {
|
||||
let relative_path =
|
||||
if let ast::StrLiteral::PlainLine(ingested_path) = path_literal.value {
|
||||
ingested_path
|
||||
} else {
|
||||
todo!(
|
||||
"Only plain strings are supported. Other cases should be made impossible here"
|
||||
);
|
||||
};
|
||||
let expr = if let Some(relative_path) = extract_str_literal(env, path_literal) {
|
||||
let mut file_path: PathBuf = env.module_path.into();
|
||||
// Remove the header file name and push the new path.
|
||||
file_path.pop();
|
||||
file_path.push(relative_path);
|
||||
|
||||
let mut file_path: PathBuf = env.module_path.into();
|
||||
// Remove the header file name and push the new path.
|
||||
file_path.pop();
|
||||
file_path.push(relative_path);
|
||||
let mut bytes = vec![];
|
||||
|
||||
let mut bytes = vec![];
|
||||
match fs::File::open(&file_path).and_then(|mut file| file.read_to_end(&mut bytes)) {
|
||||
Ok(_) => {
|
||||
Expr::IngestedFile(file_path.into(), Arc::new(bytes), var_store.fresh())
|
||||
}
|
||||
Err(e) => {
|
||||
env.problems.push(Problem::FileProblem {
|
||||
filename: file_path.to_path_buf(),
|
||||
error: e.kind(),
|
||||
});
|
||||
|
||||
let expr = match fs::File::open(&file_path)
|
||||
.and_then(|mut file| file.read_to_end(&mut bytes))
|
||||
{
|
||||
Ok(_) => Expr::IngestedFile(file_path.into(), Arc::new(bytes), var_store.fresh()),
|
||||
Err(e) => {
|
||||
env.problems.push(Problem::FileProblem {
|
||||
filename: file_path.to_path_buf(),
|
||||
error: e.kind(),
|
||||
});
|
||||
|
||||
Expr::RuntimeError(RuntimeError::ReadIngestedFileError {
|
||||
filename: file_path.to_path_buf(),
|
||||
error: e.kind(),
|
||||
region: path_literal.region,
|
||||
})
|
||||
Expr::RuntimeError(RuntimeError::ReadIngestedFileError {
|
||||
filename: file_path.to_path_buf(),
|
||||
error: e.kind(),
|
||||
region: path_literal.region,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Expr::RuntimeError(RuntimeError::IngestedFilePathError(path_literal.region))
|
||||
};
|
||||
|
||||
let loc_expr = Loc::at(path_literal.region, expr);
|
||||
|
@ -2539,6 +2534,68 @@ fn canonicalize_pending_value_def<'a>(
|
|||
output
|
||||
}
|
||||
|
||||
fn extract_str_literal<'a>(
|
||||
env: &mut Env<'a>,
|
||||
literal: Loc<ast::StrLiteral<'a>>,
|
||||
) -> Option<&'a str> {
|
||||
let relative_path = match literal.value {
|
||||
ast::StrLiteral::PlainLine(ingested_path) => ingested_path,
|
||||
ast::StrLiteral::Line(text) => {
|
||||
let mut result_text = bumpalo::collections::String::new_in(env.arena);
|
||||
if !extract_str_segments(env, text, &mut result_text) {
|
||||
return None;
|
||||
}
|
||||
result_text.into_bump_str()
|
||||
}
|
||||
ast::StrLiteral::Block(lines) => {
|
||||
let mut result_text = bumpalo::collections::String::new_in(env.arena);
|
||||
for text in lines {
|
||||
if !extract_str_segments(env, text, &mut result_text) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
result_text.into_bump_str()
|
||||
}
|
||||
};
|
||||
Some(relative_path)
|
||||
}
|
||||
|
||||
fn extract_str_segments<'a>(
|
||||
env: &mut Env<'a>,
|
||||
segments: &[ast::StrSegment<'a>],
|
||||
result_text: &mut bumpalo::collections::String<'a>,
|
||||
) -> bool {
|
||||
for segment in segments.iter() {
|
||||
match segment {
|
||||
ast::StrSegment::Plaintext(t) => {
|
||||
result_text.push_str(t);
|
||||
}
|
||||
ast::StrSegment::Unicode(t) => {
|
||||
let hex_code: &str = t.value;
|
||||
if let Some(c) = u32::from_str_radix(hex_code, 16)
|
||||
.ok()
|
||||
.and_then(char::from_u32)
|
||||
{
|
||||
result_text.push(c);
|
||||
} else {
|
||||
env.problem(Problem::InvalidUnicodeCodePt(t.region));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
ast::StrSegment::EscapedChar(c) => {
|
||||
result_text.push(c.unescape());
|
||||
}
|
||||
ast::StrSegment::Interpolated(e) => {
|
||||
// TODO: maybe in the future we do want to allow building up the path with local bindings?
|
||||
// This would require an interpreter tho; so for now we just disallow it.
|
||||
env.problem(Problem::InterpolatedStringNotAllowed(e.region));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
// TODO trim down these arguments!
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
|
|
|
@ -263,6 +263,7 @@ pub enum Problem {
|
|||
field_region: Region,
|
||||
record_region: Region,
|
||||
},
|
||||
InterpolatedStringNotAllowed(Region),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
|
@ -280,6 +281,7 @@ impl Problem {
|
|||
Problem::UnusedDef(_, _) => Warning,
|
||||
Problem::UnusedImport(_, _) => Warning,
|
||||
Problem::UnusedModuleImport(_, _) => Warning,
|
||||
Problem::InterpolatedStringNotAllowed(_) => RuntimeError,
|
||||
Problem::ImportNameConflict { .. } => RuntimeError,
|
||||
Problem::ExplicitBuiltinImport(_, _) => Warning,
|
||||
Problem::ExplicitBuiltinTypeImport(_, _) => Warning,
|
||||
|
@ -376,6 +378,7 @@ impl Problem {
|
|||
..
|
||||
}
|
||||
| Problem::ExplicitBuiltinImport(_, region)
|
||||
| Problem::InterpolatedStringNotAllowed(region)
|
||||
| Problem::ExplicitBuiltinTypeImport(_, region)
|
||||
| Problem::ImportShadowsSymbol { region, .. }
|
||||
| Problem::UnusedArgument(_, _, _, region)
|
||||
|
@ -687,6 +690,7 @@ pub enum RuntimeError {
|
|||
|
||||
NonFunctionHostedAnnotation(Region),
|
||||
InvalidTupleIndex(Region),
|
||||
IngestedFilePathError(Region),
|
||||
}
|
||||
|
||||
impl RuntimeError {
|
||||
|
@ -725,6 +729,7 @@ impl RuntimeError {
|
|||
| RuntimeError::InvalidInt(_, _, region, _)
|
||||
| RuntimeError::EmptySingleQuote(region)
|
||||
| RuntimeError::InvalidTupleIndex(region)
|
||||
| RuntimeError::IngestedFilePathError(region)
|
||||
| RuntimeError::MultipleCharsInSingleQuote(region)
|
||||
| RuntimeError::DegenerateBranch(region)
|
||||
| RuntimeError::InvalidInterpolation(region)
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
import "\\" as m
|
||||
e
|
|
@ -0,0 +1,55 @@
|
|||
@0-16 SpaceAfter(
|
||||
Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
EitherIndex(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-14,
|
||||
],
|
||||
space_before: [
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
IngestedFileImport(
|
||||
IngestedFileImport {
|
||||
before_path: [],
|
||||
path: @6-10 Line(
|
||||
[
|
||||
EscapedChar(
|
||||
Backslash,
|
||||
),
|
||||
],
|
||||
),
|
||||
name: KeywordItem {
|
||||
keyword: Spaces {
|
||||
before: [],
|
||||
item: ImportAsKeyword,
|
||||
after: [],
|
||||
},
|
||||
item: @13-14 "m",
|
||||
},
|
||||
annotation: None,
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
@15-16 SpaceBefore(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "e",
|
||||
},
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
)
|
|
@ -0,0 +1,2 @@
|
|||
import"\\"as m
|
||||
e
|
|
@ -473,6 +473,7 @@ mod test_snapshots {
|
|||
pass/implements_not_keyword.expr,
|
||||
pass/implements_record_destructure.expr,
|
||||
pass/import.moduledefs,
|
||||
pass/import_backslash_as_m.expr,
|
||||
pass/import_from_package.moduledefs,
|
||||
pass/import_in_closure_with_curlies_after.expr,
|
||||
pass/import_with_alias.moduledefs,
|
||||
|
|
|
@ -67,6 +67,7 @@ const MISSING_EXCLAMATION: &str = "MISSING EXCLAMATION";
|
|||
const UNNECESSARY_EXCLAMATION: &str = "UNNECESSARY EXCLAMATION";
|
||||
const EMPTY_TUPLE_TYPE: &str = "EMPTY TUPLE TYPE";
|
||||
const UNBOUND_TYPE_VARS_IN_AS: &str = "UNBOUND TYPE VARIABLES IN AS";
|
||||
const INTERPOLATED_STRING_NOT_ALLOWED: &str = "INTERPOLATED STRING NOT ALLOWED";
|
||||
|
||||
pub fn can_problem<'b>(
|
||||
alloc: &'b RocDocAllocator<'b>,
|
||||
|
@ -1478,6 +1479,15 @@ pub fn can_problem<'b>(
|
|||
|
||||
title = UNBOUND_TYPE_VARS_IN_AS.to_string();
|
||||
}
|
||||
Problem::InterpolatedStringNotAllowed(region) => {
|
||||
doc = alloc.stack([
|
||||
alloc.reflow("Interpolated strings are not allowed here:"),
|
||||
alloc.region(lines.convert_region(region), severity),
|
||||
alloc.reflow(r#"Only plain strings like "foo" or "foo\n" are allowed."#),
|
||||
]);
|
||||
|
||||
title = INTERPOLATED_STRING_NOT_ALLOWED.to_string();
|
||||
}
|
||||
};
|
||||
|
||||
Report {
|
||||
|
@ -2249,6 +2259,14 @@ fn pretty_runtime_error<'b>(
|
|||
doc = report.doc;
|
||||
title = INGESTED_FILE_ERROR;
|
||||
}
|
||||
RuntimeError::IngestedFilePathError(region) => {
|
||||
doc = alloc.stack([
|
||||
alloc.reflow(r"I tried to read this file, but something about the path is wrong:"),
|
||||
alloc.region(lines.convert_region(region), severity),
|
||||
]);
|
||||
|
||||
title = INGESTED_FILE_ERROR;
|
||||
}
|
||||
RuntimeError::InvalidPrecedence(_, _) => {
|
||||
// do nothing, reported with PrecedenceProblem
|
||||
unreachable!();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue