diff options
| author | Laurențiu Nicola <lnicola@dend.ro> | 2023-06-05 12:04:23 +0300 |
|---|---|---|
| committer | Laurențiu Nicola <lnicola@dend.ro> | 2023-06-05 12:04:23 +0300 |
| commit | b8a7d439db0cfd765ed4bfedd2bbaeeee58b05a5 (patch) | |
| tree | 5adcbc6cf50af3bebc2cd4f42d5252a4d728690e /src/tools/rust-analyzer/crates/syntax | |
| parent | 51f714c8c5021fe25442e46798b1cbef2f2249ed (diff) | |
| parent | aa9bc8612514d216f84eec218dfd19ab83f3598a (diff) | |
| download | rust-b8a7d439db0cfd765ed4bfedd2bbaeeee58b05a5.tar.gz rust-b8a7d439db0cfd765ed4bfedd2bbaeeee58b05a5.zip | |
Merge commit 'aa9bc8612514d216f84eec218dfd19ab83f3598a' into sync-from-ra
Diffstat (limited to 'src/tools/rust-analyzer/crates/syntax')
15 files changed, 313 insertions, 68 deletions
diff --git a/src/tools/rust-analyzer/crates/syntax/Cargo.toml b/src/tools/rust-analyzer/crates/syntax/Cargo.toml index 305cf2d394b..fb38d25ab54 100644 --- a/src/tools/rust-analyzer/crates/syntax/Cargo.toml +++ b/src/tools/rust-analyzer/crates/syntax/Cargo.toml @@ -16,12 +16,14 @@ doctest = false cov-mark = "2.0.0-pre.1" either = "1.7.0" itertools = "0.10.5" -rowan = "0.15.10" -rustc_lexer = { version = "727.0.0", package = "rustc-ap-rustc_lexer" } +rowan = "0.15.11" rustc-hash = "1.1.0" once_cell = "1.17.0" indexmap = "1.9.1" -smol_str = "0.1.23" +smol_str.workspace = true +triomphe.workspace = true + +rustc_lexer.workspace = true parser.workspace = true profile.workspace = true diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index 548b5ba8b8b..4c9027dec68 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -51,7 +51,9 @@ TypeArg = Type AssocTypeArg = - NameRef GenericArgList? (':' TypeBoundList | ('=' Type | ConstArg)) + NameRef + (GenericArgList | ParamList RetType?)? + (':' TypeBoundList | ('=' Type | ConstArg)) LifetimeArg = Lifetime @@ -581,7 +583,7 @@ ImplTraitType = 'impl' TypeBoundList DynTraitType = - 'dyn' TypeBoundList + 'dyn'? TypeBoundList TypeBoundList = bounds:(TypeBound ('+' TypeBound)* '+'?) @@ -613,7 +615,7 @@ Pat = | ConstBlockPat LiteralPat = - Literal + '-'? Literal IdentPat = Attr* 'ref'? 'mut'? Name ('@' Pat)? diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index a493c92e7da..b3ea6ca8d46 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs @@ -236,6 +236,21 @@ impl ast::GenericParamList { } } + /// Removes the existing generic param + pub fn remove_generic_param(&self, generic_param: ast::GenericParam) { + if let Some(previous) = generic_param.syntax().prev_sibling() { + if let Some(next_token) = previous.next_sibling_or_token() { + ted::remove_all(next_token..=generic_param.syntax().clone().into()); + } + } else if let Some(next) = generic_param.syntax().next_sibling() { + if let Some(next_token) = next.prev_sibling_or_token() { + ted::remove_all(generic_param.syntax().clone().into()..=next_token); + } + } else { + ted::remove(generic_param.syntax()); + } + } + /// Constructs a matching [`ast::GenericArgList`] pub fn to_generic_args(&self) -> ast::GenericArgList { let args = self.generic_params().filter_map(|param| match param { @@ -465,6 +480,8 @@ impl ast::Impl { } impl ast::AssocItemList { + /// Attention! This function does align the first line of `item` with respect to `self`, + /// but it does _not_ change indentation of other lines (if any). pub fn add_item(&self, item: ast::AssocItem) { let (indent, position, whitespace) = match self.assoc_items().last() { Some(last_item) => ( diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs index c43d0830b9e..36980b146ef 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs @@ -288,6 +288,7 @@ impl ast::ArrayExpr { pub enum LiteralKind { String(ast::String), ByteString(ast::ByteString), + CString(ast::CString), IntNumber(ast::IntNumber), FloatNumber(ast::FloatNumber), Char(ast::Char), @@ -319,6 +320,9 @@ impl ast::Literal { if let Some(t) = ast::ByteString::cast(token.clone()) { return LiteralKind::ByteString(t); } + if let Some(t) = ast::CString::cast(token.clone()) { + return LiteralKind::CString(t); + } if let Some(t) = ast::Char::cast(token.clone()) { return LiteralKind::Char(t); } @@ -366,8 +370,7 @@ impl ast::BlockExpr { match parent.kind() { FOR_EXPR | IF_EXPR => parent .children() - .filter(|it| ast::Expr::can_cast(it.kind())) - .next() + .find(|it| ast::Expr::can_cast(it.kind())) .map_or(true, |it| it == *self.syntax()), LET_ELSE | FN | WHILE_EXPR | LOOP_EXPR | CONST_BLOCK_PAT => false, _ => true, diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index fe324845360..61f6a04c98d 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -121,6 +121,8 @@ impl ast::HasTypeBounds for AssocTypeArg {} impl AssocTypeArg { pub fn name_ref(&self) -> Option<NameRef> { support::child(&self.syntax) } pub fn generic_arg_list(&self) -> Option<GenericArgList> { support::child(&self.syntax) } + pub fn param_list(&self) -> Option<ParamList> { support::child(&self.syntax) } + pub fn ret_type(&self) -> Option<RetType> { support::child(&self.syntax) } pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) } pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) } pub fn const_arg(&self) -> Option<ConstArg> { support::child(&self.syntax) } @@ -1375,6 +1377,7 @@ pub struct LiteralPat { pub(crate) syntax: SyntaxNode, } impl LiteralPat { + pub fn minus_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![-]) } pub fn literal(&self) -> Option<Literal> { support::child(&self.syntax) } } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs index a3209c5abd2..f5863e9efe0 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs @@ -91,6 +91,27 @@ impl AstToken for ByteString { } #[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CString { + pub(crate) syntax: SyntaxToken, +} +impl std::fmt::Display for CString { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(&self.syntax, f) + } +} +impl AstToken for CString { + fn can_cast(kind: SyntaxKind) -> bool { kind == C_STRING } + fn cast(syntax: SyntaxToken) -> Option<Self> { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxToken { &self.syntax } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct IntNumber { pub(crate) syntax: SyntaxToken, } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index 5aebe4cd9f5..a07561e79a2 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -158,34 +158,148 @@ fn ty_from_text(text: &str) -> ast::Type { ast_from_text(&format!("type _T = {text};")) } +pub fn ty_alias( + ident: &str, + generic_param_list: Option<ast::GenericParamList>, + type_param_bounds: Option<ast::TypeParam>, + where_clause: Option<ast::WhereClause>, + assignment: Option<(ast::Type, Option<ast::WhereClause>)>, +) -> ast::TypeAlias { + let mut s = String::new(); + s.push_str(&format!("type {} ", ident)); + + if let Some(list) = generic_param_list { + s.push_str(&list.to_string()); + } + + if let Some(list) = type_param_bounds { + s.push_str(&format!(" : {}", &list)); + } + + if let Some(cl) = where_clause { + s.push_str(&format!(" {}", &cl.to_string())); + } + + if let Some(exp) = assignment { + if let Some(cl) = exp.1 { + s.push_str(&format!("= {} {}", &exp.0.to_string(), &cl.to_string())); + } else { + s.push_str(&format!("= {}", &exp.0.to_string())); + } + } + + s.push(';'); + ast_from_text(&s) +} + pub fn assoc_item_list() -> ast::AssocItemList { ast_from_text("impl C for D {}") } -// FIXME: `ty_params` should be `ast::GenericArgList` +fn merge_gen_params( + ps: Option<ast::GenericParamList>, + bs: Option<ast::GenericParamList>, +) -> Option<ast::GenericParamList> { + match (ps, bs) { + (None, None) => None, + (None, Some(bs)) => Some(bs), + (Some(ps), None) => Some(ps), + (Some(ps), Some(bs)) => { + for b in bs.generic_params() { + ps.add_generic_param(b); + } + Some(ps) + } + } +} + pub fn impl_( - ty: ast::Path, - params: Option<ast::GenericParamList>, - ty_params: Option<ast::GenericParamList>, + generic_params: Option<ast::GenericParamList>, + generic_args: Option<ast::GenericParamList>, + path_type: ast::Type, + where_clause: Option<ast::WhereClause>, + body: Option<Vec<either::Either<ast::Attr, ast::AssocItem>>>, ) -> ast::Impl { - let params = match params { - Some(params) => params.to_string(), - None => String::new(), + let (gen_params, tr_gen_args) = match (generic_params, generic_args) { + (None, None) => (String::new(), String::new()), + (None, Some(args)) => (String::new(), args.to_generic_args().to_string()), + (Some(params), None) => (params.to_string(), params.to_generic_args().to_string()), + (Some(params), Some(args)) => match merge_gen_params(Some(params.clone()), Some(args)) { + Some(merged) => (params.to_string(), merged.to_generic_args().to_string()), + None => (params.to_string(), String::new()), + }, }; - let ty_params = match ty_params { - Some(params) => params.to_string(), + + let where_clause = match where_clause { + Some(pr) => pr.to_string(), + None => " ".to_string(), + }; + + let body = match body { + Some(bd) => bd.iter().map(|elem| elem.to_string()).join(""), None => String::new(), }; - ast_from_text(&format!("impl{params} {ty}{ty_params} {{}}")) + + ast_from_text(&format!("impl{gen_params} {path_type}{tr_gen_args}{where_clause}{{{}}}", body)) } +// FIXME : We must make *_gen_args' type ast::GenericArgList but in order to do so we must implement in `edit_in_place.rs` +// `add_generic_arg()` just like `add_generic_param()` +// is implemented for `ast::GenericParamList` pub fn impl_trait( - trait_: ast::Path, - ty: ast::Path, - ty_params: Option<ast::GenericParamList>, + is_unsafe: bool, + trait_gen_params: Option<ast::GenericParamList>, + trait_gen_args: Option<ast::GenericParamList>, + type_gen_params: Option<ast::GenericParamList>, + type_gen_args: Option<ast::GenericParamList>, + is_negative: bool, + path_type: ast::Type, + ty: ast::Type, + trait_where_clause: Option<ast::WhereClause>, + ty_where_clause: Option<ast::WhereClause>, + body: Option<Vec<either::Either<ast::Attr, ast::AssocItem>>>, ) -> ast::Impl { - let ty_params = ty_params.map_or_else(String::new, |params| params.to_string()); - ast_from_text(&format!("impl{ty_params} {trait_} for {ty}{ty_params} {{}}")) + let is_unsafe = if is_unsafe { "unsafe " } else { "" }; + let ty_gen_args = match merge_gen_params(type_gen_params.clone(), type_gen_args) { + Some(pars) => pars.to_generic_args().to_string(), + None => String::new(), + }; + + let tr_gen_args = match merge_gen_params(trait_gen_params.clone(), trait_gen_args) { + Some(pars) => pars.to_generic_args().to_string(), + None => String::new(), + }; + + let gen_params = match merge_gen_params(trait_gen_params, type_gen_params) { + Some(pars) => pars.to_string(), + None => String::new(), + }; + + let is_negative = if is_negative { "! " } else { "" }; + + let where_clause = match (ty_where_clause, trait_where_clause) { + (None, None) => " ".to_string(), + (None, Some(tr)) => format!("\n{}\n", tr).to_string(), + (Some(ty), None) => format!("\n{}\n", ty).to_string(), + (Some(ty), Some(tr)) => { + let updated = ty.clone_for_update(); + tr.predicates().for_each(|p| { + ty.add_predicate(p); + }); + format!("\n{}\n", updated).to_string() + } + }; + + let body = match body { + Some(bd) => bd.iter().map(|elem| elem.to_string()).join(""), + None => String::new(), + }; + + ast_from_text(&format!("{is_unsafe}impl{gen_params} {is_negative}{path_type}{tr_gen_args} for {ty}{ty_gen_args}{where_clause}{{{}}}" , body)) +} + +pub fn impl_trait_type(bounds: ast::TypeBoundList) -> ast::ImplTraitType { + ast_from_text(&format!("fn f(x: impl {bounds}) {{}}")) } pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment { @@ -355,7 +469,7 @@ pub fn hacky_block_expr( format_to!(buf, " {t}\n") } else if kind == SyntaxKind::WHITESPACE { let content = t.text().trim_matches(|c| c != '\n'); - if content.len() >= 1 { + if !content.is_empty() { format_to!(buf, "{}", &content[1..]) } } @@ -827,6 +941,8 @@ pub fn fn_( body: ast::BlockExpr, ret_type: Option<ast::RetType>, is_async: bool, + is_const: bool, + is_unsafe: bool, ) -> ast::Fn { let type_params = match type_params { Some(type_params) => format!("{type_params}"), @@ -846,12 +962,13 @@ pub fn fn_( }; let async_literal = if is_async { "async " } else { "" }; + let const_literal = if is_const { "const " } else { "" }; + let unsafe_literal = if is_unsafe { "unsafe " } else { "" }; ast_from_text(&format!( - "{visibility}{async_literal}fn {fn_name}{type_params}{params} {ret_type}{where_clause}{body}", + "{visibility}{async_literal}{const_literal}{unsafe_literal}fn {fn_name}{type_params}{params} {ret_type}{where_clause}{body}", )) } - pub fn struct_( visibility: Option<ast::Visibility>, strukt_name: ast::Name, @@ -901,7 +1018,7 @@ pub mod tokens { pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> = Lazy::new(|| { SourceFile::parse( - "const C: <()>::Item = (1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p)\n;\n\n", + "const C: <()>::Item = (1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p)\n;\n\n", ) }); diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs index 2cd312e7f4f..090eb89f470 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs @@ -145,6 +145,10 @@ impl QuoteOffsets { } pub trait IsString: AstToken { + const RAW_PREFIX: &'static str; + fn is_raw(&self) -> bool { + self.text().starts_with(Self::RAW_PREFIX) + } fn quote_offsets(&self) -> Option<QuoteOffsets> { let text = self.text(); let offsets = QuoteOffsets::new(text)?; @@ -183,20 +187,18 @@ pub trait IsString: AstToken { cb(text_range + offset, unescaped_char); }); } -} - -impl IsString for ast::String {} - -impl ast::String { - pub fn is_raw(&self) -> bool { - self.text().starts_with('r') - } - pub fn map_range_up(&self, range: TextRange) -> Option<TextRange> { + fn map_range_up(&self, range: TextRange) -> Option<TextRange> { let contents_range = self.text_range_between_quotes()?; assert!(TextRange::up_to(contents_range.len()).contains_range(range)); Some(range + contents_range.start()) } +} +impl IsString for ast::String { + const RAW_PREFIX: &'static str = "r"; +} + +impl ast::String { pub fn value(&self) -> Option<Cow<'_, str>> { if self.is_raw() { let text = self.text(); @@ -235,13 +237,11 @@ impl ast::String { } } -impl IsString for ast::ByteString {} +impl IsString for ast::ByteString { + const RAW_PREFIX: &'static str = "br"; +} impl ast::ByteString { - pub fn is_raw(&self) -> bool { - self.text().starts_with("br") - } - pub fn value(&self) -> Option<Cow<'_, [u8]>> { if self.is_raw() { let text = self.text(); @@ -280,6 +280,49 @@ impl ast::ByteString { } } +impl IsString for ast::CString { + const RAW_PREFIX: &'static str = "cr"; +} + +impl ast::CString { + pub fn value(&self) -> Option<Cow<'_, str>> { + if self.is_raw() { + let text = self.text(); + let text = + &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; + return Some(Cow::Borrowed(text)); + } + + let text = self.text(); + let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; + + let mut buf = String::new(); + let mut prev_end = 0; + let mut has_error = false; + unescape_literal(text, Mode::Str, &mut |char_range, unescaped_char| match ( + unescaped_char, + buf.capacity() == 0, + ) { + (Ok(c), false) => buf.push(c), + (Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => { + prev_end = char_range.end + } + (Ok(c), true) => { + buf.reserve_exact(text.len()); + buf.push_str(&text[..prev_end]); + buf.push(c); + } + (Err(_), _) => has_error = true, + }); + + match (has_error, buf.capacity() == 0) { + (true, _) => None, + (false, true) => Some(Cow::Borrowed(text)), + (false, false) => Some(Cow::Owned(buf)), + } + } +} + impl ast::IntNumber { pub fn radix(&self) -> Radix { match self.text().get(..2).unwrap_or_default() { diff --git a/src/tools/rust-analyzer/crates/syntax/src/lib.rs b/src/tools/rust-analyzer/crates/syntax/src/lib.rs index 6f57cbad66b..efbf8796644 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/lib.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/lib.rs @@ -43,10 +43,11 @@ pub mod utils; pub mod ted; pub mod hacks; -use std::{marker::PhantomData, sync::Arc}; +use std::marker::PhantomData; use stdx::format_to; use text_edit::Indel; +use triomphe::Arc; pub use crate::{ ast::{AstNode, AstToken}, diff --git a/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs b/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs index 701e6232d58..45e59160982 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs @@ -39,7 +39,7 @@ fn reparse_token( let prev_token = root.covering_element(edit.delete).as_token()?.clone(); let prev_token_kind = prev_token.kind(); match prev_token_kind { - WHITESPACE | COMMENT | IDENT | STRING => { + WHITESPACE | COMMENT | IDENT | STRING | BYTE_STRING | C_STRING => { if prev_token_kind == WHITESPACE || prev_token_kind == COMMENT { // removing a new line may extends previous token let deleted_range = edit.delete - prev_token.text_range().start(); @@ -166,8 +166,8 @@ fn merge_errors( } res.extend(new_errors.into_iter().map(|new_err| { // fighting borrow checker with a variable ;) - let offseted_range = new_err.range() + range_before_reparse.start(); - new_err.with_range(offseted_range) + let offsetted_range = new_err.range() + range_before_reparse.start(); + new_err.with_range(offsetted_range) })); res } @@ -408,7 +408,7 @@ enum Foo { #[test] fn reparse_str_token_with_error_fixed() { - do_check(r#""unterinated$0$0"#, "\"", 12); + do_check(r#""unterminated$0$0"#, "\"", 13); } #[test] diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs b/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs index ccce71966ff..c5783b91a0f 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs @@ -71,7 +71,7 @@ pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc { "super", "trait", "true", "try", "type", "unsafe", "use", "where", "while", "yield", ], contextual_keywords: &["auto", "default", "existential", "union", "raw", "macro_rules", "yeet"], - literals: &["INT_NUMBER", "FLOAT_NUMBER", "CHAR", "BYTE", "STRING", "BYTE_STRING"], + literals: &["INT_NUMBER", "FLOAT_NUMBER", "CHAR", "BYTE", "STRING", "BYTE_STRING", "C_STRING"], tokens: &["ERROR", "IDENT", "WHITESPACE", "LIFETIME_IDENT", "COMMENT", "SHEBANG"], nodes: &[ "SOURCE_FILE", @@ -199,6 +199,7 @@ pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc { "GENERIC_PARAM", "LIFETIME_PARAM", "TYPE_PARAM", + "RETURN_TYPE_ARG", "CONST_PARAM", "GENERIC_ARG_LIST", "LIFETIME", diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs b/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs index e954b58251f..c49c5fa108b 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs @@ -535,6 +535,7 @@ impl Field { "!" => "excl", "*" => "star", "&" => "amp", + "-" => "minus", "_" => "underscore", "." => "dot", ".." => "dotdot", @@ -572,10 +573,11 @@ impl Field { fn lower(grammar: &Grammar) -> AstSrc { let mut res = AstSrc { - tokens: "Whitespace Comment String ByteString IntNumber FloatNumber Char Byte Ident" - .split_ascii_whitespace() - .map(|it| it.to_string()) - .collect::<Vec<_>>(), + tokens: + "Whitespace Comment String ByteString CString IntNumber FloatNumber Char Byte Ident" + .split_ascii_whitespace() + .map(|it| it.to_string()) + .collect::<Vec<_>>(), ..Default::default() }; diff --git a/src/tools/rust-analyzer/crates/syntax/src/token_text.rs b/src/tools/rust-analyzer/crates/syntax/src/token_text.rs index 913b24d42bc..09c080c0c23 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/token_text.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/token_text.rs @@ -3,6 +3,7 @@ use std::{cmp::Ordering, fmt, ops}; use rowan::GreenToken; +use smol_str::SmolStr; pub struct TokenText<'a>(pub(crate) Repr<'a>); @@ -47,6 +48,12 @@ impl From<TokenText<'_>> for String { } } +impl From<TokenText<'_>> for SmolStr { + fn from(token_text: TokenText<'_>) -> Self { + SmolStr::new(token_text.as_str()) + } +} + impl PartialEq<&'_ str> for TokenText<'_> { fn eq(&self, other: &&str) -> bool { self.as_str() == *other diff --git a/src/tools/rust-analyzer/crates/syntax/src/validation.rs b/src/tools/rust-analyzer/crates/syntax/src/validation.rs index fb2381110bf..e0ec6a242ff 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/validation.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/validation.rs @@ -5,11 +5,11 @@ mod block; use rowan::Direction; -use rustc_lexer::unescape::{self, unescape_byte, unescape_char, unescape_literal, Mode}; +use rustc_lexer::unescape::{self, unescape_literal, Mode}; use crate::{ algo, - ast::{self, HasAttrs, HasVisibility}, + ast::{self, HasAttrs, HasVisibility, IsString}, match_ast, AstNode, SyntaxError, SyntaxKind::{CONST, FN, INT_NUMBER, TYPE_ALIAS}, SyntaxNode, SyntaxToken, TextSize, T, @@ -44,7 +44,7 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> { errors } -fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str { +fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> (&'static str, bool) { use unescape::EscapeError as EE; #[rustfmt::skip] @@ -103,12 +103,15 @@ fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str { EE::UnicodeEscapeInByte => { "Byte literals must not contain unicode escapes" } - EE::NonAsciiCharInByte | EE::NonAsciiCharInByteString => { + EE::NonAsciiCharInByte => { "Byte literals must not contain non-ASCII characters" } + EE::UnskippedWhitespaceWarning => "Whitespace after this escape is not skipped", + EE::MultipleSkippedLinesWarning => "Multiple lines are skipped by this escape", + }; - err_message + (err_message, err.is_fatal()) } fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) { @@ -121,9 +124,13 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) { let text = token.text(); // FIXME: lift this lambda refactor to `fn` (https://github.com/rust-lang/rust-analyzer/pull/2834#discussion_r366199205) - let mut push_err = |prefix_len, (off, err): (usize, unescape::EscapeError)| { + let mut push_err = |prefix_len, off, err: unescape::EscapeError| { let off = token.text_range().start() + TextSize::try_from(off + prefix_len).unwrap(); - acc.push(SyntaxError::new_at_offset(rustc_unescape_error_to_string(err), off)); + let (message, is_err) = rustc_unescape_error_to_string(err); + // FIXME: Emit lexer warnings + if is_err { + acc.push(SyntaxError::new_at_offset(message, off)); + } }; match literal.kind() { @@ -132,7 +139,7 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) { if let Some(without_quotes) = unquote(text, 1, '"') { unescape_literal(without_quotes, Mode::Str, &mut |range, char| { if let Err(err) = char { - push_err(1, (range.start, err)); + push_err(1, range.start, err); } }); } @@ -143,20 +150,39 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) { if let Some(without_quotes) = unquote(text, 2, '"') { unescape_literal(without_quotes, Mode::ByteStr, &mut |range, char| { if let Err(err) = char { - push_err(2, (range.start, err)); + push_err(1, range.start, err); + } + }); + } + } + } + ast::LiteralKind::CString(s) => { + if !s.is_raw() { + if let Some(without_quotes) = unquote(text, 2, '"') { + unescape_literal(without_quotes, Mode::ByteStr, &mut |range, char| { + if let Err(err) = char { + push_err(1, range.start, err); } }); } } } ast::LiteralKind::Char(_) => { - if let Some(Err(e)) = unquote(text, 1, '\'').map(unescape_char) { - push_err(1, e); + if let Some(without_quotes) = unquote(text, 1, '\'') { + unescape_literal(without_quotes, Mode::Char, &mut |range, char| { + if let Err(err) = char { + push_err(1, range.start, err); + } + }); } } ast::LiteralKind::Byte(_) => { - if let Some(Err(e)) = unquote(text, 2, '\'').map(unescape_byte) { - push_err(2, e); + if let Some(without_quotes) = unquote(text, 2, '\'') { + unescape_literal(without_quotes, Mode::Byte, &mut |range, char| { + if let Err(err) = char { + push_err(2, range.start, err); + } + }); } } ast::LiteralKind::IntNumber(_) @@ -175,14 +201,14 @@ pub(crate) fn validate_block_structure(root: &SyntaxNode) { assert_eq!( node.parent(), pair.parent(), - "\nunpaired curlys:\n{}\n{:#?}\n", + "\nunpaired curlies:\n{}\n{:#?}\n", root.text(), root, ); assert!( node.next_sibling_or_token().is_none() && pair.prev_sibling_or_token().is_none(), - "\nfloating curlys at {:?}\nfile:\n{}\nerror:\n{}\n", + "\nfloating curlies at {:?}\nfile:\n{}\nerror:\n{}\n", node, root.text(), node, diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0000.rs b/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0000.rs index f977d23c480..13852aa78b8 100644 --- a/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0000.rs +++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0000.rs @@ -39,13 +39,13 @@ ast::Root::cast(self.syntax()).unwrap() } pub fn syntax(&self) -> SyntaxNodeRef { - self.root.brroowed() + self.root.borrowed() } mp_tree(root), ); assert!( node.next_sibling().is_none() && pair.prev_sibling().is_none(), - "\nfloating curlys at {:?}\nfile:\n{}\nerror:\n{}\n", + "\nfloating curlies at {:?}\nfile:\n{}\nerror:\n{}\n", node, root.text(), node.text(), |
