diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml index 31bb7eed8d7..9f246098e76 100644 --- a/src/tools/rust-analyzer/.github/workflows/ci.yaml +++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml @@ -161,10 +161,21 @@ jobs: # if: runner.os == 'Linux' # working-directory: ./editors/code + # If this steps fails, your code's type integrity might be wrong at some places at TypeScript level. + - run: npm run typecheck + working-directory: ./editors/code + if: needs.changes.outputs.typescript == 'true' + + # You may fix the code automatically by running `npm run lint:fix` if this steps fails. - run: npm run lint working-directory: ./editors/code if: needs.changes.outputs.typescript == 'true' + # To fix this steps, please run `npm run format`. + - run: npm run format:check + working-directory: ./editors/code + if: needs.changes.outputs.typescript == 'true' + - name: Run VS Code tests (Linux) if: matrix.os == 'ubuntu-latest' && needs.changes.outputs.typescript == 'true' env: @@ -179,10 +190,6 @@ jobs: run: npm test working-directory: ./editors/code - - run: npm run pretest - working-directory: ./editors/code - if: needs.changes.outputs.typescript == 'true' - - run: npm run package --scripts-prepend-node-path working-directory: ./editors/code if: needs.changes.outputs.typescript == 'true' diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs index 0c6cf0b49a2..eeaed87164d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs @@ -634,7 +634,7 @@ fn print_literal(&mut self, literal: &Literal) { match literal { Literal::String(it) => w!(self, "{:?}", it), Literal::ByteString(it) => w!(self, "\"{}\"", it.escape_ascii()), - Literal::CString(it) => w!(self, "\"{}\\0\"", it), + Literal::CString(it) => w!(self, "\"{}\\0\"", it.escape_ascii()), Literal::Char(it) => w!(self, "'{}'", it.escape_debug()), Literal::Bool(it) => w!(self, "{}", it), Literal::Int(i, suffix) => { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 500e880061a..8a140a1ec18 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -85,7 +85,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { pub enum Literal { String(Box), ByteString(Box<[u8]>), - CString(Box), + CString(Box<[u8]>), Char(char), Bool(bool), Int(i128, Option), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index 4de90d40a7c..1c0f7b08da8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -16,7 +16,8 @@ use crate::{ db::HirDatabase, infer::InferenceContext, lower::ParamLoweringMode, mir::monomorphize_mir_body_bad, to_placeholder_idx, utils::Generics, Const, ConstData, - ConstScalar, ConstValue, GenericArg, Interner, MemoryMap, Substitution, Ty, TyBuilder, + ConstScalar, ConstValue, GenericArg, Interner, MemoryMap, Substitution, TraitEnvironment, Ty, + TyBuilder, }; use super::mir::{interpret_mir, lower_to_mir, pad16, MirEvalError, MirLowerError}; @@ -135,7 +136,7 @@ pub fn intern_const_ref( ty: Ty, krate: CrateId, ) -> Const { - let layout = db.layout_of_ty(ty.clone(), krate); + let layout = db.layout_of_ty(ty.clone(), Arc::new(TraitEnvironment::empty(krate))); let bytes = match value { LiteralConstRef::Int(i) => { // FIXME: We should handle failure of layout better. @@ -173,7 +174,7 @@ pub fn try_const_usize(db: &dyn HirDatabase, c: &Const) -> Option { chalk_ir::ConstValue::Concrete(c) => match &c.interned { ConstScalar::Bytes(it, _) => Some(u128::from_le_bytes(pad16(&it, false))), ConstScalar::UnevaluatedConst(c, subst) => { - let ec = db.const_eval(*c, subst.clone()).ok()?; + let ec = db.const_eval(*c, subst.clone(), None).ok()?; try_const_usize(db, &ec) } _ => None, @@ -186,6 +187,7 @@ pub(crate) fn const_eval_recover( _: &[String], _: &GeneralConstId, _: &Substitution, + _: &Option>, ) -> Result { Err(ConstEvalError::MirLowerError(MirLowerError::Loop)) } @@ -210,6 +212,7 @@ pub(crate) fn const_eval_query( db: &dyn HirDatabase, def: GeneralConstId, subst: Substitution, + trait_env: Option>, ) -> Result { let body = match def { GeneralConstId::ConstId(c) => { @@ -228,7 +231,7 @@ pub(crate) fn const_eval_query( } GeneralConstId::InTypeConstId(c) => db.mir_body(c.into())?, }; - let c = interpret_mir(db, body, false).0?; + let c = interpret_mir(db, body, false, trait_env).0?; Ok(c) } @@ -241,7 +244,7 @@ pub(crate) fn const_eval_static_query( Substitution::empty(Interner), db.trait_environment_for_body(def.into()), )?; - let c = interpret_mir(db, body, false).0?; + let c = interpret_mir(db, body, false, None).0?; Ok(c) } @@ -268,7 +271,7 @@ pub(crate) fn const_eval_discriminant_variant( Substitution::empty(Interner), db.trait_environment_for_body(def), )?; - let c = interpret_mir(db, mir_body, false).0?; + let c = interpret_mir(db, mir_body, false, None).0?; let c = try_const_usize(db, &c).unwrap() as i128; Ok(c) } @@ -293,7 +296,7 @@ pub(crate) fn eval_to_const( } let infer = ctx.clone().resolve_all(); if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, &ctx.body, &infer, expr) { - if let Ok(result) = interpret_mir(db, Arc::new(mir_body), true).0 { + if let Ok(result) = interpret_mir(db, Arc::new(mir_body), true, None).0 { return result; } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs index 5bb327606d3..98ebe557245 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs @@ -114,7 +114,7 @@ fn eval_goal(db: &TestDB, file_id: FileId) -> Result { _ => None, }) .expect("No const named GOAL found in the test"); - db.const_eval(const_id.into(), Substitution::empty(Interner)) + db.const_eval(const_id.into(), Substitution::empty(Interner), None) } #[test] @@ -1941,6 +1941,33 @@ fn bar(&self) -> i32 { 700 } "#, 900, ); + check_number( + r#" + //- minicore: coerce_unsized, index, slice + trait A { + fn x(&self) -> i32; + } + + trait B: A {} + + impl A for i32 { + fn x(&self) -> i32 { + 5 + } + } + + impl B for i32 { + + } + + const fn f(x: &dyn B) -> i32 { + x.x() + } + + const GOAL: i32 = f(&2i32); + "#, + 5, + ); } #[test] @@ -2492,6 +2519,28 @@ impl ToConst for U0 { "#, 5, ); + check_number( + r#" + //- minicore: size_of + //- /a/lib.rs crate:a + use core::mem::size_of; + pub struct S(T); + impl S { + pub const X: usize = core::mem::size_of::(); + } + //- /main.rs crate:main deps:a + use a::{S}; + trait Tr { + type Ty; + } + impl Tr for i32 { + type Ty = u64; + } + struct K(::Ty); + const GOAL: usize = S::>::X; + "#, + 8, + ); check_number( r#" struct S(*mut T); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 14b719ea412..9c96b5ab8db 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -77,8 +77,12 @@ fn monomorphized_mir_body_for_closure( #[salsa::invoke(crate::consteval::const_eval_query)] #[salsa::cycle(crate::consteval::const_eval_recover)] - fn const_eval(&self, def: GeneralConstId, subst: Substitution) - -> Result; + fn const_eval( + &self, + def: GeneralConstId, + subst: Substitution, + trait_env: Option>, + ) -> Result; #[salsa::invoke(crate::consteval::const_eval_static_query)] #[salsa::cycle(crate::consteval::const_eval_static_recover)] @@ -100,12 +104,16 @@ fn layout_of_adt( &self, def: AdtId, subst: Substitution, - krate: CrateId, + env: Arc, ) -> Result, LayoutError>; #[salsa::invoke(crate::layout::layout_of_ty_query)] #[salsa::cycle(crate::layout::layout_of_ty_recover)] - fn layout_of_ty(&self, ty: Ty, krate: CrateId) -> Result, LayoutError>; + fn layout_of_ty( + &self, + ty: Ty, + env: Arc, + ) -> Result, LayoutError>; #[salsa::invoke(crate::layout::target_data_layout_query)] fn target_data_layout(&self, krate: CrateId) -> Option>; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs index 73c8ad3dd5a..5aaa2bcc7c2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs @@ -14,13 +14,12 @@ use std::fmt; -use base_db::CrateId; use hir_def::{ data::adt::VariantData, hir::{Pat, PatId}, src::HasSource, - AdtId, AttrDefId, ConstId, EnumId, FunctionId, ItemContainerId, Lookup, ModuleDefId, StaticId, - StructId, + AdtId, AttrDefId, ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, ItemContainerId, + Lookup, ModuleDefId, StaticId, StructId, }; use hir_expand::{ name::{AsName, Name}, @@ -44,13 +43,9 @@ mod allow { pub(super) const NON_CAMEL_CASE_TYPES: &str = "non_camel_case_types"; } -pub fn incorrect_case( - db: &dyn HirDatabase, - krate: CrateId, - owner: ModuleDefId, -) -> Vec { +pub fn incorrect_case(db: &dyn HirDatabase, owner: ModuleDefId) -> Vec { let _p = profile::span("validate_module_item"); - let mut validator = DeclValidator::new(db, krate); + let mut validator = DeclValidator::new(db); validator.validate_item(owner); validator.sink } @@ -120,7 +115,6 @@ pub struct IncorrectCase { pub(super) struct DeclValidator<'a> { db: &'a dyn HirDatabase, - krate: CrateId, pub(super) sink: Vec, } @@ -132,8 +126,8 @@ struct Replacement { } impl<'a> DeclValidator<'a> { - pub(super) fn new(db: &'a dyn HirDatabase, krate: CrateId) -> DeclValidator<'a> { - DeclValidator { db, krate, sink: Vec::new() } + pub(super) fn new(db: &'a dyn HirDatabase) -> DeclValidator<'a> { + DeclValidator { db, sink: Vec::new() } } pub(super) fn validate_item(&mut self, item: ModuleDefId) { @@ -195,8 +189,7 @@ fn allowed(&self, id: AttrDefId, allow_name: &str, recursing: bool) -> bool { AttrDefId::TypeAliasId(_) => None, AttrDefId::GenericParamId(_) => None, } - .map(|mid| self.allowed(mid, allow_name, true)) - .unwrap_or(false) + .is_some_and(|mid| self.allowed(mid, allow_name, true)) } fn validate_func(&mut self, func: FunctionId) { @@ -206,17 +199,7 @@ fn validate_func(&mut self, func: FunctionId) { return; } - let body = self.db.body(func.into()); - - // Recursively validate inner scope items, such as static variables and constants. - for (_, block_def_map) in body.blocks(self.db.upcast()) { - for (_, module) in block_def_map.modules() { - for def_id in module.scope.declarations() { - let mut validator = DeclValidator::new(self.db, self.krate); - validator.validate_item(def_id); - } - } - } + self.validate_body_inner_items(func.into()); // Check whether non-snake case identifiers are allowed for this function. if self.allowed(func.into(), allow::NON_SNAKE_CASE, false) { @@ -231,6 +214,8 @@ fn validate_func(&mut self, func: FunctionId) { expected_case: CaseType::LowerSnakeCase, }); + let body = self.db.body(func.into()); + // Check the patterns inside the function body. // This includes function parameters. let pats_replacements = body @@ -496,6 +481,11 @@ fn create_incorrect_case_diagnostic_for_struct( fn validate_enum(&mut self, enum_id: EnumId) { let data = self.db.enum_data(enum_id); + for (local_id, _) in data.variants.iter() { + let variant_id = EnumVariantId { parent: enum_id, local_id }; + self.validate_body_inner_items(variant_id.into()); + } + // Check whether non-camel case names are allowed for this enum. if self.allowed(enum_id.into(), allow::NON_CAMEL_CASE_TYPES, false) { return; @@ -512,13 +502,11 @@ fn validate_enum(&mut self, enum_id: EnumId) { // Check the field names. let enum_fields_replacements = data .variants - .iter() - .filter_map(|(_, variant)| { + .values() + .filter_map(|variant| { Some(Replacement { current_name: variant.name.clone(), - suggested_text: to_camel_case( - &variant.name.display(self.db.upcast()).to_string(), - )?, + suggested_text: to_camel_case(&variant.name.to_smol_str())?, expected_case: CaseType::UpperCamelCase, }) }) @@ -622,6 +610,8 @@ fn create_incorrect_case_diagnostic_for_enum( fn validate_const(&mut self, const_id: ConstId) { let data = self.db.const_data(const_id); + self.validate_body_inner_items(const_id.into()); + if self.allowed(const_id.into(), allow::NON_UPPER_CASE_GLOBAL, false) { return; } @@ -631,7 +621,7 @@ fn validate_const(&mut self, const_id: ConstId) { None => return, }; - let const_name = name.display(self.db.upcast()).to_string(); + let const_name = name.to_smol_str(); let replacement = if let Some(new_name) = to_upper_snake_case(&const_name) { Replacement { current_name: name.clone(), @@ -670,13 +660,15 @@ fn validate_static(&mut self, static_id: StaticId) { return; } + self.validate_body_inner_items(static_id.into()); + if self.allowed(static_id.into(), allow::NON_UPPER_CASE_GLOBAL, false) { return; } let name = &data.name; - let static_name = name.display(self.db.upcast()).to_string(); + let static_name = name.to_smol_str(); let replacement = if let Some(new_name) = to_upper_snake_case(&static_name) { Replacement { current_name: name.clone(), @@ -707,4 +699,17 @@ fn validate_static(&mut self, static_id: StaticId) { self.sink.push(diagnostic); } + + // FIXME: We don't currently validate names within `DefWithBodyId::InTypeConstId`. + /// Recursively validates inner scope items, such as static variables and constants. + fn validate_body_inner_items(&mut self, body_id: DefWithBodyId) { + let body = self.db.body(body_id); + for (_, block_def_map) in body.blocks(self.db.upcast()) { + for (_, module) in block_def_map.modules() { + for def_id in module.scope.declarations() { + self.validate_item(def_id); + } + } + } + } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 8cffdef289e..96787959e1f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -29,6 +29,7 @@ use la_arena::ArenaMap; use smallvec::SmallVec; use stdx::never; +use triomphe::Arc; use crate::{ consteval::try_const_usize, @@ -43,7 +44,7 @@ AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Const, ConstScalar, ConstValue, DomainGoal, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, LifetimeOutlives, MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Scalar, - Substitution, TraitRef, TraitRefExt, Ty, TyExt, WhereClause, + Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyExt, WhereClause, }; pub trait HirWrite: fmt::Write { @@ -454,7 +455,9 @@ fn render_const_scalar( ) -> Result<(), HirDisplayError> { // FIXME: We need to get krate from the final callers of the hir display // infrastructure and have it here as a field on `f`. - let krate = *f.db.crate_graph().crates_in_topological_order().last().unwrap(); + let trait_env = Arc::new(TraitEnvironment::empty( + *f.db.crate_graph().crates_in_topological_order().last().unwrap(), + )); match ty.kind(Interner) { TyKind::Scalar(s) => match s { Scalar::Bool => write!(f, "{}", if b[0] == 0 { false } else { true }), @@ -497,7 +500,7 @@ fn render_const_scalar( TyKind::Slice(ty) => { let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap()); let count = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap()); - let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else { + let Ok(layout) = f.db.layout_of_ty(ty.clone(), trait_env) else { return f.write_str(""); }; let size_one = layout.size.bytes_usize(); @@ -523,7 +526,7 @@ fn render_const_scalar( let Ok(t) = memory_map.vtable.ty(ty_id) else { return f.write_str(""); }; - let Ok(layout) = f.db.layout_of_ty(t.clone(), krate) else { + let Ok(layout) = f.db.layout_of_ty(t.clone(), trait_env) else { return f.write_str(""); }; let size = layout.size.bytes_usize(); @@ -555,7 +558,7 @@ fn render_const_scalar( return f.write_str(""); } }); - let Ok(layout) = f.db.layout_of_ty(t.clone(), krate) else { + let Ok(layout) = f.db.layout_of_ty(t.clone(), trait_env) else { return f.write_str(""); }; let size = layout.size.bytes_usize(); @@ -567,7 +570,7 @@ fn render_const_scalar( } }, TyKind::Tuple(_, subst) => { - let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else { + let Ok(layout) = f.db.layout_of_ty(ty.clone(), trait_env.clone()) else { return f.write_str(""); }; f.write_str("(")?; @@ -580,7 +583,7 @@ fn render_const_scalar( } let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument let offset = layout.fields.offset(id).bytes_usize(); - let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else { + let Ok(layout) = f.db.layout_of_ty(ty.clone(), trait_env.clone()) else { f.write_str("")?; continue; }; @@ -590,7 +593,7 @@ fn render_const_scalar( f.write_str(")") } TyKind::Adt(adt, subst) => { - let Ok(layout) = f.db.layout_of_adt(adt.0, subst.clone(), krate) else { + let Ok(layout) = f.db.layout_of_adt(adt.0, subst.clone(), trait_env.clone()) else { return f.write_str(""); }; match adt.0 { @@ -602,7 +605,7 @@ fn render_const_scalar( &data.variant_data, f, &field_types, - adt.0.module(f.db.upcast()).krate(), + f.db.trait_environment(adt.0.into()), &layout, subst, b, @@ -614,7 +617,7 @@ fn render_const_scalar( } hir_def::AdtId::EnumId(e) => { let Some((var_id, var_layout)) = - detect_variant_from_bytes(&layout, f.db, krate, b, e) + detect_variant_from_bytes(&layout, f.db, trait_env.clone(), b, e) else { return f.write_str(""); }; @@ -626,7 +629,7 @@ fn render_const_scalar( &data.variant_data, f, &field_types, - adt.0.module(f.db.upcast()).krate(), + f.db.trait_environment(adt.0.into()), &var_layout, subst, b, @@ -645,7 +648,7 @@ fn render_const_scalar( let Some(len) = try_const_usize(f.db, len) else { return f.write_str(""); }; - let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else { + let Ok(layout) = f.db.layout_of_ty(ty.clone(), trait_env) else { return f.write_str(""); }; let size_one = layout.size.bytes_usize(); @@ -684,7 +687,7 @@ fn render_variant_after_name( data: &VariantData, f: &mut HirFormatter<'_>, field_types: &ArenaMap>, - krate: CrateId, + trait_env: Arc, layout: &Layout, subst: &Substitution, b: &[u8], @@ -695,7 +698,7 @@ fn render_variant_after_name( let render_field = |f: &mut HirFormatter<'_>, id: LocalFieldId| { let offset = layout.fields.offset(u32::from(id.into_raw()) as usize).bytes_usize(); let ty = field_types[id].clone().substitute(Interner, subst); - let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else { + let Ok(layout) = f.db.layout_of_ty(ty.clone(), trait_env.clone()) else { return f.write_str(""); }; let size = layout.size.bytes_usize(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 4b14345aa39..72e6443beb7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -1665,6 +1665,7 @@ fn check_call_arguments( // the parameter to coerce to the expected type (for example in // `coerce_unsize_expected_type_4`). let param_ty = self.normalize_associated_types_in(param_ty); + let expected_ty = self.normalize_associated_types_in(expected_ty); let expected = Expectation::rvalue_hint(self, expected_ty); // infer with the expected type we have... let ty = self.infer_expr_inner(arg, &expected); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 385f39f5374..0fb71135b4d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -252,7 +252,8 @@ pub(crate) fn normalize_associated_types_in(&mut self, ty: T) -> T // and registering an obligation. But it needs chalk support, so we handle the most basic // case (a non associated const without generic parameters) manually. if subst.len(Interner) == 0 { - if let Ok(eval) = self.db.const_eval((*c_id).into(), subst.clone()) + if let Ok(eval) = + self.db.const_eval((*c_id).into(), subst.clone(), None) { eval } else { @@ -785,7 +786,7 @@ pub(super) fn insert_const_vars_shallow(&mut self, c: Const) -> Const { crate::ConstScalar::Unknown => self.new_const_var(data.ty.clone()), // try to evaluate unevaluated const. Replace with new var if const eval failed. crate::ConstScalar::UnevaluatedConst(id, subst) => { - if let Ok(eval) = self.db.const_eval(*id, subst.clone()) { + if let Ok(eval) = self.db.const_eval(*id, subst.clone(), None) { eval } else { self.new_const_var(data.ty.clone()) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 72e2bcc5559..ffc7a6f2ebd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -1,6 +1,5 @@ //! Compute the binary representation of a type -use base_db::CrateId; use chalk_ir::{AdtId, FloatTy, IntTy, TyKind, UintTy}; use hir_def::{ layout::{ @@ -61,7 +60,6 @@ pub enum LayoutError { } struct LayoutCx<'a> { - krate: CrateId, target: &'a TargetDataLayout, } @@ -82,7 +80,7 @@ fn layout_of_simd_ty( db: &dyn HirDatabase, id: StructId, subst: &Substitution, - krate: CrateId, + env: Arc, dl: &TargetDataLayout, ) -> Result, LayoutError> { let fields = db.field_types(id.into()); @@ -111,7 +109,7 @@ fn layout_of_simd_ty( // * the homogeneous field type and the number of fields. let (e_ty, e_len, is_array) = if let TyKind::Array(e_ty, _) = f0_ty.kind(Interner) { // Extract the number of elements from the layout of the array field: - let FieldsShape::Array { count, .. } = db.layout_of_ty(f0_ty.clone(), krate)?.fields else { + let FieldsShape::Array { count, .. } = db.layout_of_ty(f0_ty.clone(), env.clone())?.fields else { user_error!("Array with non array layout"); }; @@ -122,7 +120,7 @@ fn layout_of_simd_ty( }; // Compute the ABI of the element type: - let e_ly = db.layout_of_ty(e_ty, krate)?; + let e_ly = db.layout_of_ty(e_ty, env.clone())?; let Abi::Scalar(e_abi) = e_ly.abi else { user_error!("simd type with inner non scalar type"); }; @@ -152,25 +150,25 @@ fn layout_of_simd_ty( pub fn layout_of_ty_query( db: &dyn HirDatabase, ty: Ty, - krate: CrateId, + trait_env: Arc, ) -> Result, LayoutError> { + let krate = trait_env.krate; let Some(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable); }; - let cx = LayoutCx { krate, target: &target }; + let cx = LayoutCx { target: &target }; let dl = &*cx.current_data_layout(); - let trait_env = Arc::new(TraitEnvironment::empty(krate)); - let ty = normalize(db, trait_env, ty.clone()); + let ty = normalize(db, trait_env.clone(), ty.clone()); let result = match ty.kind(Interner) { TyKind::Adt(AdtId(def), subst) => { if let hir_def::AdtId::StructId(s) = def { let data = db.struct_data(*s); let repr = data.repr.unwrap_or_default(); if repr.simd() { - return layout_of_simd_ty(db, *s, subst, krate, &target); + return layout_of_simd_ty(db, *s, subst, trait_env.clone(), &target); } }; - return db.layout_of_adt(*def, subst.clone(), krate); + return db.layout_of_adt(*def, subst.clone(), trait_env.clone()); } TyKind::Scalar(s) => match s { chalk_ir::Scalar::Bool => Layout::scalar( @@ -228,7 +226,7 @@ pub fn layout_of_ty_query( let fields = tys .iter(Interner) - .map(|k| db.layout_of_ty(k.assert_ty_ref(Interner).clone(), krate)) + .map(|k| db.layout_of_ty(k.assert_ty_ref(Interner).clone(), trait_env.clone())) .collect::, _>>()?; let fields = fields.iter().map(|it| &**it).collect::>(); let fields = fields.iter().collect::>(); @@ -238,7 +236,7 @@ pub fn layout_of_ty_query( let count = try_const_usize(db, &count).ok_or(LayoutError::UserError( "unevaluated or mistyped const generic parameter".to_string(), ))? as u64; - let element = db.layout_of_ty(element.clone(), krate)?; + let element = db.layout_of_ty(element.clone(), trait_env.clone())?; let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow)?; let abi = if count != 0 && matches!(element.abi, Abi::Uninhabited) { @@ -259,7 +257,7 @@ pub fn layout_of_ty_query( } } TyKind::Slice(element) => { - let element = db.layout_of_ty(element.clone(), krate)?; + let element = db.layout_of_ty(element.clone(), trait_env.clone())?; Layout { variants: Variants::Single { index: struct_variant_idx() }, fields: FieldsShape::Array { stride: element.size, count: 0 }, @@ -335,7 +333,7 @@ pub fn layout_of_ty_query( match impl_trait_id { crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { let infer = db.infer(func.into()); - return db.layout_of_ty(infer.type_of_rpit[idx].clone(), krate); + return db.layout_of_ty(infer.type_of_rpit[idx].clone(), trait_env.clone()); } crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { return Err(LayoutError::NotImplemented) @@ -351,7 +349,7 @@ pub fn layout_of_ty_query( .map(|it| { db.layout_of_ty( it.ty.clone().substitute(Interner, ClosureSubst(subst).parent_subst()), - krate, + trait_env.clone(), ) }) .collect::, _>>()?; @@ -377,7 +375,7 @@ pub fn layout_of_ty_recover( _: &dyn HirDatabase, _: &[String], _: &Ty, - _: &CrateId, + _: &Arc, ) -> Result, LayoutError> { user_error!("infinite sized recursive type"); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs index 19d5e98e738..1c92e80f335 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs @@ -2,7 +2,6 @@ use std::{cmp, ops::Bound}; -use base_db::CrateId; use hir_def::{ data::adt::VariantData, layout::{Integer, LayoutCalculator, ReprOptions, TargetDataLayout}, @@ -16,7 +15,7 @@ db::HirDatabase, lang_items::is_unsafe_cell, layout::{field_ty, Layout, LayoutError, RustcEnumVariantIdx}, - Substitution, + Substitution, TraitEnvironment, }; use super::LayoutCx; @@ -29,17 +28,18 @@ pub fn layout_of_adt_query( db: &dyn HirDatabase, def: AdtId, subst: Substitution, - krate: CrateId, + trait_env: Arc, ) -> Result, LayoutError> { + let krate = trait_env.krate; let Some(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable); }; - let cx = LayoutCx { krate, target: &target }; + let cx = LayoutCx { target: &target }; let dl = cx.current_data_layout(); let handle_variant = |def: VariantId, var: &VariantData| { var.fields() .iter() - .map(|(fd, _)| db.layout_of_ty(field_ty(db, def, fd, &subst), cx.krate)) + .map(|(fd, _)| db.layout_of_ty(field_ty(db, def, fd, &subst), trait_env.clone())) .collect::, _>>() }; let (variants, repr) = match def { @@ -134,7 +134,7 @@ pub fn layout_of_adt_recover( _: &[String], _: &AdtId, _: &Substitution, - _: &CrateId, + _: &Arc, ) -> Result, LayoutError> { user_error!("infinite sized recursive type"); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index a3ced2bac0a..333ad473a8b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -26,7 +26,7 @@ fn eval_goal(ra_fixture: &str, minicore: &str) -> Result, LayoutErro ); let (db, file_ids) = TestDB::with_many_files(&ra_fixture); - let (adt_or_type_alias_id, module_id) = file_ids + let adt_or_type_alias_id = file_ids .into_iter() .find_map(|file_id| { let module_id = db.module_for_file(file_id); @@ -47,7 +47,7 @@ fn eval_goal(ra_fixture: &str, minicore: &str) -> Result, LayoutErro } _ => None, })?; - Some((adt_or_type_alias_id, module_id)) + Some(adt_or_type_alias_id) }) .unwrap(); let goal_ty = match adt_or_type_alias_id { @@ -58,7 +58,13 @@ fn eval_goal(ra_fixture: &str, minicore: &str) -> Result, LayoutErro db.ty(ty_id.into()).substitute(Interner, &Substitution::empty(Interner)) } }; - db.layout_of_ty(goal_ty, module_id.krate()) + db.layout_of_ty( + goal_ty, + db.trait_environment(match adt_or_type_alias_id { + Either::Left(adt) => hir_def::GenericDefId::AdtId(adt), + Either::Right(ty) => hir_def::GenericDefId::TypeAliasId(ty), + }), + ) } /// A version of `eval_goal` for types that can not be expressed in ADTs, like closures and `impl Trait` @@ -72,7 +78,7 @@ fn eval_expr(ra_fixture: &str, minicore: &str) -> Result, LayoutErro let module_id = db.module_for_file(file_id); let def_map = module_id.def_map(&db); let scope = &def_map[module_id.local_id].scope; - let adt_id = scope + let function_id = scope .declarations() .find_map(|x| match x { hir_def::ModuleDefId::FunctionId(x) => { @@ -82,11 +88,11 @@ fn eval_expr(ra_fixture: &str, minicore: &str) -> Result, LayoutErro _ => None, }) .unwrap(); - let hir_body = db.body(adt_id.into()); + let hir_body = db.body(function_id.into()); let b = hir_body.bindings.iter().find(|x| x.1.name.to_smol_str() == "goal").unwrap().0; - let infer = db.infer(adt_id.into()); + let infer = db.infer(function_id.into()); let goal_ty = infer.type_of_binding[b].clone(); - db.layout_of_ty(goal_ty, module_id.krate()) + db.layout_of_ty(goal_ty, db.trait_environment(function_id.into())) } #[track_caller] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 5e1040bc6aa..f3a5f69b2a6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -665,13 +665,21 @@ pub fn is_dyn_method( }; let self_ty = trait_ref.self_type_parameter(Interner); if let TyKind::Dyn(d) = self_ty.kind(Interner) { - let is_my_trait_in_bounds = - d.bounds.skip_binders().as_slice(Interner).iter().any(|it| match it.skip_binders() { - // rustc doesn't accept `impl Foo<2> for dyn Foo<5>`, so if the trait id is equal, no matter - // what the generics are, we are sure that the method is come from the vtable. - WhereClause::Implemented(tr) => tr.trait_id == trait_ref.trait_id, - _ => false, - }); + let is_my_trait_in_bounds = d + .bounds + .skip_binders() + .as_slice(Interner) + .iter() + .map(|it| it.skip_binders()) + .flat_map(|it| match it { + WhereClause::Implemented(tr) => { + all_super_traits(db.upcast(), from_chalk_trait_id(tr.trait_id)) + } + _ => smallvec![], + }) + // rustc doesn't accept `impl Foo<2> for dyn Foo<5>`, so if the trait id is equal, no matter + // what the generics are, we are sure that the method is come from the vtable. + .any(|x| x == trait_id); if is_my_trait_in_bounds { return Some(fn_params); } @@ -1504,7 +1512,7 @@ fn autoderef_method_receiver( ty: Ty, ) -> Vec<(Canonical, ReceiverAdjustments)> { let mut deref_chain: Vec<_> = Vec::new(); - let mut autoderef = autoderef::Autoderef::new(table, ty, true); + let mut autoderef = autoderef::Autoderef::new(table, ty, false); while let Some((ty, derefs)) = autoderef.next() { deref_chain.push(( autoderef.table.canonicalize(ty).value, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs index da5b496e141..922e49d281d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -142,7 +142,7 @@ pub fn projected_ty( closure_field: impl FnOnce(ClosureId, &Substitution, usize) -> Ty, krate: CrateId, ) -> Ty { - if matches!(base.data(Interner).kind, TyKind::Alias(_) | TyKind::AssociatedType(..)) { + if matches!(base.kind(Interner), TyKind::Alias(_) | TyKind::AssociatedType(..)) { base = normalize( db, // FIXME: we should get this from caller @@ -151,7 +151,7 @@ pub fn projected_ty( ); } match self { - ProjectionElem::Deref => match &base.data(Interner).kind { + ProjectionElem::Deref => match &base.kind(Interner) { TyKind::Raw(_, inner) | TyKind::Ref(_, _, inner) => inner.clone(), TyKind::Adt(adt, subst) if is_box(db, adt.0) => { subst.at(Interner, 0).assert_ty_ref(Interner).clone() @@ -161,7 +161,7 @@ pub fn projected_ty( return TyKind::Error.intern(Interner); } }, - ProjectionElem::Field(f) => match &base.data(Interner).kind { + ProjectionElem::Field(f) => match &base.kind(Interner) { TyKind::Adt(_, subst) => { db.field_types(f.parent)[f.local_id].clone().substitute(Interner, subst) } @@ -170,7 +170,7 @@ pub fn projected_ty( return TyKind::Error.intern(Interner); } }, - ProjectionElem::TupleOrClosureField(f) => match &base.data(Interner).kind { + ProjectionElem::TupleOrClosureField(f) => match &base.kind(Interner) { TyKind::Tuple(_, subst) => subst .as_slice(Interner) .get(*f) @@ -187,7 +187,7 @@ pub fn projected_ty( } }, ProjectionElem::ConstantIndex { .. } | ProjectionElem::Index(_) => { - match &base.data(Interner).kind { + match &base.kind(Interner) { TyKind::Array(inner, _) | TyKind::Slice(inner) => inner.clone(), _ => { never!("Overloaded index is not a projection"); @@ -195,7 +195,7 @@ pub fn projected_ty( } } } - &ProjectionElem::Subslice { from, to } => match &base.data(Interner).kind { + &ProjectionElem::Subslice { from, to } => match &base.kind(Interner) { TyKind::Array(inner, c) => { let next_c = usize_const( db, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index d7820de629a..7bd2756c14f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -484,9 +484,10 @@ pub fn interpret_mir( // a zero size, hoping that they are all outside of our current body. Even without a fix for #7434, we can // (and probably should) do better here, for example by excluding bindings outside of the target expression. assert_placeholder_ty_is_unused: bool, + trait_env: Option>, ) -> (Result, String, String) { let ty = body.locals[return_slot()].ty.clone(); - let mut evaluator = Evaluator::new(db, body.owner, assert_placeholder_ty_is_unused); + let mut evaluator = Evaluator::new(db, body.owner, assert_placeholder_ty_is_unused, trait_env); let it: Result = (|| { if evaluator.ptr_size() != std::mem::size_of::() { not_supported!("targets with different pointer size from host"); @@ -512,9 +513,9 @@ pub fn new<'a>( db: &'a dyn HirDatabase, owner: DefWithBodyId, assert_placeholder_ty_is_unused: bool, + trait_env: Option>, ) -> Evaluator<'a> { let crate_id = owner.module(db.upcast()).krate(); - let trait_env = db.trait_environment_for_body(owner); Evaluator { stack: vec![0], heap: vec![0], @@ -524,7 +525,7 @@ pub fn new<'a>( static_locations: HashMap::default(), db, random_state: oorandom::Rand64::new(0), - trait_env, + trait_env: trait_env.unwrap_or_else(|| db.trait_environment_for_body(owner)), crate_id, stdout: vec![], stderr: vec![], @@ -634,7 +635,7 @@ fn place_addr_and_ty_and_metadata<'a>( addr = addr.offset(ty_size * offset); } &ProjectionElem::Subslice { from, to } => { - let inner_ty = match &ty.data(Interner).kind { + let inner_ty = match &ty.kind(Interner) { TyKind::Array(inner, _) | TyKind::Slice(inner) => inner.clone(), _ => TyKind::Error.intern(Interner), }; @@ -694,14 +695,14 @@ fn layout(&self, ty: &Ty) -> Result> { } let r = self .db - .layout_of_ty(ty.clone(), self.crate_id) + .layout_of_ty(ty.clone(), self.trait_env.clone()) .map_err(|e| MirEvalError::LayoutError(e, ty.clone()))?; self.layout_cache.borrow_mut().insert(ty.clone(), r.clone()); Ok(r) } fn layout_adt(&self, adt: AdtId, subst: Substitution) -> Result> { - self.db.layout_of_adt(adt, subst.clone(), self.crate_id).map_err(|e| { + self.db.layout_of_adt(adt, subst.clone(), self.trait_env.clone()).map_err(|e| { MirEvalError::LayoutError(e, TyKind::Adt(chalk_ir::AdtId(adt), subst).intern(Interner)) }) } @@ -793,7 +794,7 @@ fn interpret_mir( .iter() .map(|it| self.operand_ty_and_eval(it, &mut locals)) .collect::>>()?; - let stack_frame = match &fn_ty.data(Interner).kind { + let stack_frame = match &fn_ty.kind(Interner) { TyKind::Function(_) => { let bytes = self.eval_operand(func, &mut locals)?; self.exec_fn_pointer( @@ -1255,7 +1256,7 @@ fn eval_rvalue(&mut self, r: &Rvalue, locals: &mut Locals) -> Result { let current_ty = self.operand_ty(operand, locals)?; if let TyKind::FnDef(_, _) | TyKind::Closure(_, _) = - ¤t_ty.data(Interner).kind + ¤t_ty.kind(Interner) { let id = self.vtable_map.id(current_ty); let ptr_size = self.ptr_size(); @@ -1408,8 +1409,8 @@ fn unsizing_ptr_from_addr( addr: Interval, ) -> Result { use IntervalOrOwned::*; - Ok(match &target_ty.data(Interner).kind { - TyKind::Slice(_) => match ¤t_ty.data(Interner).kind { + Ok(match &target_ty.kind(Interner) { + TyKind::Slice(_) => match ¤t_ty.kind(Interner) { TyKind::Array(_, size) => { let len = match try_const_usize(self.db, size) { None => { @@ -1435,7 +1436,7 @@ fn unsizing_ptr_from_addr( r.extend(vtable.to_le_bytes().into_iter()); Owned(r) } - TyKind::Adt(id, target_subst) => match ¤t_ty.data(Interner).kind { + TyKind::Adt(id, target_subst) => match ¤t_ty.kind(Interner) { TyKind::Adt(current_id, current_subst) => { if id != current_id { not_supported!("unsizing struct with different type"); @@ -1582,10 +1583,13 @@ fn allocate_const_in_heap(&mut self, locals: &Locals, konst: &Const) -> Result { let layout = this.layout(ty)?; - if let Some((v, l)) = - detect_variant_from_bytes(&layout, this.db, this.crate_id, bytes, e) - { + if let Some((v, l)) = detect_variant_from_bytes( + &layout, + this.db, + this.trait_env.clone(), + bytes, + e, + ) { let data = &this.db.enum_data(e).variants[v].variant_data; let field_types = this .db @@ -1931,7 +1939,7 @@ fn exec_fn_pointer( ) -> Result> { let id = from_bytes!(usize, bytes.get(self)?); let next_ty = self.vtable_map.ty(id)?.clone(); - match &next_ty.data(Interner).kind { + match &next_ty.kind(Interner) { TyKind::FnDef(def, generic_args) => { self.exec_fn_def(*def, generic_args, destination, args, &locals, target_bb, span) } @@ -2182,7 +2190,7 @@ fn exec_fn_trait( let size = self.size_of_sized(&func_ty, locals, "self type of fn trait")?; func_data = Interval { addr: Address::from_bytes(func_data.get(self)?)?, size }; } - match &func_ty.data(Interner).kind { + match &func_ty.kind(Interner) { TyKind::FnDef(def, subst) => { return self.exec_fn_def( *def, @@ -2409,7 +2417,7 @@ pub fn render_const_using_debug_impl( owner: ConstId, c: &Const, ) -> Result { - let mut evaluator = Evaluator::new(db, owner.into(), false); + let mut evaluator = Evaluator::new(db, owner.into(), false, None); let locals = &Locals { ptr: ArenaMap::new(), body: db diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs index 03c083bac42..93f4b699147 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs @@ -30,7 +30,7 @@ fn eval_main(db: &TestDB, file_id: FileId) -> Result<(String, String), MirEvalEr db.trait_environment(func_id.into()), ) .map_err(|e| MirEvalError::MirLowerError(func_id.into(), e))?; - let (result, stdout, stderr) = interpret_mir(db, body, false); + let (result, stdout, stderr) = interpret_mir(db, body, false, None); result?; Ok((stdout, stderr)) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 36108587904..9f25175a3a9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -633,7 +633,7 @@ fn lower_expr_to_place_without_adjust( ); } let callee_ty = self.expr_ty_after_adjustments(*callee); - match &callee_ty.data(Interner).kind { + match &callee_ty.kind(Interner) { chalk_ir::TyKind::FnDef(..) => { let func = Operand::from_bytes(vec![], callee_ty.clone()); self.lower_call_and_args( @@ -1229,7 +1229,7 @@ fn lower_expr_to_place_without_adjust( } Expr::Array(l) => match l { Array::ElementList { elements, .. } => { - let elem_ty = match &self.expr_ty_without_adjust(expr_id).data(Interner).kind { + let elem_ty = match &self.expr_ty_without_adjust(expr_id).kind(Interner) { TyKind::Array(ty, _) => ty.clone(), _ => { return Err(MirLowerError::TypeError( @@ -1260,7 +1260,7 @@ fn lower_expr_to_place_without_adjust( else { return Ok(None); }; - let len = match &self.expr_ty_without_adjust(expr_id).data(Interner).kind { + let len = match &self.expr_ty_without_adjust(expr_id).kind(Interner) { TyKind::Array(_, len) => len.clone(), _ => { return Err(MirLowerError::TypeError( @@ -1341,7 +1341,7 @@ fn lower_literal_or_const_to_operand( fn lower_literal_to_operand(&mut self, ty: Ty, l: &Literal) -> Result { let size = self .db - .layout_of_ty(ty.clone(), self.owner.module(self.db.upcast()).krate())? + .layout_of_ty(ty.clone(), self.db.trait_environment_for_body(self.owner))? .size .bytes_usize(); let bytes = match l { @@ -1355,7 +1355,6 @@ fn lower_literal_to_operand(&mut self, ty: Ty, l: &Literal) -> Result { return Ok(Operand::from_concrete_const(data, mm, ty)); } hir_def::hir::Literal::CString(b) => { - let b = b.as_bytes(); let bytes = b.iter().copied().chain(iter::once(0)).collect::>(); let mut data = Vec::with_capacity(mem::size_of::() * 2); @@ -1418,7 +1417,7 @@ fn lower_const_to_operand( } else { let name = const_id.name(self.db.upcast()); self.db - .const_eval(const_id.into(), subst) + .const_eval(const_id.into(), subst, None) .map_err(|e| MirLowerError::ConstEvalError(name, Box::new(e)))? }; Ok(Operand::Constant(c)) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index a8e146b096a..c837fae3fef 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -1236,6 +1236,27 @@ fn main() { ); } +#[test] +fn inherent_method_ref_self_deref_raw() { + check_types( + r#" +struct Val; + +impl Val { + pub fn method(&self) -> u32 { + 0 + } +} + +fn main() { + let foo: *const Val; + foo.method(); + // ^^^^^^^^^^^^ {unknown} +} +"#, + ); +} + #[test] fn trait_method_deref_raw() { check_types( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index 5f5cd794512..542df8b3468 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -4434,3 +4434,47 @@ fn test(v: S) { "#, ); } + +#[test] +fn associated_type_in_argument() { + check( + r#" + trait A { + fn m(&self) -> i32; + } + + fn x(k: &::Ty) { + k.m(); + } + + struct X; + struct Y; + + impl A for X { + fn m(&self) -> i32 { + 8 + } + } + + impl A for Y { + fn m(&self) -> i32 { + 32 + } + } + + trait B { + type Ty: A; + } + + impl B for u16 { + type Ty = X; + } + + fn ttt() { + let inp = Y; + x::(&inp); + //^^^^ expected &X, got &Y + } + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs index 0c38fe5d6ab..75b8b9afa70 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs @@ -28,14 +28,15 @@ use rustc_hash::FxHashSet; use smallvec::{smallvec, SmallVec}; use stdx::never; +use triomphe::Arc; use crate::{ consteval::unknown_const, db::HirDatabase, layout::{Layout, TagEncoding}, mir::pad16, - ChalkTraitId, Const, ConstScalar, GenericArg, Interner, Substitution, TraitRef, TraitRefExt, - Ty, WhereClause, + ChalkTraitId, Const, ConstScalar, GenericArg, Interner, Substitution, TraitEnvironment, + TraitRef, TraitRefExt, Ty, WhereClause, }; pub(crate) fn fn_traits( @@ -417,7 +418,7 @@ fn try_fold_const( ) -> Result { if let chalk_ir::ConstValue::Concrete(c) = &constant.data(Interner).value { if let ConstScalar::UnevaluatedConst(id, subst) = &c.interned { - if let Ok(eval) = self.db.const_eval(*id, subst.clone()) { + if let Ok(eval) = self.db.const_eval(*id, subst.clone(), None) { return Ok(eval); } else { return Ok(unknown_const(constant.data(Interner).ty.clone())); @@ -431,10 +432,11 @@ fn try_fold_const( pub(crate) fn detect_variant_from_bytes<'a>( layout: &'a Layout, db: &dyn HirDatabase, - krate: CrateId, + trait_env: Arc, b: &[u8], e: EnumId, ) -> Option<(LocalEnumVariantId, &'a Layout)> { + let krate = trait_env.krate; let (var_id, var_layout) = match &layout.variants { hir_def::layout::Variants::Single { index } => (index.0, &*layout), hir_def::layout::Variants::Multiple { tag, tag_encoding, variants, .. } => { diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index f8d9398ae2c..b094bb7a068 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -378,11 +378,6 @@ pub fn diagnostics(self, db: &dyn HirDatabase) -> Vec { ModuleDef::BuiltinType(_) | ModuleDef::Macro(_) => return Vec::new(), }; - let module = match self.module(db) { - Some(it) => it, - None => return Vec::new(), - }; - let mut acc = Vec::new(); match self.as_def_with_body() { @@ -390,7 +385,7 @@ pub fn diagnostics(self, db: &dyn HirDatabase) -> Vec { def.diagnostics(db, &mut acc); } None => { - for diag in hir_ty::diagnostics::incorrect_case(db, module.id.krate(), id) { + for diag in hir_ty::diagnostics::incorrect_case(db, id) { acc.push(diag.into()) } } @@ -965,8 +960,15 @@ pub fn ty(&self, db: &dyn HirDatabase) -> Type { } pub fn layout(&self, db: &dyn HirDatabase) -> Result { - db.layout_of_ty(self.ty(db).ty.clone(), self.parent.module(db).krate().into()) - .map(|layout| Layout(layout, db.target_data_layout(self.krate(db).into()).unwrap())) + db.layout_of_ty( + self.ty(db).ty.clone(), + db.trait_environment(match hir_def::VariantId::from(self.parent) { + hir_def::VariantId::EnumVariantId(id) => GenericDefId::EnumVariantId(id), + hir_def::VariantId::StructId(id) => GenericDefId::AdtId(id.into()), + hir_def::VariantId::UnionId(id) => GenericDefId::AdtId(id.into()), + }), + ) + .map(|layout| Layout(layout, db.target_data_layout(self.krate(db).into()).unwrap())) } pub fn parent_def(&self, _db: &dyn HirDatabase) -> VariantDef { @@ -1246,8 +1248,12 @@ pub fn layout(self, db: &dyn HirDatabase) -> Result { return Err(LayoutError::HasPlaceholder); } let krate = self.krate(db).id; - db.layout_of_adt(self.into(), Substitution::empty(Interner), krate) - .map(|layout| Layout(layout, db.target_data_layout(krate).unwrap())) + db.layout_of_adt( + self.into(), + Substitution::empty(Interner), + db.trait_environment(self.into()), + ) + .map(|layout| Layout(layout, db.target_data_layout(krate).unwrap())) } /// Turns this ADT into a type. Any type parameters of the ADT will be @@ -1820,7 +1826,7 @@ pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec) { // FIXME: don't ignore diagnostics for in type const DefWithBody::InTypeConst(_) => return, }; - for diag in hir_ty::diagnostics::incorrect_case(db, krate, def.into()) { + for diag in hir_ty::diagnostics::incorrect_case(db, def.into()) { acc.push(diag.into()) } } @@ -1987,7 +1993,7 @@ pub fn eval( return r; } }; - let (result, stdout, stderr) = interpret_mir(db, body, false); + let (result, stdout, stderr) = interpret_mir(db, body, false, None); let mut text = match result { Ok(_) => "pass".to_string(), Err(e) => { @@ -2156,7 +2162,7 @@ pub fn ty(self, db: &dyn HirDatabase) -> Type { } pub fn render_eval(self, db: &dyn HirDatabase) -> Result { - let c = db.const_eval(self.id.into(), Substitution::empty(Interner))?; + let c = db.const_eval(self.id.into(), Substitution::empty(Interner), None)?; let data = &c.data(Interner); if let TyKind::Scalar(s) = data.ty.kind(Interner) { if matches!(s, Scalar::Int(_) | Scalar::Uint(_)) { @@ -4322,7 +4328,7 @@ pub fn generic_params(&self, db: &dyn HirDatabase) -> FxHashSet { } pub fn layout(&self, db: &dyn HirDatabase) -> Result { - db.layout_of_ty(self.ty.clone(), self.env.krate) + db.layout_of_ty(self.ty.clone(), self.env.clone()) .map(|layout| Layout(layout, db.target_data_layout(self.env.krate).unwrap())) } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/change_visibility.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/change_visibility.rs index 2b1d8f6f013..e6179ab8b1b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/change_visibility.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/change_visibility.rs @@ -2,9 +2,10 @@ ast::{self, HasName, HasVisibility}, AstNode, SyntaxKind::{ - CONST, ENUM, FN, MACRO_DEF, MODULE, STATIC, STRUCT, TRAIT, TYPE_ALIAS, USE, VISIBILITY, + self, ASSOC_ITEM_LIST, CONST, ENUM, FN, MACRO_DEF, MODULE, SOURCE_FILE, STATIC, STRUCT, + TRAIT, TYPE_ALIAS, USE, VISIBILITY, }, - T, + SyntaxNode, T, }; use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists}; @@ -46,13 +47,11 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let (offset, target) = if let Some(keyword) = item_keyword { let parent = keyword.parent()?; - let def_kws = - vec![CONST, STATIC, TYPE_ALIAS, FN, MODULE, STRUCT, ENUM, TRAIT, USE, MACRO_DEF]; - // Parent is not a definition, can't add visibility - if !def_kws.iter().any(|&def_kw| def_kw == parent.kind()) { + + if !can_add(&parent) { return None; } - // Already have visibility, do nothing + // Already has visibility, do nothing if parent.children().any(|child| child.kind() == VISIBILITY) { return None; } @@ -86,6 +85,29 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { ) } +fn can_add(node: &SyntaxNode) -> bool { + const LEGAL: &[SyntaxKind] = + &[CONST, STATIC, TYPE_ALIAS, FN, MODULE, STRUCT, ENUM, TRAIT, USE, MACRO_DEF]; + + LEGAL.contains(&node.kind()) && { + let Some(p) = node.parent() else { + return false; + }; + + if p.kind() == ASSOC_ITEM_LIST { + p.parent() + .and_then(|it| ast::Impl::cast(it)) + // inherent impls i.e 'non-trait impls' have a non-local + // effect, thus can have visibility even when nested. + // so filter them out + .filter(|imp| imp.for_token().is_none()) + .is_some() + } else { + matches!(p.kind(), SOURCE_FILE | MODULE) + } + } +} + fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> { if vis.syntax().text() == "pub" { let target = vis.syntax().text_range(); @@ -129,6 +151,16 @@ fn change_visibility_adds_pub_crate_to_items() { check_assist(change_visibility, "unsafe f$0n foo() {}", "pub(crate) unsafe fn foo() {}"); check_assist(change_visibility, "$0macro foo() {}", "pub(crate) macro foo() {}"); check_assist(change_visibility, "$0use foo;", "pub(crate) use foo;"); + check_assist( + change_visibility, + "impl Foo { f$0n foo() {} }", + "impl Foo { pub(crate) fn foo() {} }", + ); + check_assist( + change_visibility, + "fn bar() { impl Foo { f$0n foo() {} } }", + "fn bar() { impl Foo { pub(crate) fn foo() {} } }", + ); } #[test] @@ -213,4 +245,33 @@ fn change_visibility_target() { check_assist_target(change_visibility, "pub(crate)$0 fn foo() {}", "pub(crate)"); check_assist_target(change_visibility, "struct S { $0field: u32 }", "field"); } + + #[test] + fn not_applicable_for_items_within_traits() { + check_assist_not_applicable(change_visibility, "trait Foo { f$0n run() {} }"); + check_assist_not_applicable(change_visibility, "trait Foo { con$0st FOO: u8 = 69; }"); + check_assist_not_applicable(change_visibility, "impl Foo for Bar { f$0n quox() {} }"); + } + + #[test] + fn not_applicable_for_items_within_fns() { + check_assist_not_applicable(change_visibility, "fn foo() { f$0n inner() {} }"); + check_assist_not_applicable(change_visibility, "fn foo() { unsafe f$0n inner() {} }"); + check_assist_not_applicable(change_visibility, "fn foo() { const f$0n inner() {} }"); + check_assist_not_applicable(change_visibility, "fn foo() { con$0st FOO: u8 = 69; }"); + check_assist_not_applicable(change_visibility, "fn foo() { en$0um Foo {} }"); + check_assist_not_applicable(change_visibility, "fn foo() { stru$0ct Foo {} }"); + check_assist_not_applicable(change_visibility, "fn foo() { mo$0d foo {} }"); + check_assist_not_applicable(change_visibility, "fn foo() { $0use foo; }"); + check_assist_not_applicable(change_visibility, "fn foo() { $0type Foo = Bar; }"); + check_assist_not_applicable(change_visibility, "fn foo() { tr$0ait Foo {} }"); + check_assist_not_applicable( + change_visibility, + "fn foo() { impl Trait for Bar { f$0n bar() {} } }", + ); + check_assist_not_applicable( + change_visibility, + "fn foo() { impl Trait for Bar { con$0st FOO: u8 = 69; } }", + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs index 92fd4f71ca5..235062bf531 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs @@ -545,4 +545,100 @@ pub struct some_type { "#, ); } + + #[test] + fn fn_inner_items() { + check_diagnostics( + r#" +fn main() { + const foo: bool = true; + //^^^ 💡 warn: Constant `foo` should have UPPER_SNAKE_CASE name, e.g. `FOO` + static bar: bool = true; + //^^^ 💡 warn: Static variable `bar` should have UPPER_SNAKE_CASE name, e.g. `BAR` + fn BAZ() { + //^^^ 💡 warn: Function `BAZ` should have snake_case name, e.g. `baz` + const foo: bool = true; + //^^^ 💡 warn: Constant `foo` should have UPPER_SNAKE_CASE name, e.g. `FOO` + static bar: bool = true; + //^^^ 💡 warn: Static variable `bar` should have UPPER_SNAKE_CASE name, e.g. `BAR` + fn BAZ() { + //^^^ 💡 warn: Function `BAZ` should have snake_case name, e.g. `baz` + let INNER_INNER = 42; + //^^^^^^^^^^^ 💡 warn: Variable `INNER_INNER` should have snake_case name, e.g. `inner_inner` + } + + let INNER_LOCAL = 42; + //^^^^^^^^^^^ 💡 warn: Variable `INNER_LOCAL` should have snake_case name, e.g. `inner_local` + } +} +"#, + ); + } + + #[test] + fn const_body_inner_items() { + check_diagnostics( + r#" +const _: () = { + static bar: bool = true; + //^^^ 💡 warn: Static variable `bar` should have UPPER_SNAKE_CASE name, e.g. `BAR` + fn BAZ() {} + //^^^ 💡 warn: Function `BAZ` should have snake_case name, e.g. `baz` + + const foo: () = { + //^^^ 💡 warn: Constant `foo` should have UPPER_SNAKE_CASE name, e.g. `FOO` + const foo: bool = true; + //^^^ 💡 warn: Constant `foo` should have UPPER_SNAKE_CASE name, e.g. `FOO` + static bar: bool = true; + //^^^ 💡 warn: Static variable `bar` should have UPPER_SNAKE_CASE name, e.g. `BAR` + fn BAZ() {} + //^^^ 💡 warn: Function `BAZ` should have snake_case name, e.g. `baz` + }; +}; +"#, + ); + } + + #[test] + fn static_body_inner_items() { + check_diagnostics( + r#" +static FOO: () = { + const foo: bool = true; + //^^^ 💡 warn: Constant `foo` should have UPPER_SNAKE_CASE name, e.g. `FOO` + fn BAZ() {} + //^^^ 💡 warn: Function `BAZ` should have snake_case name, e.g. `baz` + + static bar: () = { + //^^^ 💡 warn: Static variable `bar` should have UPPER_SNAKE_CASE name, e.g. `BAR` + const foo: bool = true; + //^^^ 💡 warn: Constant `foo` should have UPPER_SNAKE_CASE name, e.g. `FOO` + static bar: bool = true; + //^^^ 💡 warn: Static variable `bar` should have UPPER_SNAKE_CASE name, e.g. `BAR` + fn BAZ() {} + //^^^ 💡 warn: Function `BAZ` should have snake_case name, e.g. `baz` + }; +}; +"#, + ); + } + + #[test] + fn enum_variant_body_inner_item() { + check_diagnostics( + r#" +enum E { + A = { + const foo: bool = true; + //^^^ 💡 warn: Constant `foo` should have UPPER_SNAKE_CASE name, e.g. `FOO` + static bar: bool = true; + //^^^ 💡 warn: Static variable `bar` should have UPPER_SNAKE_CASE name, e.g. `BAR` + fn BAZ() {} + //^^^ 💡 warn: Function `BAZ` should have snake_case name, e.g. `baz` + 42 + }, +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs index 577bd2bc1f8..ae97236409e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -24,7 +24,7 @@ use crate::{ syntax_highlighting::{ - escape::{highlight_escape_char, highlight_escape_string}, + escape::{highlight_escape_byte, highlight_escape_char, highlight_escape_string}, format::highlight_format_string, highlights::Highlights, macro_::MacroHighlighter, @@ -471,6 +471,14 @@ fn item(&self) -> &ast::Item { }; highlight_escape_char(hl, &char, range.start()) + } else if ast::Byte::can_cast(token.kind()) + && ast::Byte::can_cast(descended_token.kind()) + { + let Some(byte) = ast::Byte::cast(token) else { + continue; + }; + + highlight_escape_byte(hl, &byte, range.start()) } } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs index 211e3588095..5913ca5e454 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs @@ -1,7 +1,7 @@ //! Syntax highlighting for escape sequences use crate::syntax_highlighting::highlights::Highlights; use crate::{HlRange, HlTag}; -use syntax::ast::{Char, IsString}; +use syntax::ast::{Byte, Char, IsString}; use syntax::{AstToken, TextRange, TextSize}; pub(super) fn highlight_escape_string( @@ -10,14 +10,14 @@ pub(super) fn highlight_escape_string( start: TextSize, ) { string.escaped_char_ranges(&mut |piece_range, char| { - if char.is_err() { - return; - } - if string.text()[piece_range.start().into()..].starts_with('\\') { + let highlight = match char { + Ok(_) => HlTag::EscapeSequence, + Err(_) => HlTag::InvalidEscapeSequence, + }; stack.add(HlRange { range: piece_range + start, - highlight: HlTag::EscapeSequence.into(), + highlight: highlight.into(), binding_hash: None, }); } @@ -26,6 +26,9 @@ pub(super) fn highlight_escape_string( pub(super) fn highlight_escape_char(stack: &mut Highlights, char: &Char, start: TextSize) { if char.value().is_none() { + // We do not emit invalid escapes highlighting here. The lexer would likely be in a bad + // state and this token contains junks, since `'` is not a reliable delimiter (consider + // lifetimes). Nonetheless, parser errors should already be emitted. return; } @@ -43,3 +46,24 @@ pub(super) fn highlight_escape_char(stack: &mut Highlights, char: &Char, start: TextRange::new(start + TextSize::from(1), start + TextSize::from(text.len() as u32 + 1)); stack.add(HlRange { range, highlight: HlTag::EscapeSequence.into(), binding_hash: None }) } + +pub(super) fn highlight_escape_byte(stack: &mut Highlights, byte: &Byte, start: TextSize) { + if byte.value().is_none() { + // See `highlight_escape_char` for why no error highlighting here. + return; + } + + let text = byte.text(); + if !text.starts_with("b'") || !text.ends_with('\'') { + return; + } + + let text = &text[2..text.len() - 1]; + if !text.starts_with('\\') { + return; + } + + let range = + TextRange::new(start + TextSize::from(2), start + TextSize::from(text.len() as u32 + 2)); + stack.add(HlRange { range, highlight: HlTag::EscapeSequence.into(), binding_hash: None }) +} diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs index 2c7823069b3..bbc6b55a642 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs @@ -109,6 +109,7 @@ fn html_escape(text: &str) -> String { .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } -.unresolved_reference { color: #FC5555; text-decoration: wavy underline; } +.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } +.unresolved_reference { color: #FC5555; text-decoration: wavy underline; } "; diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs index f983109115f..6d4cdd0efe2 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs @@ -29,6 +29,7 @@ pub enum HlTag { Comment, EscapeSequence, FormatSpecifier, + InvalidEscapeSequence, Keyword, NumericLiteral, Operator(HlOperator), @@ -166,6 +167,7 @@ fn as_str(self) -> &'static str { HlTag::CharLiteral => "char_literal", HlTag::Comment => "comment", HlTag::EscapeSequence => "escape_sequence", + HlTag::InvalidEscapeSequence => "invalid_escape_sequence", HlTag::FormatSpecifier => "format_specifier", HlTag::Keyword => "keyword", HlTag::Punctuation(punct) => match punct { diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html index 9ed65fbc854..4dcbfe4eb62 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html @@ -40,7 +40,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } -.unresolved_reference { color: #FC5555; text-decoration: wavy underline; } +.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } +.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
fn not_static() {}
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html
index 567ab8ccc11..bf5505caf37 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html
@@ -40,7 +40,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 
-.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
+.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
+.unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
 
