From 728d257839ad2397e34c71f8ceda151fa579242d Mon Sep 17 00:00:00 2001 From: b-naber Date: Tue, 15 Dec 2020 17:43:24 +0100 Subject: [PATCH 1/3] improve diagnostics for angle args --- .../rustc_parse/src/parser/diagnostics.rs | 21 +++++-- compiler/rustc_parse/src/parser/mod.rs | 2 +- compiler/rustc_parse/src/parser/path.rs | 55 ++++--------------- 3 files changed, 28 insertions(+), 50 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 35435baea7019..f2fcce5c22662 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2,14 +2,13 @@ use super::ty::AllowPlus; use super::TokenType; use super::{BlockMode, Parser, PathStyle, Restrictions, SemiColonMode, SeqSep, TokenExpectType}; +use rustc_ast as ast; use rustc_ast::ptr::P; use rustc_ast::token::{self, Lit, LitKind, TokenKind}; use rustc_ast::util::parser::AssocOp; -use rustc_ast::{ - self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, - Block, BlockCheckMode, Expr, ExprKind, GenericArg, Item, ItemKind, Mutability, Param, Pat, - PatKind, Path, PathSegment, QSelf, Ty, TyKind, -}; +use rustc_ast::{AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec}; +use rustc_ast::{BinOpKind, BindingMode, Block, BlockCheckMode, Expr, ExprKind, GenericArg, Item}; +use rustc_ast::{ItemKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QSelf, Ty, TyKind}; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{pluralize, struct_span_err}; @@ -220,6 +219,7 @@ impl<'a> Parser<'a> { edible: &[TokenKind], inedible: &[TokenKind], ) -> PResult<'a, bool /* recovered */> { + debug!("expected_one_of_not_found(edible: {:?}, inedible: {:?})", edible, inedible); fn tokens_to_string(tokens: &[TokenType]) -> String { let mut i = tokens.iter(); // This might be a sign we need a connect method on `Iterator`. @@ -245,6 +245,7 @@ impl<'a> Parser<'a> { .collect::>(); expected.sort_by_cached_key(|x| x.to_string()); expected.dedup(); + let expect = tokens_to_string(&expected[..]); let actual = super::token_descr(&self.token); let (msg_exp, (label_sp, label_exp)) = if expected.len() > 1 { @@ -270,6 +271,16 @@ impl<'a> Parser<'a> { }; self.last_unexpected_token_span = Some(self.token.span); let mut err = self.struct_span_err(self.token.span, &msg_exp); + + // Add suggestion for a missing closing angle bracket if '>' is included in expected_tokens + // there are unclosed angle brackets + if self.unmatched_angle_bracket_count > 0 + && self.token.kind == TokenKind::Eq + && expected.iter().any(|tok| matches!(tok, TokenType::Token(TokenKind::Gt))) + { + err.span_label(self.prev_token.span, "maybe try to close unmatched angle bracket"); + } + let sp = if self.token == token::Eof { // This is EOF; don't want to point at the following char, but rather the last token. self.prev_token.span diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index c85b7a00732d3..d5e625a167c68 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -272,7 +272,7 @@ impl TokenCursor { } } -#[derive(Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq)] enum TokenType { Token(TokenKind), Keyword(Symbol), diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index dd36122f6a10e..4234740b2b15f 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -185,7 +185,6 @@ impl<'a> Parser<'a> { pub(super) fn parse_path_segment(&mut self, style: PathStyle) -> PResult<'a, PathSegment> { let ident = self.parse_path_segment_ident()?; - let is_args_start = |token: &Token| { matches!( token.kind, @@ -420,7 +419,10 @@ impl<'a> Parser<'a> { match arg { Some(arg) => { if self.check(&token::Colon) | self.check(&token::Eq) { - let (ident, gen_args) = self.get_ident_from_generic_arg(arg, lo)?; + let (ident, gen_args) = match self.get_ident_from_generic_arg(arg) { + Ok(ident_gen_args) => ident_gen_args, + Err(arg) => return Ok(Some(AngleBracketedArg::Arg(arg))), + }; let kind = if self.eat(&token::Colon) { // Parse associated type constraint bound. @@ -561,50 +563,15 @@ impl<'a> Parser<'a> { fn get_ident_from_generic_arg( &self, gen_arg: GenericArg, - lo: Span, - ) -> PResult<'a, (Ident, Option)> { - let gen_arg_span = gen_arg.span(); - match gen_arg { - GenericArg::Type(t) => match t.into_inner().kind { - ast::TyKind::Path(qself, mut path) => { - if let Some(qself) = qself { - let mut err = self.struct_span_err( - gen_arg_span, - "qualified paths cannot be used in associated type constraints", - ); - err.span_label( - qself.path_span, - "not allowed in associated type constraints", - ); - return Err(err); - } - if path.segments.len() == 1 { - let path_seg = path.segments.remove(0); - let ident = path_seg.ident; - let gen_args = path_seg.args.map(|args| args.into_inner()); - return Ok((ident, gen_args)); - } - let err = self.struct_span_err( - path.span, - "paths with multiple segments cannot be used in associated type constraints", - ); - return Err(err); - } - _ => { - let span = lo.to(self.prev_token.span); - let err = self.struct_span_err( - span, - "only path types can be used in associated type constraints", - ); - return Err(err); + ) -> Result<(Ident, Option), GenericArg> { + if let GenericArg::Type(ty) = &gen_arg { + if let ast::TyKind::Path(qself, path) = &ty.kind { + if qself.is_none() && path.segments.len() == 1 { + let seg = &path.segments[0]; + return Ok((seg.ident, seg.args.as_deref().cloned())); } - }, - _ => { - let span = lo.to(self.prev_token.span); - let err = self - .struct_span_err(span, "only types can be used in associated type constraints"); - return Err(err); } } + Err(gen_arg) } } From 5ddad99a5686ce32b87ef14c21642fc55becebf9 Mon Sep 17 00:00:00 2001 From: b-naber Date: Tue, 15 Dec 2020 21:03:25 +0100 Subject: [PATCH 2/3] add and update tests --- .../parse/trait-path-expected-token.stderr | 4 +- .../parse/trait-path-expressions.rs | 2 +- .../parse/trait-path-expressions.stderr | 8 +-- .../parse/trait-path-missing-gen_arg.stderr | 4 +- .../parse/trait-path-segments.rs | 6 +-- .../parse/trait-path-segments.stderr | 26 +++++----- .../parse/trait-path-types.rs | 6 +-- .../parse/trait-path-types.stderr | 24 +++++---- src/test/ui/issues/issue-34334.rs | 4 +- src/test/ui/issues/issue-34334.stderr | 11 +++-- ...ing-closing-angle-bracket-eq-constraint.rs | 23 +++++++++ ...closing-angle-bracket-eq-constraint.stderr | 49 +++++++++++++++++++ .../nested-missing-closing-angle-bracket.rs | 4 ++ ...ested-missing-closing-angle-bracket.stderr | 8 +++ 14 files changed, 141 insertions(+), 38 deletions(-) create mode 100644 src/test/ui/parser/missing-closing-angle-bracket-eq-constraint.rs create mode 100644 src/test/ui/parser/missing-closing-angle-bracket-eq-constraint.stderr create mode 100644 src/test/ui/parser/nested-missing-closing-angle-bracket.rs create mode 100644 src/test/ui/parser/nested-missing-closing-angle-bracket.stderr diff --git a/src/test/ui/generic-associated-types/parse/trait-path-expected-token.stderr b/src/test/ui/generic-associated-types/parse/trait-path-expected-token.stderr index 051253cadc652..8abc4ccc9a5fa 100644 --- a/src/test/ui/generic-associated-types/parse/trait-path-expected-token.stderr +++ b/src/test/ui/generic-associated-types/parse/trait-path-expected-token.stderr @@ -2,7 +2,9 @@ error: expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `=` --> $DIR/trait-path-expected-token.rs:8:33 | LL | fn f1<'a>(arg : Box>) {} - | ^ expected one of 7 possible tokens + | - ^ expected one of 7 possible tokens + | | + | maybe try to close unmatched angle bracket warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/trait-path-expected-token.rs:1:12 diff --git a/src/test/ui/generic-associated-types/parse/trait-path-expressions.rs b/src/test/ui/generic-associated-types/parse/trait-path-expressions.rs index de61cfa1cf718..5e50c6b35c960 100644 --- a/src/test/ui/generic-associated-types/parse/trait-path-expressions.rs +++ b/src/test/ui/generic-associated-types/parse/trait-path-expressions.rs @@ -17,7 +17,7 @@ mod error2 { } fn f2<'a>(arg : Box>) {} - //~^ ERROR: only types can be used in associated type constraints + //~^ ERROR: expected one of } fn main() {} diff --git a/src/test/ui/generic-associated-types/parse/trait-path-expressions.stderr b/src/test/ui/generic-associated-types/parse/trait-path-expressions.stderr index a9ba8adcaba3f..27e1a750b2131 100644 --- a/src/test/ui/generic-associated-types/parse/trait-path-expressions.stderr +++ b/src/test/ui/generic-associated-types/parse/trait-path-expressions.stderr @@ -6,11 +6,13 @@ LL | fn f1<'a>(arg : Box>) {} | | | while parsing a const generic argument starting here -error: only types can be used in associated type constraints - --> $DIR/trait-path-expressions.rs:19:30 +error: expected one of `,`, `:`, or `>`, found `=` + --> $DIR/trait-path-expressions.rs:19:36 | LL | fn f2<'a>(arg : Box>) {} - | ^^^^^ + | - ^ expected one of `,`, `:`, or `>` + | | + | maybe try to close unmatched angle bracket warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/trait-path-expressions.rs:1:12 diff --git a/src/test/ui/generic-associated-types/parse/trait-path-missing-gen_arg.stderr b/src/test/ui/generic-associated-types/parse/trait-path-missing-gen_arg.stderr index 8a5e2c29c3665..1b9de455713e9 100644 --- a/src/test/ui/generic-associated-types/parse/trait-path-missing-gen_arg.stderr +++ b/src/test/ui/generic-associated-types/parse/trait-path-missing-gen_arg.stderr @@ -28,7 +28,9 @@ error: expected one of `>`, a const expression, lifetime, or type, found `=` --> $DIR/trait-path-missing-gen_arg.rs:17:30 | LL | fn f1<'a>(arg : Box>) {} - | ^ expected one of `>`, a const expression, lifetime, or type + | - ^ expected one of `>`, const, lifetime, or type + | | + | maybe try to close unmatched angle bracket warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/trait-path-missing-gen_arg.rs:1:12 diff --git a/src/test/ui/generic-associated-types/parse/trait-path-segments.rs b/src/test/ui/generic-associated-types/parse/trait-path-segments.rs index 0bf48b1f41810..eba30e1438f04 100644 --- a/src/test/ui/generic-associated-types/parse/trait-path-segments.rs +++ b/src/test/ui/generic-associated-types/parse/trait-path-segments.rs @@ -7,7 +7,7 @@ const _: () = { } fn f1<'a>(arg : Box>) {} - //~^ ERROR: paths with multiple segments cannot be used in associated type constraints + //~^ ERROR: expected one of }; const _: () = { @@ -18,7 +18,7 @@ const _: () = { trait Z {} impl::Y<'a> = &'a u32>> Z for T {} - //~^ ERROR: qualified paths cannot be used in associated type constraints + //~^ ERROR: expected one of }; const _: () = { @@ -29,7 +29,7 @@ const _: () = { trait Z {} impl = &'a u32>> Z for T {} - //~^ ERROR: paths with multiple segments cannot be used in associated type constraints + //~^ ERROR: expected one of }; fn main() {} diff --git a/src/test/ui/generic-associated-types/parse/trait-path-segments.stderr b/src/test/ui/generic-associated-types/parse/trait-path-segments.stderr index 4e2b84d018239..c82953aaed7dd 100644 --- a/src/test/ui/generic-associated-types/parse/trait-path-segments.stderr +++ b/src/test/ui/generic-associated-types/parse/trait-path-segments.stderr @@ -1,22 +1,26 @@ -error: paths with multiple segments cannot be used in associated type constraints - --> $DIR/trait-path-segments.rs:9:31 +error: expected one of `!`, `(`, `+`, `,`, `::`, `:`, `<`, or `>`, found `=` + --> $DIR/trait-path-segments.rs:9:36 | LL | fn f1<'a>(arg : Box>) {} - | ^^^^ + | - ^ expected one of 8 possible tokens + | | + | maybe try to close unmatched angle bracket -error: qualified paths cannot be used in associated type constraints - --> $DIR/trait-path-segments.rs:20:16 +error: expected one of `,`, `::`, `:`, or `>`, found `=` + --> $DIR/trait-path-segments.rs:20:35 | LL | impl::Y<'a> = &'a u32>> Z for T {} - | ^^^^^^^^^-^^^^^^^^ - | | - | not allowed in associated type constraints + | - ^ expected one of `,`, `::`, `:`, or `>` + | | + | maybe try to close unmatched angle bracket -error: paths with multiple segments cannot be used in associated type constraints - --> $DIR/trait-path-segments.rs:31:16 +error: expected one of `!`, `+`, `,`, `::`, `:`, or `>`, found `=` + --> $DIR/trait-path-segments.rs:31:25 | LL | impl = &'a u32>> Z for T {} - | ^^^^^^^^ + | - ^ expected one of `!`, `+`, `,`, `::`, `:`, or `>` + | | + | maybe try to close unmatched angle bracket warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/trait-path-segments.rs:1:12 diff --git a/src/test/ui/generic-associated-types/parse/trait-path-types.rs b/src/test/ui/generic-associated-types/parse/trait-path-types.rs index 6cdb501ec65fb..522b3edc638ea 100644 --- a/src/test/ui/generic-associated-types/parse/trait-path-types.rs +++ b/src/test/ui/generic-associated-types/parse/trait-path-types.rs @@ -7,17 +7,17 @@ trait X { const _: () = { fn f<'a>(arg : Box>) {} - //~^ ERROR: only path types can be used in associated type constraints + //~^ ERROR: expected one of }; const _: () = { fn f1<'a>(arg : Box) = &'a ()>>) {} - //~^ ERROR: only path types can be used in associated type constraints + //~^ ERROR: expected one of }; const _: () = { fn f1<'a>(arg : Box>) {} - //~^ ERROR: only types can be used in associated type constraints + //~^ ERROR: expected one of }; fn main() {} diff --git a/src/test/ui/generic-associated-types/parse/trait-path-types.stderr b/src/test/ui/generic-associated-types/parse/trait-path-types.stderr index f5be084613b4c..ac791c224810e 100644 --- a/src/test/ui/generic-associated-types/parse/trait-path-types.stderr +++ b/src/test/ui/generic-associated-types/parse/trait-path-types.stderr @@ -1,20 +1,26 @@ -error: only path types can be used in associated type constraints - --> $DIR/trait-path-types.rs:9:29 +error: expected one of `,`, `:`, or `>`, found `=` + --> $DIR/trait-path-types.rs:9:37 | LL | fn f<'a>(arg : Box>) {} - | ^^^^^^^ + | - ^ expected one of `,`, `:`, or `>` + | | + | maybe try to close unmatched angle bracket -error: only path types can be used in associated type constraints - --> $DIR/trait-path-types.rs:14:29 +error: expected one of `,`, `:`, or `>`, found `=` + --> $DIR/trait-path-types.rs:14:37 | LL | fn f1<'a>(arg : Box) = &'a ()>>) {} - | ^^^^^^^ + | - ^ expected one of `,`, `:`, or `>` + | | + | maybe try to close unmatched angle bracket -error: only types can be used in associated type constraints - --> $DIR/trait-path-types.rs:19:30 +error: expected one of `,`, `:`, or `>`, found `=` + --> $DIR/trait-path-types.rs:19:33 | LL | fn f1<'a>(arg : Box>) {} - | ^^ + | -- ^ expected one of `,`, `:`, or `>` + | | + | maybe try to close unmatched angle bracket warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/trait-path-types.rs:1:12 diff --git a/src/test/ui/issues/issue-34334.rs b/src/test/ui/issues/issue-34334.rs index b45c00f694321..51486bc40de0c 100644 --- a/src/test/ui/issues/issue-34334.rs +++ b/src/test/ui/issues/issue-34334.rs @@ -1,6 +1,8 @@ fn main () { let sr: Vec<(u32, _, _) = vec![]; - //~^ ERROR only path types can be used in associated type constraints + //~^ ERROR expected one of + let sr2: Vec<(u32, _, _)> = sr.iter().map(|(faction, th_sender, th_receiver)| {}).collect(); //~^ ERROR a value of type `Vec<(u32, _, _)>` cannot be built + } diff --git a/src/test/ui/issues/issue-34334.stderr b/src/test/ui/issues/issue-34334.stderr index a9b9bf06d7f42..acb44ce2c3545 100644 --- a/src/test/ui/issues/issue-34334.stderr +++ b/src/test/ui/issues/issue-34334.stderr @@ -1,13 +1,14 @@ -error: only path types can be used in associated type constraints - --> $DIR/issue-34334.rs:2:17 +error: expected one of `,`, `:`, or `>`, found `=` + --> $DIR/issue-34334.rs:2:29 | LL | let sr: Vec<(u32, _, _) = vec![]; - | -- ^^^^^^^^^^^ - | | + | -- - ^ expected one of `,`, `:`, or `>` + | | | + | | maybe try to close unmatched angle bracket | while parsing the type for `sr` error[E0277]: a value of type `Vec<(u32, _, _)>` cannot be built from an iterator over elements of type `()` - --> $DIR/issue-34334.rs:4:87 + --> $DIR/issue-34334.rs:5:87 | LL | let sr2: Vec<(u32, _, _)> = sr.iter().map(|(faction, th_sender, th_receiver)| {}).collect(); | ^^^^^^^ value of type `Vec<(u32, _, _)>` cannot be built from `std::iter::Iterator` diff --git a/src/test/ui/parser/missing-closing-angle-bracket-eq-constraint.rs b/src/test/ui/parser/missing-closing-angle-bracket-eq-constraint.rs new file mode 100644 index 0000000000000..da95c1bfa2709 --- /dev/null +++ b/src/test/ui/parser/missing-closing-angle-bracket-eq-constraint.rs @@ -0,0 +1,23 @@ +struct Foo { + _a : T1, + _b : T2, +} + +fn test1(arg : T) { + let v : Vec<(u32,_) = vec![]; + //~^ ERROR: expected one of + //~| ERROR: type annotations needed +} + +fn test2(arg1 : T1, arg2 : T2) { + let foo : Foo::(arg : &'a u32) { + let v : Vec<'a = vec![]; + //~^ ERROR: expected one of + //~| ERROR: type annotations needed for `Vec` +} + +fn main() {} diff --git a/src/test/ui/parser/missing-closing-angle-bracket-eq-constraint.stderr b/src/test/ui/parser/missing-closing-angle-bracket-eq-constraint.stderr new file mode 100644 index 0000000000000..ae53334f5e02a --- /dev/null +++ b/src/test/ui/parser/missing-closing-angle-bracket-eq-constraint.stderr @@ -0,0 +1,49 @@ +error: expected one of `,`, `:`, or `>`, found `=` + --> $DIR/missing-closing-angle-bracket-eq-constraint.rs:7:23 + | +LL | let v : Vec<(u32,_) = vec![]; + | - - ^ expected one of `,`, `:`, or `>` + | | | + | | maybe try to close unmatched angle bracket + | while parsing the type for `v` + +error: expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `{` + --> $DIR/missing-closing-angle-bracket-eq-constraint.rs:13:32 + | +LL | let foo : Foo::`, found `=` + --> $DIR/missing-closing-angle-bracket-eq-constraint.rs:18:18 + | +LL | let v : Vec<'a = vec![]; + | - -- ^ expected one of `,`, `:`, or `>` + | | | + | | maybe try to close unmatched angle bracket + | while parsing the type for `v` + +error[E0282]: type annotations needed for `Vec` + --> $DIR/missing-closing-angle-bracket-eq-constraint.rs:7:25 + | +LL | let v : Vec<(u32,_) = vec![]; + | - ^^^^^^ cannot infer type for type parameter `T` + | | + | consider giving `v` the explicit type `Vec`, where the type parameter `T` is specified + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0282]: type annotations needed for `Vec` + --> $DIR/missing-closing-angle-bracket-eq-constraint.rs:18:20 + | +LL | let v : Vec<'a = vec![]; + | - ^^^^^^ cannot infer type for type parameter `T` + | | + | consider giving `v` the explicit type `Vec`, where the type parameter `T` is specified + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0282`. diff --git a/src/test/ui/parser/nested-missing-closing-angle-bracket.rs b/src/test/ui/parser/nested-missing-closing-angle-bracket.rs new file mode 100644 index 0000000000000..84ffdd176aea0 --- /dev/null +++ b/src/test/ui/parser/nested-missing-closing-angle-bracket.rs @@ -0,0 +1,4 @@ +fn main() { + let v : Vec:: = vec![vec![]]; + //~^ ERROR: expected one of +} diff --git a/src/test/ui/parser/nested-missing-closing-angle-bracket.stderr b/src/test/ui/parser/nested-missing-closing-angle-bracket.stderr new file mode 100644 index 0000000000000..b85bc02568c77 --- /dev/null +++ b/src/test/ui/parser/nested-missing-closing-angle-bracket.stderr @@ -0,0 +1,8 @@ +error: expected one of `,` or `>`, found `;` + --> $DIR/nested-missing-closing-angle-bracket.rs:2:46 + | +LL | let v : Vec:: = vec![vec![]]; + | - while parsing the type for `v` ^ expected one of `,` or `>` + +error: aborting due to previous error + From 3ba6cf13f42250c1d0d3bab206e37e93df0582c4 Mon Sep 17 00:00:00 2001 From: b-naber Date: Fri, 22 Jan 2021 18:31:25 +0100 Subject: [PATCH 3/3] bless tests --- .../parse/trait-path-missing-gen_arg.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/ui/generic-associated-types/parse/trait-path-missing-gen_arg.stderr b/src/test/ui/generic-associated-types/parse/trait-path-missing-gen_arg.stderr index 1b9de455713e9..f6038566e5b5e 100644 --- a/src/test/ui/generic-associated-types/parse/trait-path-missing-gen_arg.stderr +++ b/src/test/ui/generic-associated-types/parse/trait-path-missing-gen_arg.stderr @@ -28,7 +28,7 @@ error: expected one of `>`, a const expression, lifetime, or type, found `=` --> $DIR/trait-path-missing-gen_arg.rs:17:30 | LL | fn f1<'a>(arg : Box>) {} - | - ^ expected one of `>`, const, lifetime, or type + | - ^ expected one of `>`, a const expression, lifetime, or type | | | maybe try to close unmatched angle bracket