mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 05:49:08 +00:00
Feedback: add doc comments, verbiage changes, capitalize Roc, remove a resolved TODO
This commit is contained in:
parent
413de7f72e
commit
df915b936d
6 changed files with 93 additions and 12 deletions
|
@ -3374,7 +3374,7 @@ mod test_reporting {
|
||||||
f x y = x
|
f x y = x
|
||||||
"
|
"
|
||||||
),
|
),
|
||||||
@r#"
|
@r###"
|
||||||
── ARGUMENTS BEFORE EQUALS in tmp/elm_function_syntax/Test.roc ─────────────────
|
── ARGUMENTS BEFORE EQUALS in tmp/elm_function_syntax/Test.roc ─────────────────
|
||||||
|
|
||||||
I am partway through parsing a definition, but I got stuck here:
|
I am partway through parsing a definition, but I got stuck here:
|
||||||
|
@ -3385,9 +3385,9 @@ mod test_reporting {
|
||||||
4│ f x y = x
|
4│ f x y = x
|
||||||
^^^
|
^^^
|
||||||
|
|
||||||
Looks like you are trying to define a function. In roc, functions are
|
Looks like you are trying to define a function. In Roc, functions are
|
||||||
always written as a lambda, like increment = \n -> n + 1.
|
always written as a lambda, like increment = \n -> n + 1.
|
||||||
"#
|
"###
|
||||||
);
|
);
|
||||||
|
|
||||||
test_report!(
|
test_report!(
|
||||||
|
@ -5030,7 +5030,7 @@ mod test_reporting {
|
||||||
4│ import svg.Path a
|
4│ import svg.Path a
|
||||||
^
|
^
|
||||||
|
|
||||||
I was expecting to see the `as` keyword, like:
|
I was expecting to see the `as` keyword next, like:
|
||||||
|
|
||||||
import svg.Path as SvgPath
|
import svg.Path as SvgPath
|
||||||
|
|
||||||
|
|
|
@ -339,6 +339,7 @@ fn unary_negate<'a>() -> impl Parser<'a, (), EExpr<'a>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Entry point for parsing an expression.
|
||||||
fn expr_start<'a>(options: ExprParseOptions) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
|
fn expr_start<'a>(options: ExprParseOptions) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
|
||||||
one_of![
|
one_of![
|
||||||
loc(specialize_err(EExpr::If, if_expr_help(options))),
|
loc(specialize_err(EExpr::If, if_expr_help(options))),
|
||||||
|
@ -350,6 +351,7 @@ fn expr_start<'a>(options: ExprParseOptions) -> impl Parser<'a, Loc<Expr<'a>>, E
|
||||||
.trace("expr_start")
|
.trace("expr_start")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse a chain of expressions separated by operators. Also handles function application.
|
||||||
fn expr_operator_chain<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
|
fn expr_operator_chain<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
|
||||||
(move |arena, state: State<'a>, min_indent: u32| {
|
(move |arena, state: State<'a>, min_indent: u32| {
|
||||||
parse_expr_operator_chain(arena, state, min_indent, options)
|
parse_expr_operator_chain(arena, state, min_indent, options)
|
||||||
|
@ -431,6 +433,9 @@ fn parse_expr_operator_chain<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// We're part way thru parsing an expression, e.g. `bar foo `.
|
||||||
|
/// We just tried parsing an argument and determined we couldn't -
|
||||||
|
/// so we're going to try parsing an operator.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn parse_expr_after_apply<'a>(
|
fn parse_expr_after_apply<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
|
@ -1090,6 +1095,7 @@ fn extract_tag_and_spaces<'a>(arena: &'a Bump, expr: Expr<'a>) -> Option<Spaces<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// We just saw a ':' or ':=', and we're trying to parse an alias or opaque type definition.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn parse_stmt_alias_or_opaque<'a>(
|
fn parse_stmt_alias_or_opaque<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
|
@ -1331,6 +1337,7 @@ mod ability {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse the series of "demands" (e.g. similar to methods in a rust trait), for an ability definition.
|
||||||
fn finish_parsing_ability_def_help<'a>(
|
fn finish_parsing_ability_def_help<'a>(
|
||||||
call_min_indent: u32,
|
call_min_indent: u32,
|
||||||
name: Loc<&'a str>,
|
name: Loc<&'a str>,
|
||||||
|
@ -1379,6 +1386,16 @@ fn finish_parsing_ability_def_help<'a>(
|
||||||
Ok((MadeProgress, (type_def, def_region), state))
|
Ok((MadeProgress, (type_def, def_region), state))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A Stmt is an intermediate representation used only during parsing.
|
||||||
|
/// It consists of a fragment of code that hasn't been fully stitched together yet.
|
||||||
|
/// For example, each of the following lines is a Stmt:
|
||||||
|
/// - `foo bar` (Expr)
|
||||||
|
/// - `foo, bar <- baz` (Backpassing)
|
||||||
|
/// - `Foo : [A, B, C]` (TypeDef)
|
||||||
|
/// - `foo = \x -> x + 1` (ValueDef)
|
||||||
|
///
|
||||||
|
/// Note in particular that the Backpassing Stmt doesn't make any sense on its own;
|
||||||
|
/// we need to link it up with the following stmts to make a complete expression.
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum Stmt<'a> {
|
pub enum Stmt<'a> {
|
||||||
Expr(Expr<'a>),
|
Expr(Expr<'a>),
|
||||||
|
@ -1387,6 +1404,11 @@ pub enum Stmt<'a> {
|
||||||
ValueDef(ValueDef<'a>),
|
ValueDef(ValueDef<'a>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Having just parsed an operator, we need to dispatch to the appropriate
|
||||||
|
/// parsing function based on the operator.
|
||||||
|
///
|
||||||
|
/// Note, this function is very similar to `parse_expr_operator`, but it
|
||||||
|
/// handles additional cases to allow assignments / type annotations / etc.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn parse_stmt_operator<'a>(
|
fn parse_stmt_operator<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
|
@ -1462,6 +1484,8 @@ fn parse_stmt_operator<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// We just parsed an operator. Parse the expression that follows, taking special care
|
||||||
|
/// that this might be a negated term. (`-x` is a negated term, not a binary operation)
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn parse_expr_operator<'a>(
|
fn parse_expr_operator<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
|
@ -1508,6 +1532,7 @@ fn parse_expr_operator<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Continue parsing terms after we just parsed a binary operator
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn parse_after_binop<'a>(
|
fn parse_after_binop<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
|
@ -1581,6 +1606,7 @@ fn parse_after_binop<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse the rest of a backpassing statement, after the <- operator
|
||||||
fn parse_stmt_backpassing<'a>(
|
fn parse_stmt_backpassing<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
state: State<'a>,
|
state: State<'a>,
|
||||||
|
@ -1590,8 +1616,6 @@ fn parse_stmt_backpassing<'a>(
|
||||||
options: ExprParseOptions,
|
options: ExprParseOptions,
|
||||||
spaces_after_operator: &'a [CommentOrNewline],
|
spaces_after_operator: &'a [CommentOrNewline],
|
||||||
) -> ParseResult<'a, Stmt<'a>, EExpr<'a>> {
|
) -> ParseResult<'a, Stmt<'a>, EExpr<'a>> {
|
||||||
// called after parsing the <- operator
|
|
||||||
|
|
||||||
let expr_region = expr_state.expr.region;
|
let expr_region = expr_state.expr.region;
|
||||||
|
|
||||||
let call = expr_state
|
let call = expr_state
|
||||||
|
@ -1627,6 +1651,8 @@ fn parse_stmt_backpassing<'a>(
|
||||||
Ok((MadeProgress, ret, state))
|
Ok((MadeProgress, ret, state))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// We just saw a `,` that we think is part of a backpassing statement.
|
||||||
|
/// Parse the rest of the statement.
|
||||||
fn parse_stmt_multi_backpassing<'a>(
|
fn parse_stmt_multi_backpassing<'a>(
|
||||||
mut expr_state: ExprState<'a>,
|
mut expr_state: ExprState<'a>,
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
|
@ -1689,6 +1715,7 @@ fn parse_stmt_multi_backpassing<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// We just saw the '=' operator of an assignment stmt. Continue parsing from there.
|
||||||
fn parse_stmt_assignment<'a>(
|
fn parse_stmt_assignment<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
state: State<'a>,
|
state: State<'a>,
|
||||||
|
@ -1733,6 +1760,7 @@ fn parse_stmt_assignment<'a>(
|
||||||
Ok((MadeProgress, Stmt::ValueDef(value_def), state))
|
Ok((MadeProgress, Stmt::ValueDef(value_def), state))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// We just saw a unary negation operator, and now we need to parse the expression.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn parse_negated_term<'a>(
|
fn parse_negated_term<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
|
@ -1779,6 +1807,8 @@ fn parse_negated_term<'a>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse an expression, not allowing `if`/`when`/etc.
|
||||||
|
/// TODO: this should probably be subsumed into `parse_expr_operator_chain`
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn parse_expr_end<'a>(
|
fn parse_expr_end<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
|
@ -1837,6 +1867,13 @@ fn parse_expr_end<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// We're part way thru parsing an expression, e.g. `bar foo `.
|
||||||
|
/// We just tried parsing an argument and determined we couldn't -
|
||||||
|
/// so we're going to try parsing an operator.
|
||||||
|
///
|
||||||
|
/// Note that this looks a lot like `parse_expr_after_apply`, except
|
||||||
|
/// we handle the additional case of backpassing, which is valid
|
||||||
|
/// at the statement level but not at the expression level.
|
||||||
fn parse_stmt_after_apply<'a>(
|
fn parse_stmt_after_apply<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
state: State<'a>,
|
state: State<'a>,
|
||||||
|
@ -2679,6 +2716,7 @@ fn if_expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EIf<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse a block of statements (parser combinator version of `parse_block`)
|
||||||
fn block<'a, E>(
|
fn block<'a, E>(
|
||||||
options: ExprParseOptions,
|
options: ExprParseOptions,
|
||||||
require_indent: bool,
|
require_indent: bool,
|
||||||
|
@ -2701,6 +2739,11 @@ where
|
||||||
.trace("block")
|
.trace("block")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse a block of statements.
|
||||||
|
/// For example, the then and else branches of an `if` expression are both blocks.
|
||||||
|
/// There are two cases here:
|
||||||
|
/// 1. If there is a preceding newline, then the block must be indented and is allowed to have definitions.
|
||||||
|
/// 2. If there is no preceding newline, then the block must consist of a single expression (no definitions).
|
||||||
fn parse_block<'a, E>(
|
fn parse_block<'a, E>(
|
||||||
options: ExprParseOptions,
|
options: ExprParseOptions,
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
|
@ -2735,6 +2778,9 @@ where
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse a block of statements, and process that into an Expr.
|
||||||
|
/// Assumes the caller has already parsed the optional first "space" (newline),
|
||||||
|
/// and decided whether to allow definitions.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn parse_block_inner<'a, E>(
|
fn parse_block_inner<'a, E>(
|
||||||
options: ExprParseOptions,
|
options: ExprParseOptions,
|
||||||
|
@ -2797,6 +2843,15 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse a sequence of statements, which we'll later process into an expression.
|
||||||
|
/// Statements can include:
|
||||||
|
/// - assignments
|
||||||
|
/// - type annotations
|
||||||
|
/// - expressions
|
||||||
|
/// - [multi]backpassing
|
||||||
|
///
|
||||||
|
/// This function doesn't care about whether the order of those statements makes any sense.
|
||||||
|
/// e.g. it will happily parse two expressions in a row, or backpassing with nothing following it.
|
||||||
fn parse_stmt_seq<'a, E: SpaceProblem + 'a>(
|
fn parse_stmt_seq<'a, E: SpaceProblem + 'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
mut state: State<'a>,
|
mut state: State<'a>,
|
||||||
|
@ -2868,6 +2923,7 @@ fn parse_stmt_seq<'a, E: SpaceProblem + 'a>(
|
||||||
Ok((MadeProgress, stmts, state))
|
Ok((MadeProgress, stmts, state))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if the current byte is a terminator for a sequence of statements
|
||||||
fn at_terminator(state: &State<'_>) -> bool {
|
fn at_terminator(state: &State<'_>) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
state.bytes().first(),
|
state.bytes().first(),
|
||||||
|
@ -2875,6 +2931,8 @@ fn at_terminator(state: &State<'_>) -> bool {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert a sequence of statements into a `Expr::Defs` expression
|
||||||
|
/// (which is itself a Defs struct and final expr)
|
||||||
fn stmts_to_expr<'a>(
|
fn stmts_to_expr<'a>(
|
||||||
stmts: &[SpacesBefore<'a, Loc<Stmt<'a>>>],
|
stmts: &[SpacesBefore<'a, Loc<Stmt<'a>>>],
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
|
@ -2940,6 +2998,9 @@ fn stmts_to_expr<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert a sequence of `Stmt` into a Defs and an optional final expression.
|
||||||
|
/// Future refactoring opportunity: push this logic directly into where we're
|
||||||
|
/// parsing the statements.
|
||||||
fn stmts_to_defs<'a>(
|
fn stmts_to_defs<'a>(
|
||||||
stmts: &[SpacesBefore<'a, Loc<Stmt<'a>>>],
|
stmts: &[SpacesBefore<'a, Loc<Stmt<'a>>>],
|
||||||
mut defs: Defs<'a>,
|
mut defs: Defs<'a>,
|
||||||
|
@ -3133,6 +3194,7 @@ fn stmts_to_defs<'a>(
|
||||||
Ok((defs, last_expr))
|
Ok((defs, last_expr))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Given a type alias and a value definition, join them into a AnnotatedBody
|
||||||
pub fn join_alias_to_body<'a>(
|
pub fn join_alias_to_body<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
header: TypeHeader<'a>,
|
header: TypeHeader<'a>,
|
||||||
|
|
|
@ -1,3 +1,15 @@
|
||||||
|
//! Generate a minimized version of a given input, by removing parts of it.
|
||||||
|
//! This is useful for debugging, when you have a large input that causes a failure,
|
||||||
|
//! and you want to find the smallest input that still causes the failure.
|
||||||
|
//!
|
||||||
|
//! Typical usage:
|
||||||
|
//! `cargo run --release --bin minimize -- full <file_that_triggers_parsing_bug>`
|
||||||
|
//!
|
||||||
|
//! This tool will churn on that for a while, and eventually print out a minimized version
|
||||||
|
//! of the input that still triggers the bug.
|
||||||
|
//!
|
||||||
|
//! Note that `--release` is important, as this tool is very slow in debug mode.
|
||||||
|
|
||||||
use test_syntax::{minimize::print_minimizations, test_helpers::InputKind};
|
use test_syntax::{minimize::print_minimizations, test_helpers::InputKind};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
//! Generate a minimized version of a given input, by removing parts of it.
|
||||||
|
//! This is useful for debugging, when you have a large input that causes a failure,
|
||||||
|
//! and you want to find the smallest input that still causes the failure.
|
||||||
|
//!
|
||||||
|
//! Most users will want to use the binary instead of this module directly.
|
||||||
|
//! e.g. `cargo run --release --bin minimize -- full <file_that_triggers_parsing_bug>`
|
||||||
|
|
||||||
use crate::test_helpers::{Input, InputKind};
|
use crate::test_helpers::{Input, InputKind};
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_parse::{ast::Malformed, remove_spaces::RemoveSpaces};
|
use roc_parse::{ast::Malformed, remove_spaces::RemoveSpaces};
|
||||||
|
@ -79,7 +86,7 @@ fn round_trip_once(input: Input<'_>) -> Option<String> {
|
||||||
"Initial parse failed: {:?}",
|
"Initial parse failed: {:?}",
|
||||||
e.remove_spaces(&arena)
|
e.remove_spaces(&arena)
|
||||||
))
|
))
|
||||||
} // todo: strip pos info, use the error
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if actual.is_malformed() {
|
if actual.is_malformed() {
|
||||||
|
|
|
@ -1133,7 +1133,7 @@ fn parse_problem() {
|
||||||
4│ add m n = m + n
|
4│ add m n = m + n
|
||||||
^^^
|
^^^
|
||||||
|
|
||||||
Looks like you are trying to define a function. In roc, functions are
|
Looks like you are trying to define a function. In Roc, functions are
|
||||||
always written as a lambda, like increment = \n -> n + 1.
|
always written as a lambda, like increment = \n -> n + 1.
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
|
|
|
@ -203,7 +203,7 @@ fn to_expr_report<'a>(
|
||||||
alloc.region_with_subregion(lines.convert_region(surroundings), region, severity),
|
alloc.region_with_subregion(lines.convert_region(surroundings), region, severity),
|
||||||
alloc.concat([
|
alloc.concat([
|
||||||
alloc.reflow("Looks like you are trying to define a function. "),
|
alloc.reflow("Looks like you are trying to define a function. "),
|
||||||
alloc.reflow("In roc, functions are always written as a lambda, like "),
|
alloc.reflow("In Roc, functions are always written as a lambda, like "),
|
||||||
alloc.parser_suggestion("increment = \\n -> n + 1"),
|
alloc.parser_suggestion("increment = \\n -> n + 1"),
|
||||||
alloc.reflow("."),
|
alloc.reflow("."),
|
||||||
]),
|
]),
|
||||||
|
@ -257,7 +257,7 @@ fn to_expr_report<'a>(
|
||||||
Context::InDef(_pos) => {
|
Context::InDef(_pos) => {
|
||||||
vec![alloc.stack([
|
vec![alloc.stack([
|
||||||
alloc.reflow("Looks like you are trying to define a function. "),
|
alloc.reflow("Looks like you are trying to define a function. "),
|
||||||
alloc.reflow("In roc, functions are always written as a lambda, like "),
|
alloc.reflow("In Roc, functions are always written as a lambda, like "),
|
||||||
alloc
|
alloc
|
||||||
.parser_suggestion("increment = \\n -> n + 1")
|
.parser_suggestion("increment = \\n -> n + 1")
|
||||||
.indent(4),
|
.indent(4),
|
||||||
|
@ -509,7 +509,7 @@ fn to_expr_report<'a>(
|
||||||
alloc.region_with_subregion(lines.convert_region(surroundings), region, severity),
|
alloc.region_with_subregion(lines.convert_region(surroundings), region, severity),
|
||||||
alloc.concat([
|
alloc.concat([
|
||||||
alloc.reflow("Looks like you are trying to define a function. "),
|
alloc.reflow("Looks like you are trying to define a function. "),
|
||||||
alloc.reflow("In roc, functions are always written as a lambda, like "),
|
alloc.reflow("In Roc, functions are always written as a lambda, like "),
|
||||||
alloc.parser_suggestion("increment = \\n -> n + 1"),
|
alloc.parser_suggestion("increment = \\n -> n + 1"),
|
||||||
alloc.reflow("."),
|
alloc.reflow("."),
|
||||||
]),
|
]),
|
||||||
|
@ -1541,7 +1541,7 @@ fn to_import_report<'a>(
|
||||||
alloc.concat([
|
alloc.concat([
|
||||||
alloc.reflow("I was expecting to see the "),
|
alloc.reflow("I was expecting to see the "),
|
||||||
alloc.keyword("as"),
|
alloc.keyword("as"),
|
||||||
alloc.reflow(" keyword, like:"),
|
alloc.reflow(" keyword next, like:"),
|
||||||
]),
|
]),
|
||||||
alloc
|
alloc
|
||||||
.parser_suggestion("import svg.Path as SvgPath")
|
.parser_suggestion("import svg.Path as SvgPath")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue