about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/hir-def/src/body.rs6
-rw-r--r--crates/hir-def/src/body/lower.rs26
-rw-r--r--crates/hir-def/src/body/scope.rs5
-rw-r--r--crates/hir-ty/src/diagnostics/decl_check.rs72
-rw-r--r--crates/hir-ty/src/mir/eval.rs5
-rw-r--r--crates/hir-ty/src/tests.rs14
-rw-r--r--crates/hir/src/diagnostics.rs11
-rw-r--r--crates/hir/src/lib.rs99
-rw-r--r--crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs7
-rw-r--r--crates/ide-diagnostics/src/handlers/missing_fields.rs16
-rw-r--r--crates/ide-diagnostics/src/handlers/no_such_field.rs18
-rw-r--r--crates/ide-diagnostics/src/handlers/private_assoc_item.rs10
-rw-r--r--crates/ide-diagnostics/src/handlers/type_mismatch.rs36
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs11
-rw-r--r--crates/syntax/src/ptr.rs18
15 files changed, 164 insertions, 190 deletions
diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs
index c0baf6011f7..1942c60c075 100644
--- a/crates/hir-def/src/body.rs
+++ b/crates/hir-def/src/body.rs
@@ -57,7 +57,7 @@ pub struct Body {
 pub type ExprPtr = AstPtr<ast::Expr>;
 pub type ExprSource = InFile<ExprPtr>;
 
-pub type PatPtr = Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>;
+pub type PatPtr = AstPtr<Either<ast::Pat, ast::SelfParam>>;
 pub type PatSource = InFile<PatPtr>;
 
 pub type LabelPtr = AstPtr<ast::Label>;
@@ -356,12 +356,12 @@ impl BodySourceMap {
     }
 
     pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<PatId> {
-        let src = node.map(|it| Either::Left(AstPtr::new(it)));
+        let src = node.map(|it| AstPtr::new(it).wrap_left());
         self.pat_map.get(&src).cloned()
     }
 
     pub fn node_self_param(&self, node: InFile<&ast::SelfParam>) -> Option<PatId> {
-        let src = node.map(|it| Either::Right(AstPtr::new(it)));
+        let src = node.map(|it| AstPtr::new(it).wrap_right());
         self.pat_map.get(&src).cloned()
     }
 
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index cc02df80a8f..e4158d7564b 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -196,16 +196,12 @@ impl ExprCollector<'_> {
             if let Some(self_param) =
                 param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false))
             {
-                let ptr = AstPtr::new(&self_param);
-                let binding_id: la_arena::Idx<Binding> = self.alloc_binding(
-                    name![self],
-                    BindingAnnotation::new(
-                        self_param.mut_token().is_some() && self_param.amp_token().is_none(),
-                        false,
-                    ),
-                );
-                let param_pat =
-                    self.alloc_pat(Pat::Bind { id: binding_id, subpat: None }, Either::Right(ptr));
+                let is_mutable =
+                    self_param.mut_token().is_some() && self_param.amp_token().is_none();
+                let ptr = AstPtr::new(&Either::Right(self_param));
+                let binding_id: la_arena::Idx<Binding> =
+                    self.alloc_binding(name![self], BindingAnnotation::new(is_mutable, false));
+                let param_pat = self.alloc_pat(Pat::Bind { id: binding_id, subpat: None }, ptr);
                 self.add_definition_to_binding(binding_id, param_pat);
                 self.body.params.push(param_pat);
             }
@@ -1260,8 +1256,8 @@ impl ExprCollector<'_> {
                     (Some(id), Pat::Bind { id, subpat })
                 };
 
-                let ptr = AstPtr::new(&pat);
-                let pat = self.alloc_pat(pattern, Either::Left(ptr));
+                let ptr = AstPtr::new(&Either::Left(pat));
+                let pat = self.alloc_pat(pattern, ptr);
                 if let Some(binding_id) = binding {
                     self.add_definition_to_binding(binding_id, pat);
                 }