#[allow(dead_code)]
 #[rustfmt::skip]
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html
index 1e4c06df7ea..0d1b3c1f183 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html
@@ -40,7 +40,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 
-.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
+.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
+.unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
 
extern crate foo;
 use core::iter;
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html
index 5d66f832daf..dd1528ed03f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html
@@ -40,7 +40,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 
-.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
+.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
+.unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
 
use core::iter;
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index 35f240d4284..d5f92aa5d47 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -40,7 +40,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 
-.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
+.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
+.unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
 
//! This is a module to test doc injection.
 //! ```
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
index 87b9da46e2c..b15f7bca72b 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
@@ -40,7 +40,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 
-.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
+.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
+.unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
 
extern crate std;
 extern crate alloc as abc;
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
index 6b049f379ac..bdeb09d2f83 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
@@ -40,7 +40,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 
-.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
+.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
+.unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
 
use inner::{self as inner_mod};
 mod inner {}
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
index d9c3db6fbb5..f9c33b8a601 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
@@ -40,7 +40,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 
-.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
+.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
+.unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
 
fn fixture(ra_fixture: &str) {}
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html
index 3900959bedf..fd3b39855e2 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html
@@ -40,7 +40,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 
-.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
+.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
+.unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
 
extern crate self;
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html
index f98e0b1cda6..ec39998de26 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html
@@ -40,7 +40,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 
-.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
+.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
+.unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
 

 #[derive()]
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
index 2cbbf696415..c5fcec75680 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
@@ -40,7 +40,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 
-.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
+.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
+.unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
 
