about summary refs log tree commit diff
path: root/src/tools/rust-analyzer/crates/syntax
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/rust-analyzer/crates/syntax')
-rw-r--r--src/tools/rust-analyzer/crates/syntax/Cargo.toml8
-rw-r--r--src/tools/rust-analyzer/crates/syntax/rust.ungram8
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs17
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs7
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs3
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs21
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/make.rs155
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs71
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/lib.rs3
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs8
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs3
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs10
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/token_text.rs7
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/validation.rs56
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0000.rs4
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(),