about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs5
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs21
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lib.rs73
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/traits.rs19
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs210
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs19
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs21
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/signature_help.rs156
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs35
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs19
17 files changed, 388 insertions, 226 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
index 1fba2e2e4d3..f4564c94bb5 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
@@ -12,9 +12,6 @@ use std::sync::OnceLock;
 
 use rustc_hash::FxHashMap;
 
-/// Ignored attribute namespaces used by tools.
-pub const TOOL_MODULES: &[&str] = &["rustfmt", "clippy"];
-
 pub struct BuiltinAttribute {
     pub name: &'static str,
     pub template: AttributeTemplate,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
index a528c4cc697..497b69889ac 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
@@ -84,6 +84,14 @@ use crate::{
     LocalModuleId, Lookup, MacroExpander, MacroId, ModuleId, ProcMacroId, UseId,
 };
 
+const PREDEFINED_TOOLS: &[SmolStr] = &[
+    SmolStr::new_static("clippy"),
+    SmolStr::new_static("rustfmt"),
+    SmolStr::new_static("diagnostic"),
+    SmolStr::new_static("miri"),
+    SmolStr::new_static("rust_analyzer"),
+];
+
 /// Contains the results of (early) name resolution.
 ///
 /// A `DefMap` stores the module tree and the definitions that are in scope in every module after
@@ -160,7 +168,7 @@ impl DefMapCrateData {
             fn_proc_macro_mapping: FxHashMap::default(),
             proc_macro_loading_error: None,
             registered_attrs: Vec::new(),
-            registered_tools: Vec::new(),
+            registered_tools: PREDEFINED_TOOLS.into(),
             unstable_features: FxHashSet::default(),
             rustc_coherence_is_core: false,
             no_core: false,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs
index eb7f4c05ae2..3cb0666edf9 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs
@@ -10,7 +10,7 @@ use syntax::{ast, SmolStr};
 use triomphe::Arc;
 
 use crate::{
-    attr::builtin::{find_builtin_attr_idx, TOOL_MODULES},
+    attr::builtin::find_builtin_attr_idx,
     db::DefDatabase,
     item_scope::BuiltinShadowMode,
     nameres::path_resolution::ResolveMode,
@@ -82,8 +82,7 @@ impl DefMap {
             let name = name.to_smol_str();
             let pred = |n: &_| *n == name;
 
-            let registered = self.data.registered_tools.iter().map(SmolStr::as_str);
-            let is_tool = TOOL_MODULES.iter().copied().chain(registered).any(pred);
+            let is_tool = self.data.registered_tools.iter().map(SmolStr::as_str).any(pred);
             // FIXME: tool modules can be shadowed by actual modules
             if is_tool {
                 return true;
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 b68fefc5150..36e3a458898 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
@@ -797,19 +797,22 @@ impl<'a> InferenceTable<'a> {
             })
             .build();
 
-        let projection = {
-            let b = TyBuilder::subst_for_def(self.db, fn_once_trait, None);
-            if b.remaining() != 2 {
-                return None;
-            }
-            let fn_once_subst = b.push(ty.clone()).push(arg_ty).build();
+        let b = TyBuilder::trait_ref(self.db, fn_once_trait);
+        if b.remaining() != 2 {
+            return None;
+        }
+        let mut trait_ref = b.push(ty.clone()).push(arg_ty).build();
 
-            TyBuilder::assoc_type_projection(self.db, output_assoc_type, Some(fn_once_subst))
-                .build()
+        let projection = {
+            TyBuilder::assoc_type_projection(
+                self.db,
+                output_assoc_type,
+                Some(trait_ref.substitution.clone()),
+            )
+            .build()
         };
 
         let trait_env = self.trait_env.env.clone();
-        let mut trait_ref = projection.trait_ref(self.db);
         let obligation = InEnvironment {
             goal: trait_ref.clone().cast(Interner),
             environment: trait_env.clone(),
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
index 1727cec9893..26a839f0e9f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
@@ -570,6 +570,10 @@ impl CallableSig {
         }
     }
 
+    pub fn abi(&self) -> FnAbi {
+        self.abi
+    }
+
     pub fn params(&self) -> &[Ty] {
         &self.params_and_return[0..self.params_and_return.len() - 1]
     }
@@ -892,20 +896,16 @@ where
     Canonical { value, binders: chalk_ir::CanonicalVarKinds::from_iter(Interner, kinds) }
 }
 
-pub fn callable_sig_from_fnonce(
-    mut self_ty: &Ty,
-    env: Arc<TraitEnvironment>,
+pub fn callable_sig_from_fn_trait(
+    self_ty: &Ty,
+    trait_env: Arc<TraitEnvironment>,
     db: &dyn HirDatabase,
-) -> Option<CallableSig> {
-    if let Some((ty, _, _)) = self_ty.as_reference() {
-        // This will happen when it implements fn or fn mut, since we add a autoborrow adjustment
-        self_ty = ty;
-    }
-    let krate = env.krate;
+) -> Option<(FnTrait, CallableSig)> {
+    let krate = trait_env.krate;
     let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?;
     let output_assoc_type = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?;
 
-    let mut table = InferenceTable::new(db, env);
+    let mut table = InferenceTable::new(db, trait_env.clone());
     let b = TyBuilder::trait_ref(db, fn_once_trait);
     if b.remaining() != 2 {
         return None;
@@ -915,23 +915,56 @@ pub fn callable_sig_from_fnonce(
     // - Self: FnOnce<?args_ty>
     // - <Self as FnOnce<?args_ty>>::Output == ?ret_ty
     let args_ty = table.new_type_var();
-    let trait_ref = b.push(self_ty.clone()).push(args_ty.clone()).build();
+    let mut trait_ref = b.push(self_ty.clone()).push(args_ty.clone()).build();
     let projection = TyBuilder::assoc_type_projection(
         db,
         output_assoc_type,
         Some(trait_ref.substitution.clone()),
     )
     .build();
-    table.register_obligation(trait_ref.cast(Interner));
-    let ret_ty = table.normalize_projection_ty(projection);
-
-    let ret_ty = table.resolve_completely(ret_ty);
-    let args_ty = table.resolve_completely(args_ty);
 
-    let params =
-        args_ty.as_tuple()?.iter(Interner).map(|it| it.assert_ty_ref(Interner)).cloned().collect();
-
-    Some(CallableSig::from_params_and_return(params, ret_ty, false, Safety::Safe, FnAbi::RustCall))
+    let block = trait_env.block;
+    let trait_env = trait_env.env.clone();
+    let obligation =
+        InEnvironment { goal: trait_ref.clone().cast(Interner), environment: trait_env.clone() };
+    let canonical = table.canonicalize(obligation.clone());
+    if db.trait_solve(krate, block, canonical.cast(Interner)).is_some() {
+        table.register_obligation(obligation.goal);
+        let return_ty = table.normalize_projection_ty(projection);
+        for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] {
+            let fn_x_trait = fn_x.get_id(db, krate)?;
+            trait_ref.trait_id = to_chalk_trait_id(fn_x_trait);
+            let obligation: chalk_ir::InEnvironment<chalk_ir::Goal<Interner>> = InEnvironment {
+                goal: trait_ref.clone().cast(Interner),
+                environment: trait_env.clone(),
+            };
+            let canonical = table.canonicalize(obligation.clone());
+            if db.trait_solve(krate, block, canonical.cast(Interner)).is_some() {
+                let ret_ty = table.resolve_completely(return_ty);
+                let args_ty = table.resolve_completely(args_ty);
+                let params = args_ty
+                    .as_tuple()?
+                    .iter(Interner)
+                    .map(|it| it.assert_ty_ref(Interner))
+                    .cloned()
+                    .collect();
+
+                return Some((
+                    fn_x,
+                    CallableSig::from_params_and_return(
+                        params,
+                        ret_ty,
+                        false,
+                        Safety::Safe,
+                        FnAbi::RustCall,
+                    ),
+                ));
+            }
+        }
+        unreachable!("It should at least implement FnOnce at this point");
+    } else {
+        None
+    }
 }
 
 struct PlaceholderCollector<'db> {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs
index 930bc7df5e0..02f2cd76159 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs
@@ -1,5 +1,6 @@
 //! Trait solving using Chalk.
 
+use core::fmt;
 use std::env::var;
 
 use chalk_ir::{fold::TypeFoldable, DebruijnIndex, GoalData};
@@ -209,7 +210,25 @@ pub enum FnTrait {
     Fn,
 }
 
+impl fmt::Display for FnTrait {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            FnTrait::FnOnce => write!(f, "FnOnce"),
+            FnTrait::FnMut => write!(f, "FnMut"),
+            FnTrait::Fn => write!(f, "Fn"),
+        }
+    }
+}
+
 impl FnTrait {
+    pub const fn function_name(&self) -> &'static str {
+        match self {
+            FnTrait::FnOnce => "call_once",
+            FnTrait::FnMut => "call_mut",
+            FnTrait::Fn => "call",
+        }
+    }
+
     const fn lang_item(self) -> LangItem {
         match self {
             FnTrait::FnOnce => LangItem::FnOnce,
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 85f33a10fcb..82d2bbb6cf3 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -35,7 +35,7 @@ pub mod term_search;
 
 mod display;
 
-use std::{iter, mem::discriminant, ops::ControlFlow};
+use std::{mem::discriminant, ops::ControlFlow};
 
 use arrayvec::ArrayVec;
 use base_db::{CrateDisplayName, CrateId, CrateOrigin, FileId};
@@ -52,7 +52,6 @@ use hir_def::{
     path::ImportAlias,
     per_ns::PerNs,
     resolver::{HasResolver, Resolver},
-    src::HasSource as _,
     AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId,
     EnumId, EnumVariantId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, HasModule,
     ImplId, InTypeConstId, ItemContainerId, LifetimeParamId, LocalFieldId, Lookup, MacroExpander,
@@ -141,7 +140,7 @@ pub use {
         display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite},
         layout::LayoutError,
         mir::{MirEvalError, MirLowerError},
-        PointerCast, Safety,
+        FnAbi, PointerCast, Safety,
     },
     // FIXME: Properly encapsulate mir
     hir_ty::{mir, Interner as ChalkTyInterner},
@@ -1965,7 +1964,7 @@ impl Function {
             .enumerate()
             .map(|(idx, ty)| {
                 let ty = Type { env: environment.clone(), ty: ty.clone() };
-                Param { func: self, ty, idx }
+                Param { func: Callee::Def(CallableDefId::FunctionId(self.id)), ty, idx }
             })
             .collect()
     }
@@ -1991,7 +1990,7 @@ impl Function {
             .skip(skip)
             .map(|(idx, ty)| {
                 let ty = Type { env: environment.clone(), ty: ty.clone() };
-                Param { func: self, ty, idx }
+                Param { func: Callee::Def(CallableDefId::FunctionId(self.id)), ty, idx }
             })
             .collect()
     }
@@ -2037,7 +2036,7 @@ impl Function {
             .skip(skip)
             .map(|(idx, ty)| {
                 let ty = Type { env: environment.clone(), ty: ty.clone() };
-                Param { func: self, ty, idx }
+                Param { func: Callee::Def(CallableDefId::FunctionId(self.id)), ty, idx }
             })
             .collect()
     }
@@ -2167,17 +2166,24 @@ impl From<hir_ty::Mutability> for Access {
 
 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
 pub struct Param {
-    func: Function,
+    func: Callee,
     /// The index in parameter list, including self parameter.
     idx: usize,
     ty: Type,
 }
 
 impl Param {
-    pub fn parent_fn(&self) -> Function {
-        self.func
+    pub fn parent_fn(&self) -> Option<Function> {
+        match self.func {
+            Callee::Def(CallableDefId::FunctionId(f)) => Some(f.into()),
+            _ => None,
+        }
     }
 
+    // pub fn parent_closure(&self) -> Option<Closure> {
+    //     self.func.as_ref().right().cloned()
+    // }
+
     pub fn index(&self) -> usize {
         self.idx
     }
@@ -2191,7 +2197,11 @@ impl Param {
     }
 
     pub fn as_local(&self, db: &dyn HirDatabase) -> Option<Local> {
-        let parent = DefWithBodyId::FunctionId(self.func.into());
+        let parent = match self.func {
+            Callee::Def(CallableDefId::FunctionId(it)) => DefWithBodyId::FunctionId(it),
+            Callee::Closure(closure, _) => db.lookup_intern_closure(closure.into()).0,
+            _ => return None,
+        };
         let body = db.body(parent);
         if let Some(self_param) = body.self_param.filter(|_| self.idx == 0) {
             Some(Local { parent, binding_id: self_param })
@@ -2205,18 +2215,45 @@ impl Param {
     }
 
     pub fn pattern_source(&self, db: &dyn HirDatabase) -> Option<ast::Pat> {
-        self.source(db).and_then(|p| p.value.pat())
+        self.source(db).and_then(|p| p.value.right()?.pat())
     }
 
-    pub fn source(&self, db: &dyn HirDatabase) -> Option<InFile<ast::Param>> {
-        let InFile { file_id, value } = self.func.source(db)?;
-        let params = value.param_list()?;
-        if params.self_param().is_some() {
-            params.params().nth(self.idx.checked_sub(params.self_param().is_some() as usize)?)
-        } else {
-            params.params().nth(self.idx)
+    pub fn source(
+        &self,
+        db: &dyn HirDatabase,
+    ) -> Option<InFile<Either<ast::SelfParam, ast::Param>>> {
+        match self.func {
+            Callee::Def(CallableDefId::FunctionId(func)) => {
+                let InFile { file_id, value } = Function { id: func }.source(db)?;
+                let params = value.param_list()?;
+                if let Some(self_param) = params.self_param() {
+                    if let Some(idx) = self.idx.checked_sub(1) {
+                        params.params().nth(idx).map(Either::Right)
+                    } else {
+                        Some(Either::Left(self_param))
+                    }
+                } else {
+                    params.params().nth(self.idx).map(Either::Right)
+                }
+                .map(|value| InFile { file_id, value })
+            }
+            Callee::Closure(closure, _) => {
+                let InternedClosure(owner, expr_id) = db.lookup_intern_closure(closure.into());
+                let (_, source_map) = db.body_with_source_map(owner);
+                let ast @ InFile { file_id, value } = source_map.expr_syntax(expr_id).ok()?;
+                let root = db.parse_or_expand(file_id);
+                match value.to_node(&root) {
+                    ast::Expr::ClosureExpr(it) => it
+                        .param_list()?
+                        .params()
+                        .nth(self.idx)
+                        .map(Either::Right)
+                        .map(|value| InFile { file_id: ast.file_id, value }),
+                    _ => None,
+                }
+            }
+            _ => None,
         }
-        .map(|value| InFile { file_id, value })
     }
 }
 
@@ -3372,34 +3409,21 @@ impl BuiltinAttr {
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 pub struct ToolModule {
-    krate: Option<CrateId>,
+    krate: CrateId,
     idx: u32,
 }
 
 impl ToolModule {
-    // FIXME: consider crates\hir_def\src\nameres\attr_resolution.rs?
     pub(crate) fn by_name(db: &dyn HirDatabase, krate: Crate, name: &str) -> Option<Self> {
-        if let builtin @ Some(_) = Self::builtin(name) {
-            return builtin;
-        }
+        let krate = krate.id;
         let idx =
-            db.crate_def_map(krate.id).registered_tools().iter().position(|it| it == name)? as u32;
-        Some(ToolModule { krate: Some(krate.id), idx })
-    }
-
-    fn builtin(name: &str) -> Option<Self> {
-        hir_def::attr::builtin::TOOL_MODULES
-            .iter()
-            .position(|&tool| tool == name)
-            .map(|idx| ToolModule { krate: None, idx: idx as u32 })
+            db.crate_def_map(krate).registered_tools().iter().position(|it| it == name)? as u32;
+        Some(ToolModule { krate, idx })
     }
 
     pub fn name(&self, db: &dyn HirDatabase) -> SmolStr {
         // FIXME: Return a `Name` here
-        match self.krate {
-            Some(krate) => db.crate_def_map(krate).registered_tools()[self.idx as usize].clone(),
-            None => SmolStr::new(hir_def::attr::builtin::TOOL_MODULES[self.idx as usize]),
-        }
+        db.crate_def_map(self.krate).registered_tools()[self.idx as usize].clone()
     }
 }
 
@@ -4292,27 +4316,37 @@ impl Type {
     }
 
     pub fn as_callable(&self, db: &dyn HirDatabase) -> Option<Callable> {
-        let mut the_ty = &self.ty;
         let callee = match self.ty.kind(Interner) {
-            TyKind::Ref(_, _, ty) if ty.as_closure().is_some() => {
-                the_ty = ty;
-                Callee::Closure(ty.as_closure().unwrap())
-            }
-            TyKind::Closure(id, _) => Callee::Closure(*id),
+            TyKind::Closure(id, subst) => Callee::Closure(*id, subst.clone()),
             TyKind::Function(_) => Callee::FnPtr,
             TyKind::FnDef(..) => Callee::Def(self.ty.callable_def(db)?),
-            _ => {
-                let sig = hir_ty::callable_sig_from_fnonce(&self.ty, self.env.clone(), db)?;
+            kind => {
+                // This will happen when it implements fn or fn mut, since we add an autoborrow adjustment
+                let (ty, kind) = if let TyKind::Ref(_, _, ty) = kind {
+                    (ty, ty.kind(Interner))
+                } else {
+                    (&self.ty, kind)
+                };
+                if let TyKind::Closure(closure, subst) = kind {
+                    let sig = ty.callable_sig(db)?;
+                    return Some(Callable {
+                        ty: self.clone(),
+                        sig,
+                        callee: Callee::Closure(*closure, subst.clone()),
+                        is_bound_method: false,
+                    });
+                }
+                let (fn_trait, sig) = hir_ty::callable_sig_from_fn_trait(ty, self.env.clone(), db)?;
                 return Some(Callable {
                     ty: self.clone(),
                     sig,
-                    callee: Callee::Other,
+                    callee: Callee::FnImpl(fn_trait),
                     is_bound_method: false,
                 });
             }
         };
 
-        let sig = the_ty.callable_sig(db)?;
+        let sig = self.ty.callable_sig(db)?;
         Some(Callable { ty: self.clone(), sig, callee, is_bound_method: false })
     }
 
@@ -4929,37 +4963,39 @@ pub struct Callable {
     sig: CallableSig,
     callee: Callee,
     /// Whether this is a method that was called with method call syntax.
-    pub(crate) is_bound_method: bool,
+    is_bound_method: bool,
 }
 
-#[derive(Debug)]
+#[derive(Clone, PartialEq, Eq, Hash, Debug)]
 enum Callee {
     Def(CallableDefId),
-    Closure(ClosureId),
+    Closure(ClosureId, Substitution),
     FnPtr,
-    Other,
+    FnImpl(FnTrait),
 }
 
 pub enum CallableKind {
     Function(Function),
     TupleStruct(Struct),
     TupleEnumVariant(Variant),
-    Closure,
+    Closure(Closure),
     FnPtr,
-    /// Some other type that implements `FnOnce`.
-    Other,
+    FnImpl(FnTrait),
 }
 
 impl Callable {
     pub fn kind(&self) -> CallableKind {
-        use Callee::*;
         match self.callee {
-            Def(CallableDefId::FunctionId(it)) => CallableKind::Function(it.into()),
-            Def(CallableDefId::StructId(it)) => CallableKind::TupleStruct(it.into()),
-            Def(CallableDefId::EnumVariantId(it)) => CallableKind::TupleEnumVariant(it.into()),
-            Closure(_) => CallableKind::Closure,
-            FnPtr => CallableKind::FnPtr,
-            Other => CallableKind::Other,
+            Callee::Def(CallableDefId::FunctionId(it)) => CallableKind::Function(it.into()),
+            Callee::Def(CallableDefId::StructId(it)) => CallableKind::TupleStruct(it.into()),
+            Callee::Def(CallableDefId::EnumVariantId(it)) => {
+                CallableKind::TupleEnumVariant(it.into())
+            }
+            Callee::Closure(id, ref subst) => {
+                CallableKind::Closure(Closure { id, subst: subst.clone() })
+            }
+            Callee::FnPtr => CallableKind::FnPtr,
+            Callee::FnImpl(fn_) => CallableKind::FnImpl(fn_),
         }
     }
     pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<(SelfParam, Type)> {
@@ -4973,43 +5009,15 @@ impl Callable {
     pub fn n_params(&self) -> usize {
         self.sig.params().len() - if self.is_bound_method { 1 } else { 0 }
     }
-    pub fn params(
-        &self,
-        db: &dyn HirDatabase,
-    ) -> Vec<(Option<Either<ast::SelfParam, ast::Pat>>, Type)> {
-        let types = self
-            .sig
+    pub fn params(&self) -> Vec<Param> {
+        self.sig
             .params()
             .iter()
+            .enumerate()
             .skip(if self.is_bound_method { 1 } else { 0 })
-            .map(|ty| self.ty.derived(ty.clone()));
-        let map_param = |it: ast::Param| it.pat().map(Either::Right);
-        let patterns = match self.callee {
-            Callee::Def(CallableDefId::FunctionId(func)) => {
-                let src = func.lookup(db.upcast()).source(db.upcast());
-                src.value.param_list().map(|param_list| {
-                    param_list
-                        .self_param()
-                        .map(|it| Some(Either::Left(it)))
-                        .filter(|_| !self.is_bound_method)
-                        .into_iter()
-                        .chain(param_list.params().map(map_param))
-                })
-            }
-            Callee::Closure(closure_id) => match closure_source(db, closure_id) {
-                Some(src) => src.param_list().map(|param_list| {
-                    param_list
-                        .self_param()
-                        .map(|it| Some(Either::Left(it)))
-                        .filter(|_| !self.is_bound_method)
-                        .into_iter()
-                        .chain(param_list.params().map(map_param))
-                }),
-                None => None,
-            },
-            _ => None,
-        };
-        patterns.into_iter().flatten().chain(iter::repeat(None)).zip(types).collect()
+            .map(|(idx, ty)| (idx, self.ty.derived(ty.clone())))
+            .map(|(idx, ty)| Param { func: self.callee.clone(), idx, ty })
+            .collect()
     }
     pub fn return_type(&self) -> Type {
         self.ty.derived(self.sig.ret().clone())
@@ -5017,17 +5025,9 @@ impl Callable {
     pub fn sig(&self) -> &CallableSig {
         &self.sig
     }
-}
 
-fn closure_source(db: &dyn HirDatabase, closure: ClosureId) -> Option<ast::ClosureExpr> {
-    let InternedClosure(owner, expr_id) = db.lookup_intern_closure(closure.into());
-    let (_, source_map) = db.body_with_source_map(owner);
-    let ast = source_map.expr_syntax(expr_id).ok()?;
-    let root = ast.file_syntax(db.upcast());
-    let expr = ast.value.to_node(&root);
-    match expr {
-        ast::Expr::ClosureExpr(it) => Some(it),
-        _ => None,
+    pub fn ty(&self) -> &Type {
+        &self.ty
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
index 057b03baef0..d2295840642 100644
--- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
@@ -307,7 +307,8 @@ impl SourceAnalyzer {
         db: &dyn HirDatabase,
         call: &ast::Expr,
     ) -> Option<Callable> {
-        self.type_of_expr(db, &call.clone())?.0.as_callable(db)
+        let (orig, adjusted) = self.type_of_expr(db, &call.clone())?;
+        adjusted.unwrap_or(orig).as_callable(db)
     }
 
     pub(crate) fn resolve_field(
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs
index 7f3b0d75883..37ea5123a71 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs
@@ -114,10 +114,10 @@ pub(crate) fn replace_with_eager_method(acc: &mut Assists, ctx: &AssistContext<'
     let callable = ctx.sema.resolve_method_call_as_callable(&call)?;
     let (_, receiver_ty) = callable.receiver_param(ctx.sema.db)?;
     let n_params = callable.n_params() + 1;
-    let params = callable.params(ctx.sema.db);
+    let params = callable.params();
 
     // FIXME: Check that the arg is of the form `() -> T`
-    if !params.first()?.1.impls_fnonce(ctx.sema.db) {
+    if !params.first()?.ty().impls_fnonce(ctx.sema.db) {
         return None;
     }
 
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs
index 1859825b3d6..23a06404f30 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs
@@ -253,11 +253,8 @@ fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<St
     };
 
     let (idx, _) = arg_list.args().find_position(|it| it == expr).unwrap();
-    let (pat, _) = func.params(sema.db).into_iter().nth(idx)?;
-    let pat = match pat? {
-        either::Either::Right(pat) => pat,
-        _ => return None,
-    };
+    let param = func.params().into_iter().nth(idx)?;
+    let pat = param.source(sema.db)?.value.right()?.pat()?;
     let name = var_name_from_pat(&pat)?;
     normalize(&name.to_string())
 }
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs b/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs
index 5780b5a5bb9..98d2e817546 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs
@@ -1,7 +1,7 @@
 //! This module provides functionality for querying callable information about a token.
 
 use either::Either;
-use hir::{Semantics, Type};
+use hir::{InFile, Semantics, Type};
 use parser::T;
 use syntax::{
     ast::{self, HasArgList, HasName},
@@ -13,7 +13,7 @@ use crate::RootDatabase;
 #[derive(Debug)]
 pub struct ActiveParameter {
     pub ty: Type,
-    pub pat: Option<Either<ast::SelfParam, ast::Pat>>,
+    pub src: Option<InFile<Either<ast::SelfParam, ast::Param>>>,
 }
 
 impl ActiveParameter {
@@ -22,18 +22,18 @@ impl ActiveParameter {
         let (signature, active_parameter) = callable_for_token(sema, token)?;
 
         let idx = active_parameter?;
-        let mut params = signature.params(sema.db);
+        let mut params = signature.params();
         if idx >= params.len() {
             cov_mark::hit!(too_many_arguments);
             return None;
         }
-        let (pat, ty) = params.swap_remove(idx);
-        Some(ActiveParameter { ty, pat })
+        let param = params.swap_remove(idx);
+        Some(ActiveParameter { ty: param.ty().clone(), src: param.source(sema.db) })
     }
 
     pub fn ident(&self) -> Option<ast::Name> {
-        self.pat.as_ref().and_then(|param| match param {
-            Either::Right(ast::Pat::IdentPat(ident)) => ident.name(),
+        self.src.as_ref().and_then(|param| match param.value.as_ref().right()?.pat()? {
+            ast::Pat::IdentPat(ident) => ident.name(),
             _ => None,
         })
     }
@@ -60,10 +60,7 @@ pub fn callable_for_node(
     token: &SyntaxToken,
 ) -> Option<(hir::Callable, Option<usize>)> {
     let callable = match calling_node {
-        ast::CallableExpr::Call(call) => {
-            let expr = call.expr()?;
-            sema.type_of_expr(&expr)?.adjusted().as_callable(sema.db)
-        }
+        ast::CallableExpr::Call(call) => sema.resolve_expr_as_callable(&call.expr()?),
         ast::CallableExpr::MethodCall(call) => sema.resolve_method_call_as_callable(call),
     }?;
     let active_param = calling_node.arg_list().map(|arg_list| {
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs
index 8302b015dda..92478ef480d 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs
@@ -41,6 +41,7 @@ pub enum FormatSpecifier {
     Escape,
 }
 
+// FIXME: Remove this, we can use rustc_format_parse instead
 pub fn lex_format_specifiers(
     string: &ast::String,
     mut callback: &mut dyn FnMut(TextRange, FormatSpecifier),
diff --git a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
index 458b852e2a1..654a1cd3164 100644
--- a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
@@ -109,12 +109,12 @@ pub(crate) fn outgoing_calls(
                     let expr = call.expr()?;
                     let callable = sema.type_of_expr(&expr)?.original.as_callable(db)?;
                     match callable.kind() {
-                        hir::CallableKind::Function(it) => {
-                            let range = expr.syntax().text_range();
-                            it.try_to_nav(db).zip(Some(range))
-                        }
+                        hir::CallableKind::Function(it) => it.try_to_nav(db),
+                        hir::CallableKind::TupleEnumVariant(it) => it.try_to_nav(db),
+                        hir::CallableKind::TupleStruct(it) => it.try_to_nav(db),
                         _ => None,
                     }
+                    .zip(Some(expr.syntax().text_range()))
                 }
                 ast::CallableExpr::MethodCall(expr) => {
                     let range = expr.name_ref()?.syntax().text_range();
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs
index 96e845b2f32..fb50c49a3ae 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs
@@ -24,34 +24,29 @@ pub(super) fn hints(
 
     let (callable, arg_list) = get_callable(sema, &expr)?;
     let hints = callable
-        .params(sema.db)
+        .params()
         .into_iter()
         .zip(arg_list.args())
-        .filter_map(|((param, _ty), arg)| {
+        .filter_map(|(p, arg)| {
             // Only annotate hints for expressions that exist in the original file
             let range = sema.original_range_opt(arg.syntax())?;
-            let (param_name, name_syntax) = match param.as_ref()? {
+            let source = p.source(sema.db)?;
+            let (param_name, name_syntax) = match source.value.as_ref() {
                 Either::Left(pat) => (pat.name()?, pat.name()),
-                Either::Right(pat) => match pat {
+                Either::Right(param) => match param.pat()? {
                     ast::Pat::IdentPat(it) => (it.name()?, it.name()),
                     _ => return None,
                 },
             };
+            // make sure the file is cached so we can map out of macros
+            sema.parse_or_expand(source.file_id);
             Some((name_syntax, param_name, arg, range))
         })
         .filter(|(_, param_name, arg, _)| {
             !should_hide_param_name_hint(sema, &callable, &param_name.text(), arg)
         })
         .map(|(param, param_name, _, FileRange { range, .. })| {
-            let mut linked_location = None;
-            if let Some(name) = param {
-                if let hir::CallableKind::Function(f) = callable.kind() {
-                    // assert the file is cached so we can map out of macros
-                    if sema.source(f).is_some() {
-                        linked_location = sema.original_range_opt(name.syntax());
-                    }
-                }
-            }
+            let linked_location = param.and_then(|name| sema.original_range_opt(name.syntax()));
 
             let colon = if config.render_colons { ":" } else { "" };
             let label =
diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
index b2eb5a5fff1..378a38892c7 100644
--- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
@@ -201,7 +201,21 @@ fn signature_help_for_call(
                 variant.name(db).display(db)
             );
         }
-        hir::CallableKind::Closure | hir::CallableKind::FnPtr | hir::CallableKind::Other => (),
+        hir::CallableKind::Closure(closure) => {
+            let fn_trait = closure.fn_trait(db);
+            format_to!(res.signature, "impl {fn_trait}")
+        }
+        hir::CallableKind::FnPtr => format_to!(res.signature, "fn"),
+        hir::CallableKind::FnImpl(fn_trait) => match callable.ty().as_adt() {
+            // FIXME: Render docs of the concrete trait impl function
+            Some(adt) => format_to!(
+                res.signature,
+                "<{} as {fn_trait}>::{}",
+                adt.name(db).display(db),
+                fn_trait.function_name()
+            ),
+            None => format_to!(res.signature, "impl {fn_trait}"),
+        },
     }
 
     res.signature.push('(');
@@ -210,12 +224,15 @@ fn signature_help_for_call(
             format_to!(res.signature, "{}", self_param.display(db))
         }
         let mut buf = String::new();
-        for (idx, (pat, ty)) in callable.params(db).into_iter().enumerate() {
+        for (idx, p) in callable.params().into_iter().enumerate() {
             buf.clear();
-            if let Some(pat) = pat {
-                match pat {
-                    Either::Left(_self) => format_to!(buf, "self: "),
-                    Either::Right(pat) => format_to!(buf, "{}: ", pat),
+            if let Some(param) = p.source(sema.db) {
+                match param.value {
+                    Either::Right(param) => match param.pat() {
+                        Some(pat) => format_to!(buf, "{}: ", pat),
+                        None => format_to!(buf, "?: "),
+                    },
+                    Either::Left(_) => format_to!(buf, "self: "),
                 }
             }
             // APITs (argument position `impl Trait`s) are inferred as {unknown} as the user is
@@ -223,9 +240,9 @@ fn signature_help_for_call(
             // In that case, fall back to render definitions of the respective parameters.
             // This is overly conservative: we do not substitute known type vars
             // (see FIXME in tests::impl_trait) and falling back on any unknowns.
-            match (ty.contains_unknown(), fn_params.as_deref()) {
+            match (p.ty().contains_unknown(), fn_params.as_deref()) {
                 (true, Some(fn_params)) => format_to!(buf, "{}", fn_params[idx].ty().display(db)),
-                _ => format_to!(buf, "{}", ty.display(db)),
+                _ => format_to!(buf, "{}", p.ty().display(db)),
             }
             res.push_call_param(&buf);
         }
@@ -242,9 +259,9 @@ fn signature_help_for_call(
             render(func.ret_type(db))
         }
         hir::CallableKind::Function(_)
-        | hir::CallableKind::Closure
+        | hir::CallableKind::Closure(_)
         | hir::CallableKind::FnPtr
-        | hir::CallableKind::Other => render(callable.return_type()),
+        | hir::CallableKind::FnImpl(_) => render(callable.return_type()),
         hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {}
     }
     Some(res)
@@ -1346,14 +1363,42 @@ fn test() { S.foo($0); }
 struct S;
 fn foo(s: S) -> i32 { 92 }
 fn main() {
+    let _move = S;
+    (|s| {{_move}; foo(s)})($0)
+}
+        "#,
+            expect![[r#"
+                impl FnOnce(s: S) -> i32
+                            ^^^^
+            "#]],
+        );
+        check(
+            r#"
+struct S;
+fn foo(s: S) -> i32 { 92 }
+fn main() {
     (|s| foo(s))($0)
 }
         "#,
             expect![[r#"
-                (s: S) -> i32
-                 ^^^^
+                impl Fn(s: S) -> i32
+                        ^^^^
             "#]],
-        )
+        );
+        check(
+            r#"
+struct S;
+fn foo(s: S) -> i32 { 92 }
+fn main() {
+    let mut mutate = 0;
+    (|s| { mutate = 1; foo(s) })($0)
+}
+        "#,
+            expect![[r#"
+                impl FnMut(s: S) -> i32
+                           ^^^^
+            "#]],
+        );
     }
 
     #[test]
@@ -1383,13 +1428,82 @@ fn main(f: fn(i32, f64) -> char) {
 }
         "#,
             expect![[r#"
-                (i32, f64) -> char
-                 ---  ^^^
+                fn(i32, f64) -> char
+                   ---  ^^^
             "#]],
         )
     }
 
     #[test]
+    fn call_info_for_fn_impl() {
+        check(
+            r#"
+struct S;
+impl core::ops::FnOnce<(i32, f64)> for S {
+    type Output = char;
+}
+impl core::ops::FnMut<(i32, f64)> for S {}
+impl core::ops::Fn<(i32, f64)> for S {}
+fn main() {
+    S($0);
+}
+        "#,
+            expect![[r#"
+                <S as Fn>::call(i32, f64) -> char
+                                ^^^  ---
+            "#]],
+        );
+        check(
+            r#"
+struct S;
+impl core::ops::FnOnce<(i32, f64)> for S {
+    type Output = char;
+}
+impl core::ops::FnMut<(i32, f64)> for S {}
+impl core::ops::Fn<(i32, f64)> for S {}
+fn main() {
+    S(1, $0);
+}
+        "#,
+            expect![[r#"
+                <S as Fn>::call(i32, f64) -> char
+                                ---  ^^^
+            "#]],
+        );
+        check(
+            r#"
+struct S;
+impl core::ops::FnOnce<(i32, f64)> for S {
+    type Output = char;
+}
+impl core::ops::FnOnce<(char, char)> for S {
+    type Output = f64;
+}
+fn main() {
+    S($0);
+}
+        "#,
+            expect![""],
+        );
+        check(
+            r#"
+struct S;
+impl core::ops::FnOnce<(i32, f64)> for S {
+    type Output = char;
+}
+impl core::ops::FnOnce<(char, char)> for S {
+    type Output = f64;
+}
+fn main() {
+    // FIXME: The ide layer loses the calling info here so we get an ambiguous trait solve result
+    S(0i32, $0);
+}
+        "#,
+            expect![""],
+        );
+    }
+
+    #[test]
     fn call_info_for_unclosed_call() {
         check(
             r#"
@@ -1794,19 +1908,19 @@ fn f<F: FnOnce(u8, u16) -> i32>(f: F) {
 }
 "#,
             expect![[r#"
-                (u8, u16) -> i32
-                 ^^  ---
+                impl FnOnce(u8, u16) -> i32
+                            ^^  ---
             "#]],
         );
         check(
             r#"
-fn f<T, F: FnOnce(&T, u16) -> &T>(f: F) {
+fn f<T, F: FnMut(&T, u16) -> &T>(f: F) {
     f($0)
 }
 "#,
             expect![[r#"
-                (&T, u16) -> &T
-                 ^^  ---
+                impl FnMut(&T, u16) -> &T
+                           ^^  ---
             "#]],
         );
     }
@@ -1826,7 +1940,7 @@ fn take<C, Error>(
 }
 "#,
             expect![[r#"
-                () -> i32
+                impl Fn() -> i32
             "#]],
         );
     }
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 0439e509d21..2f387968c96 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
@@ -9,8 +9,9 @@ pub(super) fn highlight_escape_string<T: IsString>(
     string: &T,
     start: TextSize,
 ) {
+    let text = string.text();
     string.escaped_char_ranges(&mut |piece_range, char| {
-        if string.text()[piece_range.start().into()..].starts_with('\\') {
+        if text[piece_range.start().into()..].starts_with('\\') {
             let highlight = match char {
                 Ok(_) => HlTag::EscapeSequence,
                 Err(_) => HlTag::InvalidEscapeSequence,
@@ -33,17 +34,15 @@ pub(super) fn highlight_escape_char(stack: &mut Highlights, char: &Char, start:
     }
 
     let text = char.text();
-    if !text.starts_with('\'') || !text.ends_with('\'') {
+    let Some(text) = text
+        .strip_prefix('\'')
+        .and_then(|it| it.strip_suffix('\''))
+        .filter(|it| it.starts_with('\\'))
+    else {
         return;
-    }
-
-    let text = &text[1..text.len() - 1];
-    if !text.starts_with('\\') {
-        return;
-    }
+    };
 
-    let range =
-        TextRange::new(start + TextSize::from(1), start + TextSize::from(text.len() as u32 + 1));
+    let range = TextRange::at(start + TextSize::from(1), TextSize::from(text.len() as u32));
     stack.add(HlRange { range, highlight: HlTag::EscapeSequence.into(), binding_hash: None })
 }
 
@@ -54,16 +53,14 @@ pub(super) fn highlight_escape_byte(stack: &mut Highlights, byte: &Byte, start:
     }
 
     let text = byte.text();
-    if !text.starts_with("b'") || !text.ends_with('\'') {
+    let Some(text) = text
+        .strip_prefix("b'")
+        .and_then(|it| it.strip_suffix('\''))
+        .filter(|it| it.starts_with('\\'))
+    else {
         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));
+    let range = TextRange::at(start + TextSize::from(2), TextSize::from(text.len() as u32));
     stack.add(HlRange { range, highlight: HlTag::EscapeSequence.into(), binding_hash: None })
 }
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 16599881d64..1ce548f8fc7 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
@@ -8,6 +8,7 @@ use std::{
 use rustc_lexer::unescape::{
     unescape_byte, unescape_char, unescape_mixed, unescape_unicode, EscapeError, MixedUnit, Mode,
 };
+use stdx::always;
 
 use crate::{
     ast::{self, AstToken},
@@ -181,25 +182,25 @@ pub trait IsString: AstToken {
         self.quote_offsets().map(|it| it.quotes.1)
     }
     fn escaped_char_ranges(&self, cb: &mut dyn FnMut(TextRange, Result<char, EscapeError>)) {
-        let text_range_no_quotes = match self.text_range_between_quotes() {
-            Some(it) => it,
-            None => return,
-        };
+        let Some(text_range_no_quotes) = self.text_range_between_quotes() else { 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_unicode(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);
+            if let Some((s, e)) = range.start.try_into().ok().zip(range.end.try_into().ok()) {
+                cb(TextRange::new(s, e) + offset, unescaped_char);
+            }
         });
     }
     fn map_range_up(&self, range: TextRange) -> Option<TextRange> {
         let contents_range = self.text_range_between_quotes()?;
-        assert!(TextRange::up_to(contents_range.len()).contains_range(range));
-        Some(range + contents_range.start())
+        if always!(TextRange::up_to(contents_range.len()).contains_range(range)) {
+            Some(range + contents_range.start())
+        } else {
+            None
+        }
     }
 }