proc_macros::mirror! {
     {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html
index 8a1d69816e6..4dcf8e5f01f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html
@@ -40,7 +40,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 
-.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
+.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
+.unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
 
//! [Struct]
 //! This is an intra doc injection test for modules
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html
index c4c3e3dc260..084bbf2f742 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html
@@ -40,7 +40,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 
-.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
+.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
+.unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
 
/// [crate::foo::Struct]
 /// This is an intra doc injection test for modules
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html
index 2369071ae2a..1af4bcfbd9d 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html
@@ -40,7 +40,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 
-.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
+.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
+.unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
 
fn main() {
     1 + 1 - 1 * 1 / 1 % 1 | 1 & 1 ! 1 ^ 1 >> 1 << 1;
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html
index bff35c897e1..ec18c3ea1f9 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html
@@ -40,7 +40,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 
-.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
+.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
+.unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
 
fn main() {
     let hello = "hello";
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index f4f164aa1de..dcac8eb7368 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -40,7 +40,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 
-.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
+.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
+.unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
 
macro_rules! println {
     ($($arg:tt)*) => ({
@@ -105,6 +106,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     let a = '\x65';
     let a = '\x00';
 
+    let a = b'\xFF';
+
     println!("Hello {{Hello}}");
     // from https://doc.rust-lang.org/std/fmt/index.html
     println!("Hello");                 // => "Hello"
@@ -159,8 +162,9 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     println!("Hello\nWorld");
     println!("\u{48}\x65\x6C\x6C\x6F World");
 
-    let _ = "\x28\x28\x00\x63\n";
-    let _ = b"\x28\x28\x00\x63\n";
+    let _ = "\x28\x28\x00\x63\xFF\u{FF}\n"; // invalid non-UTF8 escape sequences
+    let _ = b"\x28\x28\x00\x63\xFF\u{FF}\n"; // valid bytes, invalid unicodes
+    let _ = c"\u{FF}\xFF"; // valid bytes, valid unicodes
     let backslash = r"\\";
 
     println!("{\x41}", A = 92);
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
index 654d51b8a43..c72ea54e948 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -40,7 +40,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 
-.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
+.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
+.unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
 
macro_rules! id {
     ($($tt:tt)*) => {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
index 1ee451a06d0..696aa590025 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
@@ -451,6 +451,8 @@ fn main() {
     let a = '\x65';
     let a = '\x00';
 
+    let a = b'\xFF';
+
     println!("Hello {{Hello}}");
     // from https://doc.rust-lang.org/std/fmt/index.html
     println!("Hello");                 // => "Hello"
@@ -505,8 +507,9 @@ fn main() {
     println!("Hello\nWorld");
     println!("\u{48}\x65\x6C\x6C\x6F World");
 
-    let _ = "\x28\x28\x00\x63\n";
-    let _ = b"\x28\x28\x00\x63\n";
+    let _ = "\x28\x28\x00\x63\xFF\u{FF}\n"; // invalid non-UTF8 escape sequences
+    let _ = b"\x28\x28\x00\x63\xFF\u{FF}\n"; // valid bytes, invalid unicodes
+    let _ = c"\u{FF}\xFF"; // valid bytes, valid unicodes
     let backslash = r"\\";
 
     println!("{\x41}", A = 92);
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar.rs b/src/tools/rust-analyzer/crates/parser/src/grammar.rs
index 1814e0e54c5..a868419821d 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar.rs
@@ -211,70 +211,54 @@ fn is_blocklike(kind: SyntaxKind) -> bool {
 const VISIBILITY_FIRST: TokenSet = TokenSet::new(&[T![pub], T![crate]]);
 
 fn opt_visibility(p: &mut Parser<'_>, in_tuple_field: bool) -> bool {
-    match p.current() {
-        T![pub] => {
-            let m = p.start();
-            p.bump(T![pub]);
-            if p.at(T!['(']) {
-                match p.nth(1) {
-                    // test crate_visibility
-                    // pub(crate) struct S;
-                    // pub(self) struct S;
-                    // pub(super) struct S;
+    if !p.at(T![pub]) {
+        return false;
+    }
 
-                    // test_err crate_visibility_empty_recover
-                    // pub() struct S;
+    let m = p.start();
+    p.bump(T![pub]);
+    if p.at(T!['(']) {
+        match p.nth(1) {
+            // test crate_visibility
+            // pub(crate) struct S;
+            // pub(self) struct S;
+            // pub(super) struct S;
 
-                    // test pub_parens_typepath
-                    // struct B(pub (super::A));
-                    // struct B(pub (crate::A,));
-                    T![crate] | T![self] | T![super] | T![ident] | T![')'] if p.nth(2) != T![:] => {
-                        // If we are in a tuple struct, then the parens following `pub`
-                        // might be an tuple field, not part of the visibility. So in that
-                        // case we don't want to consume an identifier.
+            // test_err crate_visibility_empty_recover
+            // pub() struct S;
 
-                        // test pub_tuple_field
-                        // struct MyStruct(pub (u32, u32));
-                        // struct MyStruct(pub (u32));
-                        // struct MyStruct(pub ());
-                        if !(in_tuple_field && matches!(p.nth(1), T![ident] | T![')'])) {
-                            p.bump(T!['(']);
-                            paths::use_path(p);
-                            p.expect(T![')']);
-                        }
-                    }
-                    // test crate_visibility_in
-                    // pub(in super::A) struct S;
-                    // pub(in crate) struct S;
-                    T![in] => {
-                        p.bump(T!['(']);
-                        p.bump(T![in]);
-                        paths::use_path(p);
-                        p.expect(T![')']);
-                    }
-                    _ => {}
+            // test pub_parens_typepath
+            // struct B(pub (super::A));
+            // struct B(pub (crate::A,));
+            T![crate] | T![self] | T![super] | T![ident] | T![')'] if p.nth(2) != T![:] => {
+                // If we are in a tuple struct, then the parens following `pub`
+                // might be an tuple field, not part of the visibility. So in that
+                // case we don't want to consume an identifier.
+
+                // test pub_tuple_field
+                // struct MyStruct(pub (u32, u32));
+                // struct MyStruct(pub (u32));
+                // struct MyStruct(pub ());
+                if !(in_tuple_field && matches!(p.nth(1), T![ident] | T![')'])) {
+                    p.bump(T!['(']);
+                    paths::use_path(p);
+                    p.expect(T![')']);
                 }
             }
-            m.complete(p, VISIBILITY);
-            true
-        }
-        // test crate_keyword_vis
-        // crate fn main() { }
-        // struct S { crate field: u32 }
-        // struct T(crate u32);
-        T![crate] => {
-            if p.nth_at(1, T![::]) {
-                // test crate_keyword_path
-                // fn foo() { crate::foo(); }
-                return false;
+            // test crate_visibility_in
+            // pub(in super::A) struct S;
+            // pub(in crate) struct S;
+            T![in] => {
+                p.bump(T!['(']);
+                p.bump(T![in]);
+                paths::use_path(p);
+                p.expect(T![')']);
             }
-            let m = p.start();
-            p.bump(T![crate]);
-            m.complete(p, VISIBILITY);
-            true
+            _ => {}
         }
-        _ => false,
     }
+    m.complete(p, VISIBILITY);
+    true
 }
 
 fn opt_rename(p: &mut Parser<'_>) {
diff --git a/src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs b/src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs
index 11f9c34abdf..2f3c7febc04 100644
--- a/src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs
@@ -6,7 +6,6 @@ fn vis() {
     check(PrefixEntryPoint::Vis, "fn foo() {}", "");
     check(PrefixEntryPoint::Vis, "pub(fn foo() {}", "pub");
     check(PrefixEntryPoint::Vis, "pub(crate fn foo() {}", "pub(crate");
-    check(PrefixEntryPoint::Vis, "crate fn foo() {}", "crate");
 }
 
 #[test]
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0040_crate_keyword_vis.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0040_crate_keyword_vis.rast
deleted file mode 100644
index 07b0210e44d..00000000000
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0040_crate_keyword_vis.rast
+++ /dev/null
@@ -1,63 +0,0 @@
-SOURCE_FILE
-  FN
-    VISIBILITY
-      CRATE_KW "crate"
-    WHITESPACE " "
-    FN_KW "fn"
-    WHITESPACE " "
-    NAME
-      IDENT "main"
-    PARAM_LIST
-      L_PAREN "("
-      R_PAREN ")"
-    WHITESPACE " "
-    BLOCK_EXPR
-      STMT_LIST
-        L_CURLY "{"
-        WHITESPACE " "
-        R_CURLY "}"
-  WHITESPACE "\n"
-  STRUCT
-    STRUCT_KW "struct"
-    WHITESPACE " "
-    NAME
-      IDENT "S"
-    WHITESPACE " "
-    RECORD_FIELD_LIST
-      L_CURLY "{"
-      WHITESPACE " "
-      RECORD_FIELD
-        VISIBILITY
-          CRATE_KW "crate"
-        WHITESPACE " "
-        NAME
-          IDENT "field"
-        COLON ":"
-        WHITESPACE " "
-        PATH_TYPE
-          PATH
-            PATH_SEGMENT
-              NAME_REF
-                IDENT "u32"
-      WHITESPACE " "
-      R_CURLY "}"
-  WHITESPACE "\n"
-  STRUCT
-    STRUCT_KW "struct"
-    WHITESPACE " "
-    NAME
-      IDENT "T"
-    TUPLE_FIELD_LIST
-      L_PAREN "("
-      TUPLE_FIELD
-        VISIBILITY
-          CRATE_KW "crate"
-        WHITESPACE " "
-        PATH_TYPE
-          PATH
-            PATH_SEGMENT
-              NAME_REF
-                IDENT "u32"
-      R_PAREN ")"
-    SEMICOLON ";"
-  WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0040_crate_keyword_vis.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0040_crate_keyword_vis.rs
deleted file mode 100644
index e2b5f2161df..00000000000
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0040_crate_keyword_vis.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-crate fn main() { }
-struct S { crate field: u32 }
-struct T(crate u32);
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0125_crate_keyword_path.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0125_crate_keyword_path.rast
deleted file mode 100644
index 8d9b61630ae..00000000000
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0125_crate_keyword_path.rast
+++ /dev/null
@@ -1,33 +0,0 @@
-SOURCE_FILE
-  FN
-    FN_KW "fn"
-    WHITESPACE " "
-    NAME
-      IDENT "foo"
-    PARAM_LIST
-      L_PAREN "("
-      R_PAREN ")"
-    WHITESPACE " "
-    BLOCK_EXPR
-      STMT_LIST
-        L_CURLY "{"
-        WHITESPACE " "
-        EXPR_STMT
-          CALL_EXPR
-            PATH_EXPR
-              PATH
-                PATH
-                  PATH_SEGMENT
-                    NAME_REF
-                      CRATE_KW "crate"
-                COLON2 "::"
-                PATH_SEGMENT
-                  NAME_REF
-                    IDENT "foo"
-            ARG_LIST
-              L_PAREN "("
-              R_PAREN ")"
-          SEMICOLON ";"
-        WHITESPACE " "
-        R_CURLY "}"
-  WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0125_crate_keyword_path.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0125_crate_keyword_path.rs
deleted file mode 100644
index 0f454d121d6..00000000000
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0125_crate_keyword_path.rs
+++ /dev/null
@@ -1 +0,0 @@
-fn foo() { crate::foo(); }
diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
index e1117ac464b..e47808a2cc9 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
@@ -145,7 +145,7 @@ pub struct PackageDependency {
     pub kind: DepKind,
 }
 
-#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub enum DepKind {
     /// Available to the library, binary, and dev targets in the package (but not the build script).
     Normal,
@@ -156,23 +156,20 @@ pub enum DepKind {
 }
 
 impl DepKind {
-    fn iter(list: &[cargo_metadata::DepKindInfo]) -> impl Iterator + '_ {
-        let mut dep_kinds = Vec::new();
+    fn iter(list: &[cargo_metadata::DepKindInfo]) -> impl Iterator {
+        let mut dep_kinds = [None; 3];
         if list.is_empty() {
-            dep_kinds.push(Self::Normal);
+            dep_kinds[0] = Some(Self::Normal);
         }
         for info in list {
-            let kind = match info.kind {
-                cargo_metadata::DependencyKind::Normal => Self::Normal,
-                cargo_metadata::DependencyKind::Development => Self::Dev,
-                cargo_metadata::DependencyKind::Build => Self::Build,
+            match info.kind {
+                cargo_metadata::DependencyKind::Normal => dep_kinds[0] = Some(Self::Normal),
+                cargo_metadata::DependencyKind::Development => dep_kinds[1] = Some(Self::Dev),
+                cargo_metadata::DependencyKind::Build => dep_kinds[2] = Some(Self::Build),
                 cargo_metadata::DependencyKind::Unknown => continue,
-            };
-            dep_kinds.push(kind);
+            }
         }
-        dep_kinds.sort_unstable();
-        dep_kinds.dedup();
-        dep_kinds.into_iter()
+        dep_kinds.into_iter().flatten()
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
index 8d68bf160a2..33d7b5ed878 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -8,7 +8,7 @@
 
 use hir::{
     db::{DefDatabase, ExpandDatabase, HirDatabase},
-    Adt, AssocItem, Crate, DefWithBody, HasCrate, HasSource, HirDisplay, ModuleDef, Name,
+    Adt, AssocItem, Crate, DefWithBody, HasSource, HirDisplay, ModuleDef, Name,
 };
 use hir_def::{
     body::{BodySourceMap, SyntheticSyntax},
@@ -277,7 +277,7 @@ fn run_data_layout(&self, db: &RootDatabase, adts: &[hir::Adt], verbosity: Verbo
             let Err(e) = db.layout_of_adt(
                 hir_def::AdtId::from(a).into(),
                 Substitution::empty(Interner),
-                a.krate(db).into(),
+                db.trait_environment(a.into()),
             ) else {
                 continue;
             };
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/semantic_tokens.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/semantic_tokens.rs
index d4bb20c8f44..1fe02fc7ead 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/semantic_tokens.rs
@@ -78,6 +78,7 @@ pub(crate) fn standard_fallback_type(token: SemanticTokenType) -> Option DECORATOR,
         (DOT, "dot"),
         (ESCAPE_SEQUENCE, "escapeSequence") => STRING,
+        (INVALID_ESCAPE_SEQUENCE, "invalidEscapeSequence") => STRING,
         (FORMAT_SPECIFIER, "formatSpecifier") => STRING,
         (GENERIC, "generic") => TYPE_PARAMETER,
         (LABEL, "label"),
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
index ba3421bf9e7..7a89533a5e9 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
@@ -640,6 +640,7 @@ fn semantic_token_type_and_modifiers(
         HlTag::CharLiteral => semantic_tokens::CHAR,
         HlTag::Comment => semantic_tokens::COMMENT,
         HlTag::EscapeSequence => semantic_tokens::ESCAPE_SEQUENCE,
+        HlTag::InvalidEscapeSequence => semantic_tokens::INVALID_ESCAPE_SEQUENCE,
         HlTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER,
         HlTag::Keyword => semantic_tokens::KEYWORD,
         HlTag::None => semantic_tokens::GENERIC,
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 090eb89f470..87fd51d703c 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
@@ -2,7 +2,9 @@
 
 use std::borrow::Cow;
 
-use rustc_lexer::unescape::{unescape_byte, unescape_char, unescape_literal, Mode};
+use rustc_lexer::unescape::{
+    unescape_byte, unescape_c_string, unescape_char, unescape_literal, CStrUnit, Mode,
+};
 
 use crate::{
     ast::{self, AstToken},
@@ -146,6 +148,7 @@ fn new(literal: &str) -> Option {
 
 pub trait IsString: AstToken {
     const RAW_PREFIX: &'static str;
+    const MODE: Mode;
     fn is_raw(&self) -> bool {
         self.text().starts_with(Self::RAW_PREFIX)
     }
@@ -181,7 +184,7 @@ fn escaped_char_ranges(
         let text = &self.text()[text_range_no_quotes - start];
         let offset = text_range_no_quotes.start() - start;
 
-        unescape_literal(text, Mode::Str, &mut |range, unescaped_char| {
+        unescape_literal(text, Self::MODE, &mut |range, unescaped_char| {
             let text_range =
                 TextRange::new(range.start.try_into().unwrap(), range.end.try_into().unwrap());
             cb(text_range + offset, unescaped_char);
@@ -196,6 +199,7 @@ fn map_range_up(&self, range: TextRange) -> Option {
 
 impl IsString for ast::String {
     const RAW_PREFIX: &'static str = "r";
+    const MODE: Mode = Mode::Str;
 }
 
 impl ast::String {
@@ -213,7 +217,7 @@ pub fn value(&self) -> Option> {
         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 (
+        unescape_literal(text, Self::MODE, &mut |char_range, unescaped_char| match (
             unescaped_char,
             buf.capacity() == 0,
         ) {
@@ -239,6 +243,7 @@ pub fn value(&self) -> Option> {
 
 impl IsString for ast::ByteString {
     const RAW_PREFIX: &'static str = "br";
+    const MODE: Mode = Mode::ByteStr;
 }
 
 impl ast::ByteString {
@@ -256,7 +261,7 @@ pub fn value(&self) -> Option> {
         let mut buf: Vec = Vec::new();
         let mut prev_end = 0;
         let mut has_error = false;
-        unescape_literal(text, Mode::ByteStr, &mut |char_range, unescaped_char| match (
+        unescape_literal(text, Self::MODE, &mut |char_range, unescaped_char| match (
             unescaped_char,
             buf.capacity() == 0,
         ) {
@@ -282,42 +287,70 @@ pub fn value(&self) -> Option> {
 
 impl IsString for ast::CString {
     const RAW_PREFIX: &'static str = "cr";
+    const MODE: Mode = Mode::CStr;
+
+    fn escaped_char_ranges(
+        &self,
+        cb: &mut dyn FnMut(TextRange, Result),
+    ) {
+        let text_range_no_quotes = match self.text_range_between_quotes() {
+            Some(it) => it,
+            None => return,
+        };
+
+        let start = self.syntax().text_range().start();
+        let text = &self.text()[text_range_no_quotes - start];
+        let offset = text_range_no_quotes.start() - start;
+
+        unescape_c_string(text, Self::MODE, &mut |range, unescaped_char| {
+            let text_range =
+                TextRange::new(range.start.try_into().unwrap(), range.end.try_into().unwrap());
+            // XXX: This method should only be used for highlighting ranges. The unescaped
+            // char/byte is not used. For simplicity, we return an arbitrary placeholder char.
+            cb(text_range + offset, unescaped_char.map(|_| ' '));
+        });
+    }
 }
 
 impl ast::CString {
-    pub fn value(&self) -> Option> {
+    pub fn value(&self) -> Option> {
         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));
+            return Some(Cow::Borrowed(text.as_bytes()));
         }
 
         let text = self.text();
         let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
 
-        let mut buf = String::new();
+        let mut buf = Vec::new();
         let mut prev_end = 0;
         let mut has_error = false;
-        unescape_literal(text, Mode::Str, &mut |char_range, unescaped_char| match (
-            unescaped_char,
+        let mut char_buf = [0u8; 4];
+        let mut extend_unit = |buf: &mut Vec, unit: CStrUnit| match unit {
+            CStrUnit::Byte(b) => buf.push(b),
+            CStrUnit::Char(c) => buf.extend(c.encode_utf8(&mut char_buf).as_bytes()),
+        };
+        unescape_c_string(text, Self::MODE, &mut |char_range, unescaped| match (
+            unescaped,
             buf.capacity() == 0,
         ) {
-            (Ok(c), false) => buf.push(c),
+            (Ok(u), false) => extend_unit(&mut buf, u),
             (Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => {
                 prev_end = char_range.end
             }
-            (Ok(c), true) => {
+            (Ok(u), true) => {
                 buf.reserve_exact(text.len());
-                buf.push_str(&text[..prev_end]);
-                buf.push(c);
+                buf.extend(text[..prev_end].as_bytes());
+                extend_unit(&mut buf, u);
             }
             (Err(_), _) => has_error = true,
         });
 
         match (has_error, buf.capacity() == 0) {
             (true, _) => None,
-            (false, true) => Some(Cow::Borrowed(text)),
+            (false, true) => Some(Cow::Borrowed(text.as_bytes())),
             (false, false) => Some(Cow::Owned(buf)),
         }
     }
diff --git a/src/tools/rust-analyzer/docs/user/manual.adoc b/src/tools/rust-analyzer/docs/user/manual.adoc
index 31035c4b729..2cf985adabc 100644
--- a/src/tools/rust-analyzer/docs/user/manual.adoc
+++ b/src/tools/rust-analyzer/docs/user/manual.adoc
@@ -449,27 +449,24 @@ You'll need to close and reopen all .rs and Cargo files, or to restart the IDE,
 Support for the language server protocol is built into Kate through the LSP plugin, which is included by default.
 It is preconfigured to use rust-analyzer for Rust sources since Kate 21.12.
 
-Earlier versions allow you to use rust-analyzer through a simple settings change.
-In the LSP Client settings of Kate, copy the content of the third tab "default parameters" to the second tab "server configuration".
-Then in the configuration replace:
+To change rust-analyzer config options, start from the following example and put it into Kate's "User Server Settings" tab (located under the LSP Client settings):
 [source,json]
 ----
+{
+    "servers": {
         "rust": {
-            "command": ["rls"],
-            "rootIndicationFileNames": ["Cargo.lock", "Cargo.toml"],
-            "url": "https://github.com/rust-lang/rls",
-            "highlightingModeRegex": "^Rust$"
-        },
-----
-With
-[source,json]
-----
-        "rust": {
-            "command": ["rust-analyzer"],
-            "rootIndicationFileNames": ["Cargo.lock", "Cargo.toml"],
-            "url": "https://github.com/rust-lang/rust-analyzer",
-            "highlightingModeRegex": "^Rust$"
-        },
+            "initializationOptions": {
+                "cachePriming": {
+                    "enable": false
+                },
+                "check": {
+                    "allTargets": false
+                },
+                "checkOnSave": false
+            }
+        }
+    }
+}
 ----
 Then click on apply, and restart the LSP server for your rust project.
 
diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json
index ffb5dd9079a..a4897899cab 100644
--- a/src/tools/rust-analyzer/editors/code/package.json
+++ b/src/tools/rust-analyzer/editors/code/package.json
@@ -35,9 +35,12 @@
         "build-base": "esbuild ./src/main.ts --bundle --outfile=out/main.js --external:vscode --format=cjs --platform=node --target=node16",
         "build": "npm run build-base -- --sourcemap",
         "watch": "npm run build-base -- --sourcemap --watch",
-        "lint": "prettier --check . && eslint -c .eslintrc.js --ext ts ./src ./tests",
-        "fix": "prettier --write . && eslint -c .eslintrc.js --ext ts ./src ./tests --fix",
-        "pretest": "tsc && npm run build",
+        "format": "prettier --write .",
+        "format:check": "prettier --check .",
+        "lint": "eslint -c .eslintrc.js --ext ts ./src ./tests",
+        "lint:fix": "npm run lint -- --fix",
+        "typecheck": "tsc",
+        "pretest": "npm run typecheck && npm run build",
         "test": "node ./out/tests/runTests.js"
     },
     "dependencies": {
@@ -1801,12 +1804,16 @@
             },
             {
                 "id": "escapeSequence",
-                "description": "Style for char escapes in strings"
+                "description": "Style for char or byte escapes in strings"
             },
             {
                 "id": "formatSpecifier",
                 "description": "Style for {} placeholders in format strings"
             },
+            {
+                "id": "invalidEscapeSequence",
+                "description": "Style for invalid char or byte escapes in strings"
+            },
             {
                 "id": "label",
                 "description": "Style for labels"
diff --git a/src/tools/rust-analyzer/editors/code/src/debug.ts b/src/tools/rust-analyzer/editors/code/src/debug.ts
index f3d6238d51b..e817d680eae 100644
--- a/src/tools/rust-analyzer/editors/code/src/debug.ts
+++ b/src/tools/rust-analyzer/editors/code/src/debug.ts
@@ -66,6 +66,12 @@ export async function startDebugSession(ctx: Ctx, runnable: ra.Runnable): Promis
     return vscode.debug.startDebugging(undefined, debugConfig);
 }
 
+function createCommandLink(extensionId: string): string {
+    // do not remove the second quotes inside
+    // encodeURIComponent or it won't work
+    return `extension.open?${encodeURIComponent(`"${extensionId}"`)}`;
+}
+
 async function getDebugConfiguration(
     ctx: Ctx,
     runnable: ra.Runnable,
@@ -90,9 +96,12 @@ async function getDebugConfiguration(
     }
 
     if (!debugEngine) {
+        const commandCodeLLDB: string = createCommandLink("vadimcn.vscode-lldb");
+        const commandCpp: string = createCommandLink("ms-vscode.cpptools");
+
         await vscode.window.showErrorMessage(
-            `Install [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb)` +
-                ` or [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) extension for debugging.`,
+            `Install [CodeLLDB](command:${commandCodeLLDB} "Open CodeLLDB")` +
+                ` or [C/C++](command:${commandCpp} "Open C/C++") extension for debugging.`,
         );
         return;
     }