diff --git a/compiler/frontend/src/cst/id.rs b/compiler/frontend/src/cst/id.rs index 045de4e92..884f0163d 100644 --- a/compiler/frontend/src/cst/id.rs +++ b/compiler/frontend/src/cst/id.rs @@ -1,13 +1,28 @@ -use crate::impl_countable_id; -use std::fmt::{self, Display, Formatter}; +use crate::{ + impl_countable_id, + rich_ir::{RichIrBuilder, ToRichIr, TokenType}, +}; +use enumset::EnumSet; +use std::fmt::{self, Debug, Display, Formatter}; -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Id(pub usize); impl_countable_id!(Id); +impl Debug for Id { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "${}", self.0) + } +} impl Display for Id { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "CstId({})", self.0) + write!(f, "${}", self.0) + } +} +impl ToRichIr for Id { + fn build_rich_ir(&self, builder: &mut RichIrBuilder) { + let range = builder.push(self.to_string(), TokenType::Variable, EnumSet::empty()); + builder.push_reference(*self, range); } } diff --git a/compiler/frontend/src/cst/kind.rs b/compiler/frontend/src/cst/kind.rs index dd9e566cd..a4a2f3d41 100644 --- a/compiler/frontend/src/cst/kind.rs +++ b/compiler/frontend/src/cst/kind.rs @@ -1,5 +1,7 @@ use super::{Cst, CstData, CstError}; -use num_bigint::BigUint; +use crate::rich_ir::{RichIrBuilder, ToRichIr, TokenType}; +use enumset::EnumSet; +use num_bigint::{BigInt, BigUint}; use std::fmt::{self, Display, Formatter}; use strum_macros::EnumIs; @@ -551,3 +553,451 @@ impl Display for CstKind { } } } + +impl ToRichIr for CstKind +where + Cst: ToRichIr, +{ + fn build_rich_ir(&self, builder: &mut RichIrBuilder) { + match self { + Self::EqualsSign => { + builder.push_simple("EqualsSign"); + } + Self::Comma => { + builder.push_simple("Comma"); + } + Self::Dot => { + builder.push_simple("Dot"); + } + Self::Colon => { + builder.push_simple("Colon"); + } + Self::ColonEqualsSign => { + builder.push_simple("ColonEqualsSign"); + } + Self::Bar => { + builder.push_simple("Bar"); + } + Self::OpeningParenthesis => { + builder.push_simple("OpeningParenthesis"); + } + Self::ClosingParenthesis => { + builder.push_simple("ClosingParenthesis"); + } + Self::OpeningBracket => { + builder.push_simple("OpeningBracket"); + } + Self::ClosingBracket => { + builder.push_simple("ClosingBracket"); + } + Self::OpeningCurlyBrace => { + builder.push_simple("OpeningCurlyBrace"); + } + Self::ClosingCurlyBrace => { + builder.push_simple("ClosingCurlyBrace"); + } + Self::Arrow => { + builder.push_simple("Arrow"); + } + Self::SingleQuote => { + builder.push_simple("SingleQuote"); + } + Self::DoubleQuote => { + builder.push_simple("DoubleQuote"); + } + Self::Percent => { + builder.push_simple("Percent"); + } + Self::Octothorpe => { + builder.push_simple("Octothorpe"); + } + Self::Whitespace(whitespace) => { + builder.push_simple(format!("Whitespace \"{whitespace}\"")); + } + Self::Newline(newline) => { + builder.push_simple(format!( + "Newline \"{}\"", + newline.replace('\n', "\\n").replace('\r', "\\r") + )); + } + Self::Comment { + octothorpe, + comment, + } => { + builder.push_cst_kind("Comment", |builder| { + builder.push_cst_kind_property("octothorpe", octothorpe); + builder.push_cst_kind_property("comment", format!("\"{comment}\"")); + }); + } + Self::TrailingWhitespace { child, whitespace } => { + builder.push_cst_kind("TrailingWhitespace", |builder| { + builder.push_cst_kind_property("child", child); + + builder.push_cst_kind_property_name("whitespace"); + builder.push_indented_foldable(|builder| { + for whitespace in whitespace { + builder.push_newline(); + whitespace.build_rich_ir(builder); + } + }); + }); + } + Self::Identifier(identifier) => { + builder.push_simple(format!("Identifier \"{identifier}\"")); + } + Self::Symbol(symbol) => { + builder.push_simple(format!("Symbol \"{symbol}\"")); + } + Self::Int { + radix_prefix, + value, + string, + } => { + let start = builder.push_simple("Int:").start; + builder.push_indented_foldable(|builder| { + builder.push_cst_kind_property_name("radix_prefix"); + if let Some((radix, prefix)) = radix_prefix { + builder.push_indented_foldable(|builder| { + builder.push_cst_kind_property("radix", format!("{radix:?}")); + builder.push_cst_kind_property("prefix", format!("\"{prefix}\"")); + }); + } else { + builder.push_simple("None"); + } + + builder.push_cst_kind_property_name("value"); + builder.push(value.to_string(), TokenType::Int, EnumSet::new()); + + builder.push_cst_kind_property("string", format!("\"{string}\"")); + }); + builder + .push_reference(BigInt::from(value.clone()), start..builder.current_offset()); + } + Self::OpeningText { + opening_single_quotes, + opening_double_quote, + } => { + builder.push_cst_kind("OpeningText", |builder| { + builder.push_cst_kind_property_name("opening_single_quotes"); + builder.push_indented_foldable(|builder| { + for opening_single_quote in opening_single_quotes { + builder.push_newline(); + opening_single_quote.build_rich_ir(builder); + } + }); + + builder.push_cst_kind_property("opening_double_quote", opening_double_quote); + }); + } + Self::ClosingText { + closing_double_quote, + closing_single_quotes, + } => { + builder.push_cst_kind("ClosingText", |builder| { + builder.push_cst_kind_property("closing_double_quote", closing_double_quote); + + builder.push_cst_kind_property_name("closing_single_quotes"); + builder.push_indented_foldable(|builder| { + for closing_single_quote in closing_single_quotes { + builder.push_newline(); + closing_single_quote.build_rich_ir(builder); + } + }); + }); + } + Self::Text { + opening, + parts, + closing, + } => { + builder.push_cst_kind("Text", |builder| { + builder.push_cst_kind_property("opening", opening); + + builder.push_cst_kind_property_name("parts"); + builder.push_indented_foldable(|builder| { + for part in parts { + builder.push_newline(); + part.build_rich_ir(builder); + } + }); + + builder.push_cst_kind_property("closing", closing); + }); + } + Self::TextNewline(newline) => { + builder.push_simple(format!( + "TextNewline \"{}\"", + newline.replace('\n', "\\n").replace('\r', "\\r") + )); + } + Self::TextPart(literal) => { + let start = builder.push_simple("TextPart \"").start; + builder.push(literal, TokenType::Text, EnumSet::new()); + let end = builder.push_simple("\"").end; + builder.push_reference(literal.to_string(), start..end); + } + Self::TextInterpolation { + opening_curly_braces, + expression, + closing_curly_braces, + } => { + builder.push_cst_kind("TextInterpolation", |builder| { + builder.push_cst_kind_property_name("opening_curly_braces"); + builder.push_indented_foldable(|builder| { + for opening_curly_brace in opening_curly_braces { + builder.push_newline(); + opening_curly_brace.build_rich_ir(builder); + } + }); + + builder.push_cst_kind_property("expression", expression); + + builder.push_cst_kind_property_name("closing_curly_braces"); + builder.push_indented_foldable(|builder| { + for closing_curly_brace in closing_curly_braces { + builder.push_newline(); + closing_curly_brace.build_rich_ir(builder); + } + }); + }); + } + Self::BinaryBar { left, bar, right } => { + builder.push_cst_kind("BinaryBar", |builder| { + builder.push_cst_kind_property("left", left); + + builder.push_cst_kind_property("bar", bar); + + builder.push_cst_kind_property("right", right); + }); + } + Self::Parenthesized { + opening_parenthesis, + inner, + closing_parenthesis, + } => { + builder.push_cst_kind("Parenthesized", |builder| { + builder.push_cst_kind_property("opening_parenthesis", opening_parenthesis); + + builder.push_cst_kind_property("inner", inner); + + builder.push_cst_kind_property("closing_parenthesis", closing_parenthesis); + }); + } + Self::Call { + receiver, + arguments, + } => { + builder.push_cst_kind("Call", |builder| { + builder.push_cst_kind_property("receiver", receiver); + + builder.push_cst_kind_property_name("arguments"); + builder.push_indented_foldable(|builder| { + for argument in arguments { + builder.push_newline(); + argument.build_rich_ir(builder); + } + }); + }); + } + Self::List { + opening_parenthesis, + items, + closing_parenthesis, + } => { + builder.push_cst_kind("List", |builder| { + builder.push_cst_kind_property("opening_parenthesis", opening_parenthesis); + + builder.push_cst_kind_property_name("items"); + builder.push_indented_foldable(|builder| { + for item in items { + builder.push_newline(); + item.build_rich_ir(builder); + } + }); + + builder.push_cst_kind_property("closing_parenthesis", closing_parenthesis); + }); + } + Self::ListItem { value, comma } => { + builder.push_cst_kind("ListItem", |builder| { + builder.push_cst_kind_property("value", value); + + builder.push_cst_kind_property_name("comma"); + if let Some(comma) = comma { + comma.build_rich_ir(builder); + } else { + builder.push_simple("None"); + } + }); + } + Self::Struct { + opening_bracket, + fields, + closing_bracket, + } => { + builder.push_cst_kind("Struct", |builder| { + builder.push_cst_kind_property("opening_bracket", opening_bracket); + + builder.push_cst_kind_property_name("fields"); + builder.push_indented_foldable(|builder| { + for field in fields { + builder.push_newline(); + field.build_rich_ir(builder); + } + }); + + builder.push_cst_kind_property("closing_bracket", closing_bracket); + }); + } + Self::StructField { + key_and_colon, + value, + comma, + } => { + builder.push_cst_kind("StructField", |builder| { + builder.push_cst_kind_property_name("key_and_colon"); + if let Some(box (key, colon)) = key_and_colon { + builder.push_cst_kind_property("key", key); + builder.push_cst_kind_property("colon", colon); + } else { + builder.push_simple("None"); + } + + builder.push_cst_kind_property("value", value); + + builder.push_cst_kind_property_name("comma"); + if let Some(comma) = comma { + comma.build_rich_ir(builder); + } else { + builder.push_simple("None"); + } + }); + } + Self::StructAccess { struct_, dot, key } => { + builder.push_cst_kind("StructAccess", |builder| { + builder.push_cst_kind_property("struct", struct_); + builder.push_cst_kind_property("dot", dot); + builder.push_cst_kind_property("key", key); + }); + } + Self::Match { + expression, + percent, + cases, + } => { + builder.push_cst_kind("Match", |builder| { + builder.push_cst_kind_property("expression", expression); + builder.push_cst_kind_property("percent", percent); + + builder.push_cst_kind_property_name("cases"); + builder.push_indented_foldable(|builder| { + for case in cases { + builder.push_newline(); + case.build_rich_ir(builder); + } + }); + }); + } + Self::MatchCase { + pattern, + arrow, + body, + } => { + builder.push_cst_kind("MatchCase", |builder| { + builder.push_cst_kind_property("pattern", pattern); + builder.push_cst_kind_property("arrow", arrow); + + builder.push_cst_kind_property_name("body"); + builder.push_indented_foldable(|builder| { + for expression in body { + builder.push_newline(); + expression.build_rich_ir(builder); + } + }); + }); + } + Self::Function { + opening_curly_brace, + parameters_and_arrow, + body, + closing_curly_brace, + } => { + builder.push_cst_kind("Function", |builder| { + builder.push_cst_kind_property("opening_curly_brace", opening_curly_brace); + + builder.push_cst_kind_property_name("parameters_and_arrow"); + if let Some((parameters, arrow)) = parameters_and_arrow { + builder.push_indented_foldable(|builder| { + builder.push_cst_kind_property_name("parameters"); + builder.push_indented_foldable(|builder| { + for parameter in parameters { + builder.push_newline(); + parameter.build_rich_ir(builder); + } + }); + + builder.push_cst_kind_property("arrow", arrow); + }); + } else { + builder.push_simple("None"); + } + + builder.push_cst_kind_property_name("body"); + builder.push_indented_foldable(|builder| { + for expression in body { + builder.push_newline(); + expression.build_rich_ir(builder); + } + }); + + builder.push_cst_kind_property("closing_curly_brace", closing_curly_brace); + }); + } + Self::Assignment { + left, + assignment_sign, + body, + } => { + builder.push_cst_kind("Assignment", |builder| { + builder.push_cst_kind_property("left", left); + builder.push_cst_kind_property("assignment_sign", assignment_sign); + + builder.push_cst_kind_property_name("body"); + builder.push_indented_foldable(|builder| { + for expression in body { + builder.push_newline(); + expression.build_rich_ir(builder); + } + }); + }); + } + Self::Error { + unparsable_input, + error, + } => { + builder.push_cst_kind("Error", |builder| { + builder.push_cst_kind_property( + "unparsable_input", + format!("\"{unparsable_input}\""), + ); + builder.push_cst_kind_property("error", format!("{error:?}")); + }); + } + } + } +} +impl RichIrBuilder { + fn push_cst_kind(&mut self, kind: &str, build_children: impl FnOnce(&mut Self)) { + self.push_simple(format!("{kind}:")); + self.push_indented_foldable(build_children); + } + fn push_cst_kind_property_name(&mut self, property_name: &str) { + self.push_newline(); + self.push_simple(format!("{property_name}: ")); + } + fn push_cst_kind_property(&mut self, property_name: &str, value: impl ToRichIr) { + self.push_newline(); + self.push_simple(format!("{property_name}:")); + value.build_rich_ir(self); + } +} diff --git a/compiler/frontend/src/cst/mod.rs b/compiler/frontend/src/cst/mod.rs index f89d296f7..276b62bd9 100644 --- a/compiler/frontend/src/cst/mod.rs +++ b/compiler/frontend/src/cst/mod.rs @@ -3,7 +3,12 @@ pub use self::{ error::CstError, id::Id, is_multiline::IsMultiline, kind::CstKind, kind::IntRadix, unwrap_whitespace_and_comment::UnwrapWhitespaceAndComment, }; -use crate::{module::Module, position::Offset, rcst_to_cst::RcstToCst}; +use crate::{ + module::Module, + position::Offset, + rcst_to_cst::RcstToCst, + rich_ir::{RichIrBuilder, ToRichIr}, +}; use derive_more::Deref; use std::{ fmt::{self, Display, Formatter}, @@ -50,6 +55,18 @@ impl Display for Cst { self.kind.fmt(f) } } +impl ToRichIr for Cst { + fn build_rich_ir(&self, builder: &mut RichIrBuilder) { + builder.push_simple("Cst "); + self.data.id.build_rich_ir(builder); + builder.push_simple(format!( + " at {}..{}", + *self.data.span.start, *self.data.span.end + )); + builder.push_simple(": "); + self.kind.build_rich_ir(builder); + } +} #[salsa::query_group(CstDbStorage)] pub trait CstDb: RcstToCst { diff --git a/compiler/frontend/src/rcst.rs b/compiler/frontend/src/rcst.rs index 81855ec32..24ffdf998 100644 --- a/compiler/frontend/src/rcst.rs +++ b/compiler/frontend/src/rcst.rs @@ -2,7 +2,6 @@ use crate::{ cst::{Cst, CstKind}, rich_ir::{RichIrBuilder, ToRichIr}, }; -use enumset::EnumSet; pub type Rcst = Cst<()>; @@ -15,9 +14,9 @@ impl From> for Cst<()> { } } -impl ToRichIr for Rcst { +impl ToRichIr for Cst<()> { fn build_rich_ir(&self, builder: &mut RichIrBuilder) { - builder.push(format!("{self:?}"), None, EnumSet::empty()); + self.kind.build_rich_ir(builder); } } diff --git a/compiler/frontend/src/rich_ir.rs b/compiler/frontend/src/rich_ir.rs index dee631163..475fa2bb0 100644 --- a/compiler/frontend/src/rich_ir.rs +++ b/compiler/frontend/src/rich_ir.rs @@ -1,7 +1,7 @@ use crate::{ ast::Ast, builtin_functions::BuiltinFunction, - hir, + cst, hir, lir::{self, Lir}, mir::{self, Mir}, module::Module, @@ -13,7 +13,6 @@ use crate::{ }; use derive_more::From; use enumset::{EnumSet, EnumSetType}; -use itertools::Itertools; use num_bigint::{BigInt, BigUint}; use rustc_hash::FxHashMap; use std::{ @@ -45,6 +44,7 @@ pub enum ReferenceKey { Module(Module), #[from(ignore)] ModuleWithSpan(Module, Range), + CstId(cst::Id), HirId(hir::Id), MirId(mir::Id), LirId(lir::Id), @@ -92,9 +92,19 @@ pub trait ToRichIr { fn build_rich_ir(&self, builder: &mut RichIrBuilder); } +impl ToRichIr for &T { + fn build_rich_ir(&self, builder: &mut RichIrBuilder) { + (*self).build_rich_ir(builder); + } +} impl ToRichIr for str { fn build_rich_ir(&self, builder: &mut RichIrBuilder) { - builder.push(self, None, EnumSet::empty()); + builder.push_simple(self); + } +} +impl ToRichIr for String { + fn build_rich_ir(&self, builder: &mut RichIrBuilder) { + builder.push_simple(self); } } impl ToRichIr for Option { @@ -158,9 +168,9 @@ impl RichIrBuilder { self.push_indented(|builder| builder.push_foldable(build_children)); } pub fn push_foldable(&mut self, build_children: impl FnOnce(&mut Self)) { - let start = self.ir.text.len().into(); + let start = self.current_offset(); build_children(self); - let end = self.ir.text.len().into(); + let end = self.current_offset(); self.ir.folding_ranges.push(start..end); } @@ -257,6 +267,9 @@ impl RichIrBuilder { self.push_newline(); } + pub fn push_simple(&mut self, text: impl AsRef) -> Range { + self.push(text, None, EnumSet::empty()) + } pub fn push( &mut self, text: impl AsRef, @@ -269,9 +282,9 @@ impl RichIrBuilder { token_modifiers.is_empty() || token_type.is_some(), "`token_modifiers` can only be specified if a `token_type` is specified.", ); - let start = self.ir.text.len().into(); + let start = self.current_offset(); self.ir.text.push_str(text.as_ref()); - let end = self.ir.text.len().into(); + let end = self.current_offset(); let range = start..end; if token_type.is_some() || !token_modifiers.is_empty() { self.ir.annotations.push(RichIrAnnotation { @@ -282,6 +295,10 @@ impl RichIrBuilder { } range } + #[must_use] + pub fn current_offset(&self) -> Offset { + self.ir.text.len().into() + } pub fn push_definition(&mut self, key: impl Into, range: Range) { self.ir.references.entry(key.into()).or_default().definition = Some(range); @@ -365,14 +382,7 @@ impl RichIr { Some(Self::for_ir("CST", module, None, |builder| { match cst { - Ok(cst) => { - // TODO: `impl ToRichIr for Cst` - builder.push( - cst.iter().map(ToString::to_string).join(""), - None, - EnumSet::empty(), - ); - } + Ok(cst) => cst.build_rich_ir(builder), Err(error) => error.build_rich_ir(builder), }; })) diff --git a/compiler/language_server/src/features_ir.rs b/compiler/language_server/src/features_ir.rs index e08cf3086..03944484a 100644 --- a/compiler/language_server/src/features_ir.rs +++ b/compiler/language_server/src/features_ir.rs @@ -516,6 +516,13 @@ impl LanguageFeatures for IrFeatures { let range = db.range_to_lsp_range(module.clone(), span.clone()); (module_to_url(module, &packages_path).unwrap(), range) } + ReferenceKey::CstId(_) => { + let config = IrConfig { + module: config.module.clone(), + ir: Ir::Rcst, + }; + find_in_other_ir(config, &key).await? + } ReferenceKey::HirId(id) => { let config = IrConfig { module: id.module.clone(),