From 9fca85086309875a12f2892b106c9c095eef42a3 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Wed, 19 Apr 2023 16:03:23 +0200 Subject: [PATCH] Allow more precise span origin tracing --- crates/common2/src/diagnostics.rs | 26 ++++- crates/hir/src/hir_def/expr.rs | 8 ++ crates/hir/src/hir_def/pat.rs | 10 +- crates/hir/src/hir_def/stmt.rs | 10 +- crates/hir/src/lib.rs | 8 +- crates/hir/src/lower/parse.rs | 4 +- crates/hir/src/span/attr.rs | 15 +-- crates/hir/src/span/expr.rs | 20 ++-- crates/hir/src/span/item.rs | 12 +-- crates/hir/src/span/mod.rs | 8 -- crates/hir/src/span/pat.rs | 20 ++-- crates/hir/src/span/stmt.rs | 54 +++++++--- crates/hir/src/span/transition.rs | 170 +++++++++++++++++++++++------- 13 files changed, 257 insertions(+), 108 deletions(-) diff --git a/crates/common2/src/diagnostics.rs b/crates/common2/src/diagnostics.rs index bd9235b84d..3fed6dd8e0 100644 --- a/crates/common2/src/diagnostics.rs +++ b/crates/common2/src/diagnostics.rs @@ -52,11 +52,33 @@ pub struct SubDiagnostic { pub struct Span { pub file: InputFile, pub range: TextRange, + pub kind: SpanKind, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum SpanKind { + /// A node corresponding is originally written in the source code. + Original, + + /// A node corresponding to the span is generated by macro expansion. + Expanded, + + /// No span information was found. + /// This happens if analysis code tries to get a span for a node that is + /// generated in lowering phase. + /// + /// If span has this kind, it means there is a bug in the analysis code. + /// The reason not to panic is that LSP should continue working even if + /// there are bugs in the span generation(This also makes easier to identify + /// the cause of the bug) + /// + /// Range is always the first character of the file in this case. + NotFound, } impl Span { - pub fn new(file: InputFile, range: TextRange) -> Self { - Self { file, range } + pub fn new(file: InputFile, range: TextRange, kind: SpanKind) -> Self { + Self { file, range, kind } } } diff --git a/crates/hir/src/hir_def/expr.rs b/crates/hir/src/hir_def/expr.rs index 589b20edb3..0abc647779 100644 --- a/crates/hir/src/hir_def/expr.rs +++ b/crates/hir/src/hir_def/expr.rs @@ -1,5 +1,7 @@ use cranelift_entity::entity_impl; +use crate::span::expr::LazyExprSpan; + use super::{Body, GenericArgListId, IdentId, IntegerId, LitKind, Partial, PatId, PathId, StmtId}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -44,6 +46,12 @@ pub enum Expr { pub struct ExprId(u32); entity_impl!(ExprId); +impl ExprId { + pub fn lazy_span(self, body: Body) -> LazyExprSpan { + LazyExprSpan::new(self, body) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum FieldIndex { /// The field is indexed by its name. diff --git a/crates/hir/src/hir_def/pat.rs b/crates/hir/src/hir_def/pat.rs index 18c5669a51..3b55fbd3bf 100644 --- a/crates/hir/src/hir_def/pat.rs +++ b/crates/hir/src/hir_def/pat.rs @@ -1,6 +1,8 @@ use cranelift_entity::entity_impl; -use super::{IdentId, LitKind, Partial, PathId}; +use crate::span::pat::LazyPatSpan; + +use super::{Body, IdentId, LitKind, Partial, PathId}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Pat { @@ -18,6 +20,12 @@ pub enum Pat { pub struct PatId(u32); entity_impl!(PatId); +impl PatId { + pub fn lazy_span(self, body: Body) -> LazyPatSpan { + LazyPatSpan::new(self, body) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct RecordPatField { pub label: Partial, diff --git a/crates/hir/src/hir_def/stmt.rs b/crates/hir/src/hir_def/stmt.rs index e96cd18f92..707d2bdf3e 100644 --- a/crates/hir/src/hir_def/stmt.rs +++ b/crates/hir/src/hir_def/stmt.rs @@ -1,6 +1,8 @@ use cranelift_entity::entity_impl; -use super::{ExprId, PatId, TypeId}; +use crate::span::stmt::LazyStmtSpan; + +use super::{Body, ExprId, PatId, TypeId}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Stmt { @@ -31,3 +33,9 @@ pub enum Stmt { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct StmtId(u32); entity_impl!(StmtId); + +impl StmtId { + pub fn lazy_span(self, body: Body) -> LazyStmtSpan { + LazyStmtSpan::new(self, body) + } +} diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index f1ff6858a5..9e87255b19 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -133,16 +133,12 @@ mod test_db { /// Parses the given source text and returns the first inner item in the /// file. - pub fn parse_source_to_first_item(&mut self, text: &str) -> T + pub fn expect_item(&mut self, text: &str) -> T where ItemKind: TryInto, { let tree = self.parse_source(text); - tree.children(tree.top_mod) - .next() - .unwrap() - .try_into() - .unwrap() + tree.dfs().find_map(|it| it.try_into().ok()).unwrap() } pub fn text_at(&self, top_mod: TopLevelMod, span: &impl LazySpan) -> &str { diff --git a/crates/hir/src/lower/parse.rs b/crates/hir/src/lower/parse.rs index 29f60cb7f5..93b43bb68a 100644 --- a/crates/hir/src/lower/parse.rs +++ b/crates/hir/src/lower/parse.rs @@ -1,5 +1,5 @@ use common::{ - diagnostics::{AnalysisPass, CompleteDiagnostic, GlobalErrorCode, Severity, Span}, + diagnostics::{AnalysisPass, CompleteDiagnostic, GlobalErrorCode, Severity, Span, SpanKind}, InputFile, }; use parser::GreenNode; @@ -38,7 +38,7 @@ impl DiagnosticVoucher for ParseDiagnostic { fn to_complete(self, _db: &dyn SpannedHirDb) -> CompleteDiagnostic { let error_code = self.error_code(); - let span = Span::new(self.file, self.error.range); + let span = Span::new(self.file, self.error.range, SpanKind::Original); CompleteDiagnostic::new(Severity::Error, self.error.msg, span, vec![], error_code) } } diff --git a/crates/hir/src/span/attr.rs b/crates/hir/src/span/attr.rs index dc0ec2e4a6..6e78feeb4b 100644 --- a/crates/hir/src/span/attr.rs +++ b/crates/hir/src/span/attr.rs @@ -1,5 +1,7 @@ use parser::ast::{self, prelude::*}; +use crate::span::transition::ResolvedOrigin; + use super::define_lazy_span_node; define_lazy_span_node!( @@ -11,17 +13,16 @@ define_lazy_span_node!( ); impl LazyAttrListSpan { pub fn normal_attr(&self, idx: usize) -> LazyNormalAttrSpan { - fn f( - node: parser::SyntaxNode, - arg: crate::span::transition::LazyArg, - ) -> Option { + fn f(origin: ResolvedOrigin, arg: crate::span::transition::LazyArg) -> ResolvedOrigin { let idx = match arg { crate::span::transition::LazyArg::Idx(idx) => idx, _ => unreachable!(), }; - ast::AttrList::cast(node) - .and_then(|f| f.normal_attrs().nth(idx)) - .map(|n| n.syntax().clone().into()) + origin.map(|node| { + ast::AttrList::cast(node) + .and_then(|f| f.normal_attrs().nth(idx)) + .map(|n| n.syntax().clone().into()) + }) } let lazy_transition = crate::span::transition::LazyTransitionFn { diff --git a/crates/hir/src/span/expr.rs b/crates/hir/src/span/expr.rs index e4e3f98ba8..979e1c8ed2 100644 --- a/crates/hir/src/span/expr.rs +++ b/crates/hir/src/span/expr.rs @@ -1,5 +1,4 @@ -use common::InputFile; -use parser::{ast, SyntaxNode}; +use parser::ast; use crate::{ hir_def::{Body, ExprId}, @@ -8,8 +7,8 @@ use crate::{ }; use super::{ - body_ast, body_source_map, define_lazy_span_node, - transition::{ChainInitiator, SpanTransitionChain}, + body_source_map, define_lazy_span_node, + transition::{ChainInitiator, ResolvedOrigin, SpanTransitionChain}, }; define_lazy_span_node!(LazyExprSpan, ast::Expr,); @@ -169,15 +168,10 @@ pub(crate) struct ExprRoot { } impl ChainInitiator for ExprRoot { - fn init(&self, db: &dyn SpannedHirDb) -> (InputFile, SyntaxNode) { + fn init(&self, db: &dyn SpannedHirDb) -> ResolvedOrigin { let source_map = body_source_map(db, self.body); - let expr_source = source_map.expr_map.node_to_source(self.expr); - let ptr = expr_source - .syntax_ptr() - .unwrap_or_else(|| body_ast(db, self.body).syntax_ptr().unwrap()); - - let (file, root_node) = self.body.top_mod(db.upcast()).init(db); - let node = ptr.to_node(&root_node); - (file, node) + let origin = source_map.expr_map.node_to_source(self.expr); + let top_mod = self.body.top_mod(db.upcast()); + ResolvedOrigin::resolve(db, top_mod, origin) } } diff --git a/crates/hir/src/span/item.rs b/crates/hir/src/span/item.rs index 60e82fc1b7..3e9acf9dd4 100644 --- a/crates/hir/src/span/item.rs +++ b/crates/hir/src/span/item.rs @@ -275,7 +275,7 @@ mod tests { } "#; - let mod_ = db.parse_source_to_first_item::(text); + let mod_ = db.expect_item::(text); let top_mod = mod_.top_mod(db.upcast()); let mod_span = mod_.lazy_span(); assert_eq!( @@ -296,7 +296,7 @@ mod tests { where U: Add "#; - let fn_ = db.parse_source_to_first_item::(text); + let fn_ = db.expect_item::(text); let top_mod = fn_.top_mod(db.upcast()); let fn_span = fn_.lazy_span(); assert_eq!("my_func", db.text_at(top_mod, &fn_span.name())); @@ -347,7 +347,7 @@ mod tests { pub y: foo::Bar<2> }"#; - let struct_ = db.parse_source_to_first_item::(text); + let struct_ = db.expect_item::(text); let top_mod = struct_.top_mod(db.upcast()); let struct_span = struct_.lazy_span(); assert_eq!("Foo", db.text_at(top_mod, &struct_span.name())); @@ -374,7 +374,7 @@ mod tests { Baz(u32, i32) }"#; - let enum_ = db.parse_source_to_first_item::(text); + let enum_ = db.expect_item::(text); let top_mod = enum_.top_mod(db.upcast()); let enum_span = enum_.lazy_span(); assert_eq!("Foo", db.text_at(top_mod, &enum_span.name())); @@ -396,7 +396,7 @@ mod tests { pub type Foo = u32 "#; - let type_alias = db.parse_source_to_first_item::(text); + let type_alias = db.expect_item::(text); let top_mod = type_alias.top_mod(db.upcast()); let type_alias_span = type_alias.lazy_span(); assert_eq!("Foo", db.text_at(top_mod, &type_alias_span.alias())); @@ -412,7 +412,7 @@ mod tests { use foo::bar::{baz::*, qux as Alias} "#; - let use_ = db.parse_source_to_first_item::(text); + let use_ = db.expect_item::(text); let top_mod = use_.top_mod(db.upcast()); let use_tree = use_.lazy_span().use_tree(); diff --git a/crates/hir/src/span/mod.rs b/crates/hir/src/span/mod.rs index 43fe74112d..a7d2c30aff 100644 --- a/crates/hir/src/span/mod.rs +++ b/crates/hir/src/span/mod.rs @@ -128,14 +128,6 @@ where Self::Raw(AstPtr::new(ast)) } - fn syntax_ptr(&self) -> Option { - match self { - HirOrigin::Raw(ptr) => Some(ptr.syntax_node_ptr()), - HirOrigin::Expanded(ptr) => Some(ptr.clone()), - _ => None, - } - } - pub(crate) fn desugared(origin: impl Into) -> Self { Self::Desugared(origin.into()) } diff --git a/crates/hir/src/span/pat.rs b/crates/hir/src/span/pat.rs index b613032486..61b559bd21 100644 --- a/crates/hir/src/span/pat.rs +++ b/crates/hir/src/span/pat.rs @@ -1,5 +1,4 @@ -use common::InputFile; -use parser::{ast, SyntaxNode}; +use parser::ast; use crate::{ hir_def::{Body, PatId}, @@ -8,8 +7,8 @@ use crate::{ }; use super::{ - body_ast, body_source_map, define_lazy_span_node, - transition::{ChainInitiator, SpanTransitionChain}, + body_source_map, define_lazy_span_node, + transition::{ChainInitiator, ResolvedOrigin, SpanTransitionChain}, }; define_lazy_span_node!(LazyPatSpan, ast::Pat,); @@ -80,15 +79,10 @@ pub(crate) struct PatRoot { } impl ChainInitiator for PatRoot { - fn init(&self, db: &dyn SpannedHirDb) -> (InputFile, SyntaxNode) { + fn init(&self, db: &dyn SpannedHirDb) -> ResolvedOrigin { let source_map = body_source_map(db, self.body); - let pat_source = source_map.pat_map.node_to_source(self.pat); - let ptr = pat_source - .syntax_ptr() - .unwrap_or_else(|| body_ast(db, self.body).syntax_ptr().unwrap()); - - let (file, root_node) = self.body.top_mod(db.upcast()).init(db); - let node = ptr.to_node(&root_node); - (file, node) + let origin = source_map.pat_map.node_to_source(self.pat); + let top_mod = self.body.top_mod(db.upcast()); + ResolvedOrigin::resolve(db, top_mod, origin) } } diff --git a/crates/hir/src/span/stmt.rs b/crates/hir/src/span/stmt.rs index 23956cf108..9f7570f8ba 100644 --- a/crates/hir/src/span/stmt.rs +++ b/crates/hir/src/span/stmt.rs @@ -1,5 +1,4 @@ -use common::InputFile; -use parser::{ast, SyntaxNode}; +use parser::ast; use crate::{ hir_def::{Body, StmtId}, @@ -8,8 +7,8 @@ use crate::{ }; use super::{ - body_ast, body_source_map, define_lazy_span_node, - transition::{ChainInitiator, SpanTransitionChain}, + body_source_map, define_lazy_span_node, + transition::{ChainInitiator, ResolvedOrigin, SpanTransitionChain}, }; define_lazy_span_node!(LazyStmtSpan, ast::Stmt,); @@ -39,15 +38,44 @@ pub(crate) struct StmtRoot { } impl ChainInitiator for StmtRoot { - fn init(&self, db: &dyn SpannedHirDb) -> (InputFile, SyntaxNode) { + fn init(&self, db: &dyn SpannedHirDb) -> ResolvedOrigin { let source_map = body_source_map(db, self.body); - let stmt_source = source_map.stmt_map.node_to_source(self.stmt); - let ptr = stmt_source - .syntax_ptr() - .unwrap_or_else(|| body_ast(db, self.body).syntax_ptr().unwrap()); - - let (file, root_node) = self.body.top_mod(db.upcast()).init(db); - let node = ptr.to_node(&root_node); - (file, node) + let origin = source_map.stmt_map.node_to_source(self.stmt); + let top_mod = self.body.top_mod(db.upcast()); + ResolvedOrigin::resolve(db, top_mod, origin) + } +} + +#[cfg(test)] +mod tests { + use crate::{hir_def::Body, test_db::TestDb}; + use common::Upcast; + + #[test] + fn aug_assign() { + let mut db = TestDb::default(); + + let text = r#" { + fn foo() { + let mut x = 0 + x += 1 + } + }"#; + + let body: Body = db.expect_item::(text); + let top_mod = body.top_mod(db.upcast()); + for (i, stmt) in body.stmts(db.upcast()).keys().enumerate() { + match i { + 0 => { + let span = stmt.lazy_span(body); + assert_eq!("let mut x = 0", db.text_at(top_mod, &span)); + } + 1 => { + let span = stmt.lazy_span(body); + assert_eq!("x += 1", db.text_at(top_mod, &span)); + } + _ => unreachable!(), + } + } } } diff --git a/crates/hir/src/span/transition.rs b/crates/hir/src/span/transition.rs index 6f6c6b7496..05731c2e75 100644 --- a/crates/hir/src/span/transition.rs +++ b/crates/hir/src/span/transition.rs @@ -1,23 +1,29 @@ -use common::{diagnostics::Span, InputFile}; -use parser::{ast::prelude::*, syntax_node::NodeOrToken, SyntaxNode}; +use common::{ + diagnostics::{Span, SpanKind}, + InputFile, +}; +use parser::{ + ast::prelude::*, syntax_node::NodeOrToken, FeLang, SyntaxNode, SyntaxToken, TextRange, +}; use crate::{ hir_def::{ Body, Const, Contract, Enum, ExternFunc, Func, Impl, ImplTrait, Mod, Struct, TopLevelMod, Trait, TypeAlias, Use, }, - lower::top_mod_ast, + lower::{map_file_to_mod_impl, top_mod_ast}, + SpannedHirDb, }; use super::{ body_ast, const_ast, contract_ast, enum_ast, expr::ExprRoot, extern_func_ast, func_ast, impl_ast, impl_trait_ast, mod_ast, pat::PatRoot, stmt::StmtRoot, struct_ast, trait_ast, - type_alias_ast, use_ast, LazySpan, + type_alias_ast, use_ast, AugAssignDesugared, DesugaredOrigin, HirOrigin, LazySpan, }; #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] pub(crate) struct LazyTransitionFn { - pub(super) f: fn(SyntaxNode, LazyArg) -> Option, + pub(super) f: fn(ResolvedOrigin, LazyArg) -> ResolvedOrigin, pub(super) arg: LazyArg, } @@ -54,8 +60,66 @@ pub(crate) enum ChainRoot { Pat(PatRoot), } +pub(crate) struct ResolvedOrigin { + pub(crate) file: InputFile, + pub(crate) kind: ResolvedOriginKind, +} +impl ResolvedOrigin { + pub(crate) fn new(file: InputFile, kind: ResolvedOriginKind) -> Self { + Self { file, kind } + } + + pub(crate) fn resolve( + db: &dyn SpannedHirDb, + top_mod: TopLevelMod, + origin: &HirOrigin, + ) -> ResolvedOrigin + where + T: AstNode, + { + let root = top_mod_ast(db.upcast(), top_mod).syntax().clone(); + let kind = match origin { + HirOrigin::Raw(ptr) => ResolvedOriginKind::Node(ptr.syntax_node_ptr().to_node(&root)), + HirOrigin::Expanded(ptr) => ResolvedOriginKind::Expanded(ptr.to_node(&root)), + HirOrigin::Desugared(desugared) => ResolvedOriginKind::Desugared(desugared.clone()), + HirOrigin::None => ResolvedOriginKind::None, + }; + + ResolvedOrigin::new(top_mod.file(db.upcast()), kind) + } +} + +pub(crate) enum ResolvedOriginKind { + Node(SyntaxNode), + Token(SyntaxToken), + Expanded(SyntaxNode), + Desugared(DesugaredOrigin), + None, +} + +impl ResolvedOrigin { + pub(crate) fn map(self, f: F) -> Self + where + F: FnOnce(SyntaxNode) -> Option, + { + let kind = match self.kind { + ResolvedOriginKind::Node(node) => match f(node) { + Some(NodeOrToken::Node(node)) => ResolvedOriginKind::Node(node), + Some(NodeOrToken::Token(token)) => ResolvedOriginKind::Token(token), + None => ResolvedOriginKind::None, + }, + kind => kind, + }; + + ResolvedOrigin { + file: self.file, + kind, + } + } +} + impl ChainInitiator for ChainRoot { - fn init(&self, db: &dyn crate::SpannedHirDb) -> (InputFile, SyntaxNode) { + fn init(&self, db: &dyn crate::SpannedHirDb) -> ResolvedOrigin { match self { Self::TopMod(top_mod) => top_mod.init(db), Self::Mod(mod_) => mod_.init(db), @@ -95,33 +159,43 @@ impl SpanTransitionChain { impl LazySpan for SpanTransitionChain { fn resolve(&self, db: &dyn crate::SpannedHirDb) -> Span { - let (file, mut node) = self.root.init(db); + let mut resolved = self.root.init(db); for LazyTransitionFn { f, arg } in &self.chain { - node = match f(node.clone(), *arg) { - Some(NodeOrToken::Node(node)) => node, - Some(NodeOrToken::Token(token)) => { - return Span::new(file, token.text_range()); - } - None => { - break; - } - }; + resolved = f(resolved, *arg); } - Span::new(file, node.text_range()) + match resolved.kind { + ResolvedOriginKind::Node(node) => { + Span::new(resolved.file, node.text_range(), SpanKind::Original) + } + ResolvedOriginKind::Token(token) => { + Span::new(resolved.file, token.text_range(), SpanKind::Original) + } + ResolvedOriginKind::Expanded(node) => { + Span::new(resolved.file, node.text_range(), SpanKind::Expanded) + } + ResolvedOriginKind::Desugared(desugared) => desugared.resolve(db, resolved.file), + ResolvedOriginKind::None => Span::new( + resolved.file, + TextRange::new(0.into(), 0.into()), + SpanKind::NotFound, + ), + } } } -pub trait ChainInitiator { - fn init(&self, db: &dyn crate::SpannedHirDb) -> (InputFile, SyntaxNode); +/// A trait for types that can be used as the root of a `SpanTransitionChain`. +pub(crate) trait ChainInitiator { + /// Returns the `ResolvedOrigin` for the root of the chain. + fn init(&self, db: &dyn crate::SpannedHirDb) -> ResolvedOrigin; } impl ChainInitiator for TopLevelMod { - fn init(&self, db: &dyn crate::SpannedHirDb) -> (InputFile, SyntaxNode) { + fn init(&self, db: &dyn crate::SpannedHirDb) -> ResolvedOrigin { let file = self.file(db.upcast()); let ast = top_mod_ast(db.upcast(), *self); - (file, ast.syntax().clone()) + ResolvedOrigin::new(file, ResolvedOriginKind::Node(ast.syntax().clone())) } } @@ -129,12 +203,10 @@ macro_rules! impl_chain_root { ($(($ty:ty, $fn:ident),)*) => { $( impl ChainInitiator for $ty { - fn init(&self, db: &dyn crate::SpannedHirDb) -> (InputFile, SyntaxNode) { - let ast = $fn(db, *self); - let (file, root) = self.top_mod(db.upcast()).init(db); - let ptr = ast.syntax_ptr().unwrap(); - let node = ptr.to_node(&root); - (file, node) + fn init(&self, db: &dyn crate::SpannedHirDb) -> ResolvedOrigin { + let top_mod = self.top_mod(db.upcast()); + let origin = $fn(db, *self); + ResolvedOrigin::resolve(db, top_mod, origin) } })* }; @@ -183,10 +255,10 @@ macro_rules! define_lazy_span_node { $($( pub fn $name_token(&self) -> crate::span::LazySpanAtom { use parser::ast::prelude::*; - fn f(node: parser::SyntaxNode, _: crate::span::transition::LazyArg) -> Option { - <$sk_node as AstNode>::cast(node) + fn f(origin: crate::span::transition::ResolvedOrigin, _: crate::span::transition::LazyArg) -> crate::span::transition::ResolvedOrigin { + origin.map(|node| <$sk_node as AstNode>::cast(node) .and_then(|n| n.$getter_token()) - .map(|n| n.into()) + .map(|n| n.into())) } let lazy_transition = crate::span::transition::LazyTransitionFn { @@ -203,10 +275,10 @@ macro_rules! define_lazy_span_node { pub fn $name_node(&self) -> $result { use parser::ast::prelude::*; - fn f(node: parser::SyntaxNode, _: crate::span::transition::LazyArg) -> Option { - <$sk_node as AstNode>::cast(node) + fn f(origin: crate::span::transition::ResolvedOrigin, _: crate::span::transition::LazyArg) -> crate::span::transition::ResolvedOrigin { + origin.map(|node| <$sk_node as AstNode>::cast(node) .and_then(|n| n.$getter_node()) - .map(|n| n.syntax().clone().into()) + .map(|n| n.syntax().clone().into())) } let lazy_transition = crate::span::transition::LazyTransitionFn { @@ -221,15 +293,15 @@ macro_rules! define_lazy_span_node { pub fn $name_iter(&self, idx: usize) -> $result_iter { use parser::ast::prelude::*; - fn f(node: parser::SyntaxNode, arg: crate::span::transition::LazyArg) -> Option { + fn f(origin: crate::span::transition::ResolvedOrigin, arg: crate::span::transition::LazyArg) -> crate::span::transition::ResolvedOrigin { let idx = match arg { crate::span::transition::LazyArg::Idx(idx) => idx, _ => unreachable!(), }; - <$sk_node as AstNode>::cast(node) + origin.map(|node| <$sk_node as AstNode>::cast(node) .and_then(|f| f.into_iter().nth(idx)) - .map(|n| n.syntax().clone().into()) + .map(|n| n.syntax().clone().into())) } let lazy_transition = crate::span::transition::LazyTransitionFn { @@ -251,4 +323,30 @@ macro_rules! define_lazy_span_node { }; } +impl DesugaredOrigin { + fn resolve(self, db: &dyn SpannedHirDb, file: InputFile) -> Span { + let range = match self { + Self::AugAssign(AugAssignDesugared::Stmt(ptr)) => { + let top_mod = map_file_to_mod_impl(db.upcast(), file); + let top_mod_ast = top_mod_ast(db.upcast(), top_mod); + ptr.syntax_node_ptr() + .to_node(top_mod_ast.syntax()) + .text_range() + } + + Self::AugAssign(AugAssignDesugared::Lhs(range)) => range, + + Self::AugAssign(AugAssignDesugared::Rhs(ptr)) => { + let top_mod = map_file_to_mod_impl(db.upcast(), file); + let top_mod_ast = top_mod_ast(db.upcast(), top_mod); + ptr.syntax_node_ptr() + .to_node(top_mod_ast.syntax()) + .text_range() + } + }; + + Span::new(file, range, SpanKind::Original) + } +} + pub(super) use define_lazy_span_node;