@@ -1395,7 +1391,7 @@ impl ExprCollector<'_> {
             ast::Pat::MacroPat(mac) => match mac.macro_call() {
                 Some(call) => {
                     let macro_ptr = AstPtr::new(&call);
-                    let src = self.expander.to_source(Either::Left(AstPtr::new(&pat)));
+                    let src = self.expander.to_source(AstPtr::new(&Either::Left(pat)));
                     let pat =
                         self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| {
                             this.collect_pat_opt(expanded_pat, binding_list)
@@ -1430,8 +1426,8 @@ impl ExprCollector<'_> {
                 Pat::Range { start, end }
             }
         };
-        let ptr = AstPtr::new(&pat);
-        self.alloc_pat(pattern, Either::Left(ptr))
+        let ptr = AstPtr::new(&Either::Left(pat));
+        self.alloc_pat(pattern, ptr)
     }
 
     fn collect_pat_opt(&mut self, pat: Option<ast::Pat>, binding_list: &mut BindingList) -> PatId {
diff --git a/crates/hir-def/src/body/scope.rs b/crates/hir-def/src/body/scope.rs
index f6946663135..baca293e290 100644
--- a/crates/hir-def/src/body/scope.rs
+++ b/crates/hir-def/src/body/scope.rs
@@ -475,10 +475,7 @@ fn foo() {
             .pat_syntax(*body.bindings[resolved.binding()].definitions.first().unwrap())
             .unwrap();
 
-        let local_name = pat_src.value.either(
-            |it| it.syntax_node_ptr().to_node(file.syntax()),
-            |it| it.syntax_node_ptr().to_node(file.syntax()),
-        );
+        let local_name = pat_src.value.syntax_node_ptr().to_node(file.syntax());
         assert_eq!(local_name.text_range(), expected_name.syntax().text_range());
     }
 
diff --git a/crates/hir-ty/src/diagnostics/decl_check.rs b/crates/hir-ty/src/diagnostics/decl_check.rs
index 36d69edf9d5..60563e02b70 100644
--- a/crates/hir-ty/src/diagnostics/decl_check.rs
+++ b/crates/hir-ty/src/diagnostics/decl_check.rs
@@ -336,48 +336,44 @@ impl<'a> DeclValidator<'a> {
 
         for (id, replacement) in pats_replacements {
             if let Ok(source_ptr) = source_map.pat_syntax(id) {
-                if let Some(expr) = source_ptr.value.as_ref().left() {
+                if let Some(ptr) = source_ptr.value.clone().cast::<ast::IdentPat>() {
                     let root = source_ptr.file_syntax(self.db.upcast());
-                    if let ast::Pat::IdentPat(ident_pat) = expr.to_node(&root) {
-                        let parent = match ident_pat.syntax().parent() {
-                            Some(parent) => parent,
-                            None => continue,
-                        };
-                        let name_ast = match ident_pat.name() {
-                            Some(name_ast) => name_ast,
-                            None => continue,
-                        };
+                    let ident_pat = ptr.to_node(&root);
+                    let parent = match ident_pat.syntax().parent() {
+                        Some(parent) => parent,
+                        None => continue,
+                    };
+                    let name_ast = match ident_pat.name() {
+                        Some(name_ast) => name_ast,
+                        None => continue,
+                    };
+
+                    let is_param = ast::Param::can_cast(parent.kind());
+
+                    // We have to check that it's either `let var = ...` or `var @ Variant(_)` statement,
+                    // because e.g. match arms are patterns as well.
+                    // In other words, we check that it's a named variable binding.
+                    let is_binding = ast::LetStmt::can_cast(parent.kind())
+                        || (ast::MatchArm::can_cast(parent.kind())
+                            && ident_pat.at_token().is_some());
+                    if !(is_param || is_binding) {
+                        // This pattern is not an actual variable declaration, e.g. `Some(val) => {..}` match arm.
+                        continue;
+                    }
 
-                        let is_param = ast::Param::can_cast(parent.kind());
-
-                        // We have to check that it's either `let var = ...` or `var @ Variant(_)` statement,
-                        // because e.g. match arms are patterns as well.
-                        // In other words, we check that it's a named variable binding.
-                        let is_binding = ast::LetStmt::can_cast(parent.kind())
-                            || (ast::MatchArm::can_cast(parent.kind())
-                                && ident_pat.at_token().is_some());
-                        if !(is_param || is_binding) {
-                            // This pattern is not an actual variable declaration, e.g. `Some(val) => {..}` match arm.
-                            continue;
-                        }
+                    let ident_type =
+                        if is_param { IdentType::Parameter } else { IdentType::Variable };
 
-                        let ident_type =
-                            if is_param { IdentType::Parameter } else { IdentType::Variable };
-
-                        let diagnostic = IncorrectCase {
-                            file: source_ptr.file_id,
-                            ident_type,
-                            ident: AstPtr::new(&name_ast),
-                            expected_case: replacement.expected_case,
-                            ident_text: replacement
-                                .current_name
-                                .display(self.db.upcast())
-                                .to_string(),
-                            suggested_text: replacement.suggested_text,
-                        };
+                    let diagnostic = IncorrectCase {
+                        file: source_ptr.file_id,
+                        ident_type,
+                        ident: AstPtr::new(&name_ast),
+                        expected_case: replacement.expected_case,
+                        ident_text: replacement.current_name.display(self.db.upcast()).to_string(),
+                        suggested_text: replacement.suggested_text,
+                    };
 
-                        self.sink.push(diagnostic);
-                    }
+                    self.sink.push(diagnostic);
                 }
             }
         }
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index 98c78f7f305..7823c320341 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -375,10 +375,7 @@ impl MirEvalError {
                         Err(_) => continue,
                     },
                     MirSpan::PatId(p) => match source_map.pat_syntax(*p) {
-                        Ok(s) => s.map(|it| match it {
-                            Either::Left(e) => e.into(),
-                            Either::Right(e) => e.into(),
-                        }),
+                        Ok(s) => s.map(|it| it.syntax_node_ptr()),
                         Err(_) => continue,
                     },
                     MirSpan::Unknown => continue,
diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs
index d22d0d85c8e..1446e83fa88 100644
--- a/crates/hir-ty/src/tests.rs
+++ b/crates/hir-ty/src/tests.rs
@@ -269,12 +269,7 @@ fn pat_node(
     Some(match body_source_map.pat_syntax(pat) {
         Ok(sp) => {
             let root = db.parse_or_expand(sp.file_id);
-            sp.map(|ptr| {
-                ptr.either(
-                    |it| it.to_node(&root).syntax().clone(),
-                    |it| it.to_node(&root).syntax().clone(),
-                )
-            })
+            sp.map(|ptr| ptr.to_node(&root).syntax().clone())
         }
         Err(SyntheticSyntax) => return None,
     })
@@ -303,12 +298,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
             let syntax_ptr = match body_source_map.pat_syntax(pat) {
                 Ok(sp) => {
                     let root = db.parse_or_expand(sp.file_id);
-                    sp.map(|ptr| {
-                        ptr.either(
-                            |it| it.to_node(&root).syntax().clone(),
-                            |it| it.to_node(&root).syntax().clone(),
-                        )
-                    })
+                    sp.map(|ptr| ptr.to_node(&root).syntax().clone())
                 }
                 Err(SyntheticSyntax) => continue,
             };
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 66ad95c5597..67d3169243c 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -174,20 +174,19 @@ pub struct MalformedDerive {
 
 #[derive(Debug)]
 pub struct NoSuchField {
-    pub field: InFile<Either<AstPtr<ast::RecordExprField>, AstPtr<ast::RecordPatField>>>,
+    pub field: InFile<AstPtr<Either<ast::RecordExprField, ast::RecordPatField>>>,
     pub private: bool,
 }
 
 #[derive(Debug)]
 pub struct PrivateAssocItem {
-    pub expr_or_pat:
-        InFile<Either<AstPtr<ast::Expr>, Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>>>,
+    pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, Either<ast::Pat, ast::SelfParam>>>>,
     pub item: AssocItem,
 }
 
 #[derive(Debug)]
 pub struct MismatchedTupleStructPatArgCount {
-    pub expr_or_pat: InFile<Either<AstPtr<ast::Expr>, AstPtr<ast::Pat>>>,
+    pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
     pub expected: usize,
     pub found: usize,
 }
@@ -228,7 +227,7 @@ pub struct MissingUnsafe {
 #[derive(Debug)]
 pub struct MissingFields {
     pub file: HirFileId,
-    pub field_list_parent: Either<AstPtr<ast::RecordExpr>, AstPtr<ast::RecordPat>>,
+    pub field_list_parent: AstPtr<Either<ast::RecordExpr, ast::RecordPat>>,
     pub field_list_parent_path: Option<AstPtr<ast::Path>>,
     pub missed_fields: Vec<Name>,
 }
@@ -255,7 +254,7 @@ pub struct MissingMatchArms {
 
 #[derive(Debug)]
 pub struct TypeMismatch {
-    pub expr_or_pat: Either<InFile<AstPtr<ast::Expr>>, InFile<AstPtr<ast::Pat>>>,
+    pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
     pub expected: Type,
     pub actual: Type,
 }
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 8e48afd6af8..8246297705c 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -1509,10 +1509,10 @@ impl DefWithBody {
                 &hir_ty::InferenceDiagnostic::NoSuchField { field: expr, private } => {
                     let expr_or_pat = match expr {
                         ExprOrPatId::ExprId(expr) => {
-                            source_map.field_syntax(expr).map(Either::Left)
+                            source_map.field_syntax(expr).map(AstPtr::wrap_left)
                         }
                         ExprOrPatId::PatId(pat) => {
-                            source_map.pat_field_syntax(pat).map(Either::Right)
+                            source_map.pat_field_syntax(pat).map(AstPtr::wrap_right)
                         }
                     };
                     acc.push(NoSuchField { field: expr_or_pat, private }.into())
@@ -1530,8 +1530,8 @@ impl DefWithBody {
                 }
                 &hir_ty::InferenceDiagnostic::PrivateAssocItem { id, item } => {
                     let expr_or_pat = match id {
-                        ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(Either::Left),
-                        ExprOrPatId::PatId(pat) => pat_syntax(pat).map(Either::Right),
+                        ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(AstPtr::wrap_left),
+                        ExprOrPatId::PatId(pat) => pat_syntax(pat).map(AstPtr::wrap_right),
                     };
                     let item = item.into();
                     acc.push(PrivateAssocItem { expr_or_pat, item }.into())
@@ -1609,12 +1609,17 @@ impl DefWithBody {
                     found,
                 } => {
                     let expr_or_pat = match pat {
-                        ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(Either::Left),
-                        ExprOrPatId::PatId(pat) => source_map
-                            .pat_syntax(pat)
-                            .expect("unexpected synthetic")
-                            .map(|it| it.unwrap_left())
-                            .map(Either::Right),
+                        ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(AstPtr::wrap_left),
+                        ExprOrPatId::PatId(pat) => {
+                            let InFile { file_id, value } =
+                                source_map.pat_syntax(pat).expect("unexpected synthetic");
+
+                            // cast from Either<Pat, SelfParam> -> Either<_, Pat>
+                            let Some(ptr) = AstPtr::try_from_raw(value.syntax_node_ptr()) else {
+                                continue;
+                            };
+                            InFile { file_id, value: ptr }
+                        }
                     };
                     acc.push(
                         MismatchedTupleStructPatArgCount { expr_or_pat, expected, found }.into(),
@@ -1628,11 +1633,15 @@ impl DefWithBody {
                 ExprOrPatId::PatId(pat) => source_map.pat_syntax(pat).map(Either::Right),
             };
             let expr_or_pat = match expr_or_pat {
-                Ok(Either::Left(expr)) => Either::Left(expr),
-                Ok(Either::Right(InFile { file_id, value: Either::Left(pat) })) => {
-                    Either::Right(InFile { file_id, value: pat })
+                Ok(Either::Left(expr)) => expr.map(AstPtr::wrap_left),
+                Ok(Either::Right(InFile { file_id, value: pat })) => {
+                    // cast from Either<Pat, SelfParam> -> Either<_, Pat>
+                    let Some(ptr) = AstPtr::try_from_raw(pat.syntax_node_ptr()) else {
+                        continue;
+                    };
+                    InFile { file_id, value: ptr }
                 }
-                Ok(Either::Right(_)) | Err(SyntheticSyntax) => continue,
+                Err(SyntheticSyntax) => continue,
             };
 
             acc.push(
@@ -1667,10 +1676,7 @@ impl DefWithBody {
                             Err(_) => continue,
                         },
                         mir::MirSpan::PatId(p) => match source_map.pat_syntax(p) {
-                            Ok(s) => s.map(|it| match it {
-                                Either::Left(e) => e.into(),
-                                Either::Right(e) => e.into(),
-                            }),
+                            Ok(s) => s.map(|it| it.into()),
                             Err(_) => continue,
                         },
                         mir::MirSpan::Unknown => continue,
@@ -1721,10 +1727,7 @@ impl DefWithBody {
                                         Err(_) => continue,
                                     },
                                     mir::MirSpan::PatId(p) => match source_map.pat_syntax(*p) {
-                                        Ok(s) => s.map(|it| match it {
-                                            Either::Left(e) => e.into(),
-                                            Either::Right(e) => e.into(),
-                                        }),
+                                        Ok(s) => s.map(|it| it.into()),
                                         Err(_) => continue,
                                     },
                                     mir::MirSpan::Unknown => continue,
@@ -1763,18 +1766,18 @@ impl DefWithBody {
                             Ok(source_ptr) => {
                                 let root = source_ptr.file_syntax(db.upcast());
                                 if let ast::Expr::RecordExpr(record_expr) =
-                                    &source_ptr.value.to_node(&root)
+                                    source_ptr.value.to_node(&root)
                                 {
                                     if record_expr.record_expr_field_list().is_some() {
+                                        let field_list_parent_path =
+                                            record_expr.path().map(|path| AstPtr::new(&path));
                                         acc.push(
                                             MissingFields {
                                                 file: source_ptr.file_id,
-                                                field_list_parent: Either::Left(AstPtr::new(
+                                                field_list_parent: AstPtr::new(&Either::Left(
                                                     record_expr,
                                                 )),
-                                                field_list_parent_path: record_expr
-                                                    .path()
-                                                    .map(|path| AstPtr::new(&path)),
+                                                field_list_parent_path,
                                                 missed_fields,
                                             }
                                             .into(),
@@ -1786,24 +1789,24 @@ impl DefWithBody {
                         },
                         Either::Right(record_pat) => match source_map.pat_syntax(record_pat) {
                             Ok(source_ptr) => {
-                                if let Some(expr) = source_ptr.value.as_ref().left() {
+                                if let Some(ptr) = source_ptr.value.clone().cast::<ast::RecordPat>()
+                                {
                                     let root = source_ptr.file_syntax(db.upcast());
-                                    if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) {
-                                        if record_pat.record_pat_field_list().is_some() {
-                                            acc.push(
-                                                MissingFields {
-                                                    file: source_ptr.file_id,
-                                                    field_list_parent: Either::Right(AstPtr::new(
-                                                        &record_pat,
-                                                    )),
-                                                    field_list_parent_path: record_pat
-                                                        .path()
-                                                        .map(|path| AstPtr::new(&path)),
-                                                    missed_fields,
-                                                }
-                                                .into(),
-                                            )
-                                        }
+                                    let record_pat = ptr.to_node(&root);
+                                    if record_pat.record_pat_field_list().is_some() {
+                                        let field_list_parent_path =
+                                            record_pat.path().map(|path| AstPtr::new(&path));
+                                        acc.push(
+                                            MissingFields {
+                                                file: source_ptr.file_id,
+                                                field_list_parent: AstPtr::new(&Either::Right(
+                                                    record_pat,
+                                                )),
+                                                field_list_parent_path,
+                                                missed_fields,
+                                            }
+                                            .into(),
+                                        )
                                     }
                                 }
                             }
@@ -2948,10 +2951,10 @@ impl Local {
             .map(|&definition| {
                 let src = source_map.pat_syntax(definition).unwrap(); // Hmm...
                 let root = src.file_syntax(db.upcast());
-                src.map(|ast| match ast {
-                    // Suspicious unwrap
-                    Either::Left(it) => Either::Left(it.cast().unwrap().to_node(&root)),
-                    Either::Right(it) => Either::Right(it.to_node(&root)),
+                src.map(|ast| match ast.to_node(&root) {
+                    Either::Left(ast::Pat::IdentPat(it)) => Either::Left(it),
+                    Either::Left(_) => unreachable!("local with non ident-pattern"),
+                    Either::Right(it) => Either::Right(it),
                 })
             })
             .map(move |source| LocalSource { local: self, source })
diff --git a/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
index ede9858c726..06ba13bcc55 100644
--- a/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
+++ b/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
@@ -23,12 +23,7 @@ pub(crate) fn mismatched_tuple_struct_pat_arg_count(
     Diagnostic::new(
         DiagnosticCode::RustcHardError("E0023"),
         message,
-        invalid_args_range(
-            ctx,
-            d.expr_or_pat.clone().map(|it| it.either(Into::into, Into::into)),
-            d.expected,
-            d.found,
-        ),
+        invalid_args_range(ctx, d.expr_or_pat.clone().map(Into::into), d.expected, d.found),
     )
 }
 
diff --git a/crates/ide-diagnostics/src/handlers/missing_fields.rs b/crates/ide-diagnostics/src/handlers/missing_fields.rs
index 3178c7fa2bc..c09be3fee7e 100644
--- a/crates/ide-diagnostics/src/handlers/missing_fields.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_fields.rs
@@ -39,7 +39,7 @@ pub(crate) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingField
         d.field_list_parent_path
             .clone()
             .map(SyntaxNodePtr::from)
-            .unwrap_or_else(|| d.field_list_parent.clone().either(|it| it.into(), |it| it.into())),
+            .unwrap_or_else(|| d.field_list_parent.clone().into()),
     );
 
     Diagnostic::new_with_syntax_node_ptr(ctx, DiagnosticCode::RustcHardError("E0063"), message, ptr)
@@ -58,10 +58,8 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
 
     let root = ctx.sema.db.parse_or_expand(d.file);
 
-    let current_module = match &d.field_list_parent {
-        Either::Left(ptr) => ctx.sema.scope(ptr.to_node(&root).syntax()).map(|it| it.module()),
-        Either::Right(ptr) => ctx.sema.scope(ptr.to_node(&root).syntax()).map(|it| it.module()),
-    };
+    let current_module =
+        ctx.sema.scope(d.field_list_parent.to_node(&root).syntax()).map(|it| it.module());
 
     let build_text_edit = |parent_syntax, new_syntax: &SyntaxNode, old_syntax| {
         let edit = {
@@ -87,9 +85,8 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
         )])
     };
 
-    match &d.field_list_parent {
-        Either::Left(record_expr) => {
-            let field_list_parent = record_expr.to_node(&root);
+    match &d.field_list_parent.to_node(&root) {
+        Either::Left(field_list_parent) => {
             let missing_fields = ctx.sema.record_literal_missing_fields(&field_list_parent);
 
             let mut locals = FxHashMap::default();
@@ -152,8 +149,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
                 old_field_list.syntax(),
             )
         }
-        Either::Right(record_pat) => {
-            let field_list_parent = record_pat.to_node(&root);
+        Either::Right(field_list_parent) => {
             let missing_fields = ctx.sema.record_pattern_missing_fields(&field_list_parent);
 
             let old_field_list = field_list_parent.record_pat_field_list()?;
diff --git a/crates/ide-diagnostics/src/handlers/no_such_field.rs b/crates/ide-diagnostics/src/handlers/no_such_field.rs
index 290c16c9d24..ee8a9c95793 100644
--- a/crates/ide-diagnostics/src/handlers/no_such_field.rs
+++ b/crates/ide-diagnostics/src/handlers/no_such_field.rs
@@ -13,7 +13,7 @@ use crate::{fix, Assist, Diagnostic, DiagnosticCode, DiagnosticsContext};
 //
 // This diagnostic is triggered if created structure does not have field provided in record.
 pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Diagnostic {
-    let node = d.field.clone().map(|it| it.either(Into::into, Into::into));
+    let node = d.field.clone().map(Into::into);
     if d.private {
         // FIXME: quickfix to add required visibility
         Diagnostic::new_with_syntax_node_ptr(
@@ -35,15 +35,13 @@ pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField)
 
 fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Option<Vec<Assist>> {
     // FIXME: quickfix for pattern
-    match &d.field.value {
-        Either::Left(ptr) => {
-            let root = ctx.sema.db.parse_or_expand(d.field.file_id);
-            missing_record_expr_field_fixes(
-                &ctx.sema,
-                d.field.file_id.original_file(ctx.sema.db),
-                &ptr.to_node(&root),
-            )
-        }
+    let root = ctx.sema.db.parse_or_expand(d.field.file_id);
+    match &d.field.value.to_node(&root) {
+        Either::Left(node) => missing_record_expr_field_fixes(
+            &ctx.sema,
+            d.field.file_id.original_file(ctx.sema.db),
+            node,
+        ),
         _ => None,
     }
 }
diff --git a/crates/ide-diagnostics/src/handlers/private_assoc_item.rs b/crates/ide-diagnostics/src/handlers/private_assoc_item.rs
index c44d28e77f6..a828b8b4fd2 100644
--- a/crates/ide-diagnostics/src/handlers/private_assoc_item.rs
+++ b/crates/ide-diagnostics/src/handlers/private_assoc_item.rs
@@ -1,5 +1,3 @@
-use either::Either;
-
 use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
 
 // Diagnostic: private-assoc-item
@@ -28,13 +26,7 @@ pub(crate) fn private_assoc_item(
             },
             name,
         ),
-        d.expr_or_pat.clone().map(|it| match it {
-            Either::Left(it) => it.into(),
-            Either::Right(it) => match it {
-                Either::Left(it) => it.into(),
-                Either::Right(it) => it.into(),
-            },
-        }),
+        d.expr_or_pat.clone().map(Into::into),
     )
 }
 
diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
index 1f400bb42dd..14454fe8dc4 100644
--- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs
+++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
@@ -1,4 +1,3 @@
-use either::Either;
 use hir::{db::ExpandDatabase, ClosureStyle, HirDisplay, InFile, Type};
 use ide_db::{famous_defs::FamousDefs, source_change::SourceChange};
 use syntax::{
@@ -14,9 +13,11 @@ use crate::{adjusted_display_range, fix, Assist, Diagnostic, DiagnosticCode, Dia
 // This diagnostic is triggered when the type of an expression or pattern does not match
 // the expected type.
 pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Diagnostic {
-    let display_range = match &d.expr_or_pat {
-        Either::Left(expr) => {
-            adjusted_display_range::<ast::Expr>(ctx, expr.clone().map(|it| it.into()), &|expr| {
+    let display_range = match &d.expr_or_pat.value {
+        expr if ast::Expr::can_cast(expr.kind()) => adjusted_display_range::<ast::Expr>(
+            ctx,
+            InFile { file_id: d.expr_or_pat.file_id, value: expr.syntax_node_ptr() },
+            &|expr| {
                 let salient_token_range = match expr {
                     ast::Expr::IfExpr(it) => it.if_token()?.text_range(),
                     ast::Expr::LoopExpr(it) => it.loop_token()?.text_range(),
@@ -32,10 +33,15 @@ pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch)
 
                 cov_mark::hit!(type_mismatch_range_adjustment);
                 Some(salient_token_range)
-            })
-        }
-        Either::Right(pat) => {
-            ctx.sema.diagnostics_display_range(pat.clone().map(|it| it.into())).range
+            },
+        ),
+        pat => {
+            ctx.sema
+                .diagnostics_display_range(InFile {
+                    file_id: d.expr_or_pat.file_id,
+                    value: pat.syntax_node_ptr(),
+                })
+                .range
         }
     };
     let mut diag = Diagnostic::new(
@@ -57,14 +63,12 @@ pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch)
 fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Option<Vec<Assist>> {
     let mut fixes = Vec::new();
 
-    match &d.expr_or_pat {
-        Either::Left(expr_ptr) => {
-            add_reference(ctx, d, expr_ptr, &mut fixes);
-            add_missing_ok_or_some(ctx, d, expr_ptr, &mut fixes);
-            remove_semicolon(ctx, d, expr_ptr, &mut fixes);
-            str_ref_to_owned(ctx, d, expr_ptr, &mut fixes);
-        }
-        Either::Right(_pat_ptr) => {}
+    if let Some(expr_ptr) = d.expr_or_pat.value.clone().cast::<ast::Expr>() {
+        let expr_ptr = &InFile { file_id: d.expr_or_pat.file_id, value: expr_ptr.clone() };
+        add_reference(ctx, d, expr_ptr, &mut fixes);
+        add_missing_ok_or_some(ctx, d, expr_ptr, &mut fixes);
+        remove_semicolon(ctx, d, expr_ptr, &mut fixes);
+        str_ref_to_owned(ctx, d, expr_ptr, &mut fixes);
     }
 
     if fixes.is_empty() {
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index dcb3ca6581c..230ff5f9b86 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -846,9 +846,7 @@ fn location_csv_pat(db: &RootDatabase, vfs: &Vfs, sm: &BodySourceMap, pat_id: Pa
         Err(SyntheticSyntax) => return "synthetic,,".to_string(),
     };
     let root = db.parse_or_expand(src.file_id);
-    let node = src.map(|e| {
-        e.either(|it| it.to_node(&root).syntax().clone(), |it| it.to_node(&root).syntax().clone())
-    });
+    let node = src.map(|e| e.to_node(&root).syntax().clone());
     let original_range = node.as_ref().original_file_range(db);
     let path = vfs.file_path(original_range.file_id);
     let line_index = db.line_index(original_range.file_id);
@@ -888,12 +886,7 @@ fn pat_syntax_range(
     let src = sm.pat_syntax(pat_id);
     if let Ok(src) = src {
         let root = db.parse_or_expand(src.file_id);
-        let node = src.map(|e| {
-            e.either(
-                |it| it.to_node(&root).syntax().clone(),
-                |it| it.to_node(&root).syntax().clone(),
-            )
-        });
+        let node = src.map(|e| e.to_node(&root).syntax().clone());
         let original_range = node.as_ref().original_file_range(db);
         let path = vfs.file_path(original_range.file_id);
         let line_index = db.line_index(original_range.file_id);
diff --git a/crates/syntax/src/ptr.rs b/crates/syntax/src/ptr.rs
index 1d4a89201ae..71762996cd7 100644
--- a/crates/syntax/src/ptr.rs
+++ b/crates/syntax/src/ptr.rs
@@ -73,6 +73,10 @@ impl<N: AstNode> AstPtr<N> {
         Some(AstPtr { raw: self.raw, _ty: PhantomData })
     }
 
+    pub fn kind(&self) -> parser::SyntaxKind {
+        self.raw.kind()
+    }
+
     pub fn upcast<M: AstNode>(self) -> AstPtr<M>
     where
         N: Into<M>,
@@ -84,6 +88,20 @@ impl<N: AstNode> AstPtr<N> {
     pub fn try_from_raw(raw: SyntaxNodePtr) -> Option<AstPtr<N>> {
         N::can_cast(raw.kind()).then_some(AstPtr { raw, _ty: PhantomData })
     }
+
+    pub fn wrap_left<R>(self) -> AstPtr<either::Either<N, R>>
+    where
+        either::Either<N, R>: AstNode,
+    {
+        AstPtr { raw: self.raw, _ty: PhantomData }
+    }
+
+    pub fn wrap_right<L>(self) -> AstPtr<either::Either<L, N>>
+    where
+        either::Either<L, N>: AstNode,
+    {
+        AstPtr { raw: self.raw, _ty: PhantomData }
+    }
 }
 
 impl<N: AstNode> From<AstPtr<N>> for SyntaxNodePtr {