diff --git a/Cargo.lock b/Cargo.lock index 520f8dce13b..e62c6182245 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -965,6 +965,7 @@ dependencies = [ "cairo-lang-diagnostics", "cairo-lang-filesystem", "cairo-lang-lowering", + "cairo-lang-parser", "cairo-lang-plugins", "cairo-lang-semantic", "cairo-lang-sierra", @@ -1053,6 +1054,7 @@ dependencies = [ "cairo-lang-defs", "cairo-lang-filesystem", "cairo-lang-lowering", + "cairo-lang-parser", "cairo-lang-semantic", "cairo-lang-sierra", "cairo-lang-sierra-generator", diff --git a/corelib/src/test/language_features/for_test.cairo b/corelib/src/test/language_features/for_test.cairo index 9c29b2b7e83..9e197a2efbf 100644 --- a/corelib/src/test/language_features/for_test.cairo +++ b/corelib/src/test/language_features/for_test.cairo @@ -22,7 +22,7 @@ fn test_for_loop_array_variables() { fn test_for_loop_array_tuples() { let mut i = 10; for (x, y) in array![ - (10, 10), (11, 11), (12, 12), (13, 13), (14, 14), (15, 15), (16, 16), (17, 17) + (10, 10), (11, 11), (12, 12), (13, 13), (14, 14), (15, 15), (16, 16), (17, 17), ] { assert_eq!(x, i); assert_eq!(y, i); diff --git a/crates/cairo-lang-defs/src/diagnostic_utils.rs b/crates/cairo-lang-defs/src/diagnostic_utils.rs index a4a1c0b73ad..7ff51350aad 100644 --- a/crates/cairo-lang-defs/src/diagnostic_utils.rs +++ b/crates/cairo-lang-defs/src/diagnostic_utils.rs @@ -3,7 +3,7 @@ use std::fmt; use cairo_lang_debug::DebugWithDb; use cairo_lang_diagnostics::DiagnosticLocation; use cairo_lang_filesystem::ids::FileId; -use cairo_lang_filesystem::span::TextSpan; +use cairo_lang_filesystem::span::{TextSpan, TextWidth}; use cairo_lang_syntax::node::ids::SyntaxStablePtrId; use cairo_lang_syntax::node::{SyntaxNode, TypedSyntaxNode}; @@ -11,31 +11,54 @@ use crate::db::DefsGroup; /// A stable location of a real, concrete syntax. #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] -pub struct StableLocation(SyntaxStablePtrId); +pub struct StableLocation { + stable_ptr: SyntaxStablePtrId, + /// An optional inner span of the stable location. Useful for diagnostics caused by inline + /// macros, see [crate::plugin::PluginDiagnostic] for more information. The tuple is (offset, + /// width). + inner_span: Option<(TextWidth, TextWidth)>, +} + impl StableLocation { pub fn new(stable_ptr: SyntaxStablePtrId) -> Self { - Self(stable_ptr) + Self { stable_ptr, inner_span: None } + } + + pub fn with_inner_span( + stable_ptr: SyntaxStablePtrId, + inner_span: (TextWidth, TextWidth), + ) -> Self { + Self { stable_ptr, inner_span: Some(inner_span) } } pub fn file_id(&self, db: &dyn DefsGroup) -> FileId { - self.0.file_id(db.upcast()) + self.stable_ptr.file_id(db.upcast()) } pub fn from_ast(node: &TNode) -> Self { - Self(node.as_syntax_node().stable_ptr()) + Self::new(node.as_syntax_node().stable_ptr()) } /// Returns the [SyntaxNode] that corresponds to the [StableLocation]. pub fn syntax_node(&self, db: &dyn DefsGroup) -> SyntaxNode { - self.0.lookup(db.upcast()) + self.stable_ptr.lookup(db.upcast()) } /// Returns the [DiagnosticLocation] that corresponds to the [StableLocation]. pub fn diagnostic_location(&self, db: &dyn DefsGroup) -> DiagnosticLocation { - let syntax_node = self.syntax_node(db); - DiagnosticLocation { - file_id: self.file_id(db), - span: syntax_node.span_without_trivia(db.upcast()), + match self.inner_span { + Some((start, width)) => { + let start = self.syntax_node(db).offset().add_width(start); + let end = start.add_width(width); + DiagnosticLocation { file_id: self.file_id(db), span: TextSpan { start, end } } + } + None => { + let syntax_node = self.syntax_node(db); + DiagnosticLocation { + file_id: self.file_id(db), + span: syntax_node.span_without_trivia(db.upcast()), + } + } } } @@ -46,9 +69,13 @@ impl StableLocation { until_stable_ptr: SyntaxStablePtrId, ) -> DiagnosticLocation { let syntax_db = db.upcast(); - let start = self.0.lookup(syntax_db).span_start_without_trivia(syntax_db); + let start = self.stable_ptr.lookup(syntax_db).span_start_without_trivia(syntax_db); let end = until_stable_ptr.lookup(syntax_db).span_end_without_trivia(syntax_db); - DiagnosticLocation { file_id: self.0.file_id(syntax_db), span: TextSpan { start, end } } + + DiagnosticLocation { + file_id: self.stable_ptr.file_id(syntax_db), + span: TextSpan { start, end }, + } } } diff --git a/crates/cairo-lang-defs/src/plugin.rs b/crates/cairo-lang-defs/src/plugin.rs index d001a6b6d50..cf6dac14b59 100644 --- a/crates/cairo-lang-defs/src/plugin.rs +++ b/crates/cairo-lang-defs/src/plugin.rs @@ -6,9 +6,10 @@ use cairo_lang_diagnostics::Severity; use cairo_lang_filesystem::cfg::CfgSet; use cairo_lang_filesystem::db::Edition; use cairo_lang_filesystem::ids::CodeMapping; -use cairo_lang_syntax::node::ast; +use cairo_lang_filesystem::span::TextWidth; use cairo_lang_syntax::node::db::SyntaxGroup; use cairo_lang_syntax::node::ids::SyntaxStablePtrId; +use cairo_lang_syntax::node::{SyntaxNode, ast}; use cairo_lang_utils::ordered_hash_set::OrderedHashSet; use smol_str::SmolStr; @@ -63,18 +64,56 @@ pub struct PluginResult { pub remove_original_item: bool, } +/// A diagnostic generated by a plugin. #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct PluginDiagnostic { + /// The stable pointer of the syntax node that caused the diagnostic. pub stable_ptr: SyntaxStablePtrId, + /// The content of the diagnostic. pub message: String, + /// The severity of the diagnostic. pub severity: Severity, + /// An optional inner span inside the stable pointer that caused the diagnostic. Useful for + /// diagnostics caused by inline macros, since the syntax of the arguments is a token tree and + /// is not segmented into each argument. + /// The tuple is (offset, width). + pub inner_span: Option<(TextWidth, TextWidth)>, } impl PluginDiagnostic { pub fn error(stable_ptr: impl Into, message: String) -> PluginDiagnostic { - PluginDiagnostic { stable_ptr: stable_ptr.into(), message, severity: Severity::Error } + PluginDiagnostic { + stable_ptr: stable_ptr.into(), + message, + severity: Severity::Error, + inner_span: None, + } } + + /// Creates a diagnostic, pointing to an inner span inside the given stable pointer. + pub fn error_with_inner_span( + db: &dyn SyntaxGroup, + stable_ptr: impl Into, + inner_span: SyntaxNode, + message: String, + ) -> PluginDiagnostic { + let stable_ptr = stable_ptr.into(); + let offset = inner_span.offset() - stable_ptr.lookup(db).offset(); + let width = inner_span.width(db); + PluginDiagnostic { + stable_ptr, + message, + severity: Severity::Error, + inner_span: Some((offset, width)), + } + } + pub fn warning(stable_ptr: impl Into, message: String) -> PluginDiagnostic { - PluginDiagnostic { stable_ptr: stable_ptr.into(), message, severity: Severity::Warning } + PluginDiagnostic { + stable_ptr: stable_ptr.into(), + message, + severity: Severity::Warning, + inner_span: None, + } } } diff --git a/crates/cairo-lang-defs/src/plugin_utils.rs b/crates/cairo-lang-defs/src/plugin_utils.rs index ce3ae12d3e4..202ecc350c0 100644 --- a/crates/cairo-lang-defs/src/plugin_utils.rs +++ b/crates/cairo-lang-defs/src/plugin_utils.rs @@ -1,5 +1,6 @@ use cairo_lang_syntax::node::db::SyntaxGroup; use cairo_lang_syntax::node::helpers::WrappedArgListHelper; +use cairo_lang_syntax::node::ids::SyntaxStablePtrId; use cairo_lang_syntax::node::{SyntaxNode, TypedSyntaxNode, ast}; use cairo_lang_utils::require; use itertools::Itertools; @@ -14,7 +15,7 @@ pub trait InlineMacroCall { fn path(&self, db: &dyn SyntaxGroup) -> Self::PathNode; } -impl InlineMacroCall for ast::ExprInlineMacro { +impl InlineMacroCall for ast::LegacyExprInlineMacro { type PathNode = ast::ExprPath; type Result = InlinePluginResult; @@ -27,7 +28,7 @@ impl InlineMacroCall for ast::ExprInlineMacro { } } -impl InlineMacroCall for ast::ItemInlineMacro { +impl InlineMacroCall for ast::LegacyItemInlineMacro { type PathNode = ast::TerminalIdentifier; type Result = PluginResult; @@ -60,17 +61,29 @@ impl PluginResultTrait for PluginResult { /// Returns diagnostics for an unsupported bracket type. pub fn unsupported_bracket_diagnostic( db: &dyn SyntaxGroup, - macro_ast: &CallAst, + legacy_macro_ast: &CallAst, + macro_ast: impl Into, ) -> CallAst::Result { - CallAst::Result::diagnostic_only(PluginDiagnostic::error( - macro_ast.arguments(db).left_bracket_stable_ptr(db), + CallAst::Result::diagnostic_only(PluginDiagnostic::error_with_inner_span( + db, + macro_ast, + legacy_macro_ast.arguments(db).left_bracket_syntax_node(db), format!( "Macro `{}` does not support this bracket type.", - macro_ast.path(db).as_syntax_node().get_text_without_trivia(db) + legacy_macro_ast.path(db).as_syntax_node().get_text_without_trivia(db) ), )) } +pub fn not_legacy_macro_diagnostic(stable_ptr: SyntaxStablePtrId) -> PluginDiagnostic { + PluginDiagnostic::error( + stable_ptr, + "Macro can not be parsed as legacy macro. Expected an argument list wrapped in either \ + parentheses, brackets, or braces." + .to_string(), + ) +} + /// Extracts a single unnamed argument. pub fn extract_single_unnamed_arg( db: &dyn SyntaxGroup, @@ -118,14 +131,19 @@ pub fn escape_node(db: &dyn SyntaxGroup, node: SyntaxNode) -> String { /// db, /// syntax, /// 2, -/// ast::WrappedArgList::ParenthesizedArgList(_) | ast::WrappedArgList::BracedArgList(_) +/// ast::WrappedArgList::ParenthesizedArgList(_) | ast::WrappedArgList::BracedArgList(_), +/// token_tree_syntax.stable_ptr() /// ); #[macro_export] macro_rules! extract_macro_unnamed_args { - ($db:expr, $syntax:expr, $n:expr, $pattern:pat) => {{ + ($db:expr, $syntax:expr, $n:expr, $pattern:pat, $diagnostics_ptr:expr) => {{ let arguments = $crate::plugin_utils::InlineMacroCall::arguments($syntax, $db); if !matches!(arguments, $pattern) { - return $crate::plugin_utils::unsupported_bracket_diagnostic($db, $syntax); + return $crate::plugin_utils::unsupported_bracket_diagnostic( + $db, + $syntax, + $diagnostics_ptr, + ); } // `unwrap` is ok because the above `matches` condition ensures it's not None (unless // the pattern contains the `Missing` variant). @@ -137,7 +155,7 @@ macro_rules! extract_macro_unnamed_args { let Some(args) = args else { return $crate::plugin_utils::PluginResultTrait::diagnostic_only( PluginDiagnostic::error( - $syntax, + $diagnostics_ptr, format!( "Macro `{}` must have exactly {} unnamed arguments.", $crate::plugin_utils::InlineMacroCall::path($syntax, $db) @@ -154,18 +172,23 @@ macro_rules! extract_macro_unnamed_args { } /// Macro to extract a single unnamed argument of an inline macro. +/// /// Gets the pattern for the allowed bracket types, and returns the argument expression. +/// The arguments are extracted from a `WrappedArgList` node syntax, as was in legacy inline macros. +/// However, as macros are now parsed as general token trees, the diagnostics pointer is passed to +/// the macro to allow pointing to the original location. /// /// Example usage (allowing `()` or `{}` brackets): /// let arg = extract_macro_single_unnamed_arg!( /// db, -/// syntax, -/// ast::WrappedArgList::ParenthesizedArgList(_) | ast::WrappedArgList::BracedArgList(_) +/// arg_list_syntax, +/// ast::WrappedArgList::ParenthesizedArgList(_) | ast::WrappedArgList::BracedArgList(_), +/// token_tree_syntax.stable_ptr() /// ); #[macro_export] macro_rules! extract_macro_single_unnamed_arg { - ($db:expr, $syntax:expr, $pattern:pat) => {{ - let [x] = $crate::extract_macro_unnamed_args!($db, $syntax, 1, $pattern); + ($db:expr, $syntax:expr, $pattern:pat, $diagnostics_ptr:expr) => {{ + let [x] = $crate::extract_macro_unnamed_args!($db, $syntax, 1, $pattern, $diagnostics_ptr); x }}; } diff --git a/crates/cairo-lang-defs/src/test.rs b/crates/cairo-lang-defs/src/test.rs index 1b26ce2da72..34e979031a3 100644 --- a/crates/cairo-lang-defs/src/test.rs +++ b/crates/cairo-lang-defs/src/test.rs @@ -476,6 +476,6 @@ fn test_unknown_item_macro() { format!("{:?}", db.module_plugin_diagnostics(module_id).unwrap()), "[(ModuleFileId(CrateRoot(CrateId(0)), FileIndex(0)), PluginDiagnostic { stable_ptr: \ SyntaxStablePtrId(3), message: \"Unknown inline item macro: 'unknown_item_macro'.\", \ - severity: Error })]" + severity: Error, inner_span: None })]" ) } diff --git a/crates/cairo-lang-formatter/src/formatter_impl.rs b/crates/cairo-lang-formatter/src/formatter_impl.rs index 31635d3886e..1cab2e7d348 100644 --- a/crates/cairo-lang-formatter/src/formatter_impl.rs +++ b/crates/cairo-lang-formatter/src/formatter_impl.rs @@ -2,9 +2,10 @@ use std::cmp::Ordering; use std::fmt; use cairo_lang_filesystem::span::TextWidth; +use cairo_lang_parser::macro_helpers::token_tree_as_wrapped_arg_list; use cairo_lang_syntax as syntax; use cairo_lang_syntax::attribute::consts::FMT_SKIP_ATTR; -use cairo_lang_syntax::node::ast::UsePath; +use cairo_lang_syntax::node::ast::{TokenTreeNode, UsePath}; use cairo_lang_syntax::node::db::SyntaxGroup; use cairo_lang_syntax::node::{SyntaxNode, Terminal, TypedSyntaxNode, ast}; use itertools::Itertools; @@ -792,6 +793,24 @@ impl<'a> FormatterImpl<'a> { /// Appends a formatted string, representing the syntax_node, to the result. /// Should be called with a root syntax node to format a file. fn format_node(&mut self, syntax_node: &SyntaxNode) { + // If we encounter a token tree node, i.e. a macro, we try to parse it as a + // [ast::WrappedArgList] (the syntax kind of legacy macro calls). If successful, we + // format the wrapped arg list according to the rules of wrapped arg lists, otherwise we + // treat it as a normal syntax node, and in practice no formatting is done. + // TODO(Gil): Consider if we want to keep this behavior when general macro support is added. + if syntax_node.kind(self.db) == SyntaxKind::TokenTreeNode { + let as_wrapped_arg_list = token_tree_as_wrapped_arg_list( + TokenTreeNode::from_syntax_node(self.db, syntax_node.clone()), + self.db, + ); + let file_id = syntax_node.stable_ptr().file_id(self.db); + + if let Some(wrapped_arg_list) = as_wrapped_arg_list { + let new_syntax_node = SyntaxNode::new_root(self.db, file_id, wrapped_arg_list.0); + self.format_node(&new_syntax_node); + return; + } + } if syntax_node.text(self.db).is_some() { panic!("Token reached before terminal."); } diff --git a/crates/cairo-lang-formatter/src/node_properties.rs b/crates/cairo-lang-formatter/src/node_properties.rs index f204a65962e..69200e0292f 100644 --- a/crates/cairo-lang-formatter/src/node_properties.rs +++ b/crates/cairo-lang-formatter/src/node_properties.rs @@ -378,7 +378,10 @@ impl SyntaxNodeFormat for SyntaxNode { | SyntaxKind::TraitItemList | SyntaxKind::ImplItemList | SyntaxKind::UsePathMulti - | SyntaxKind::ItemEnum => Some(5), + | SyntaxKind::ItemEnum + | SyntaxKind::ParenthesizedTokenTree + | SyntaxKind::BracedTokenTree + | SyntaxKind::BracketedTokenTree => Some(5), _ => None, }, } diff --git a/crates/cairo-lang-language-server/src/ide/macros/expand.rs b/crates/cairo-lang-language-server/src/ide/macros/expand.rs index 3123d242a2a..e6d15f12433 100644 --- a/crates/cairo-lang-language-server/src/ide/macros/expand.rs +++ b/crates/cairo-lang-language-server/src/ide/macros/expand.rs @@ -155,6 +155,7 @@ fn expand_inline_macros( &mut files, &mut output, FileProcessorConfig::main_file(db, node_to_expand, top_level_macro_kind), + top_level_macro_kind, )?; while let Some(file) = files.pop_front() { @@ -165,6 +166,7 @@ fn expand_inline_macros( &mut files, &mut output, FileProcessorConfig::generated_file(db, file, db.file_content(file)?.to_string())?, + top_level_macro_kind, )?; } @@ -250,6 +252,7 @@ fn expand_inline_macros_in_single_file( files: &mut VecDeque, output: &mut String, mut config: FileProcessorConfig, + top_level_macro_kind: TopLevelMacroKind, ) -> Option<()> { let plugins = db.inline_macro_plugins(); @@ -277,10 +280,12 @@ fn expand_inline_macros_in_single_file( name: file.file_name(db).into(), content: config.content.into(), code_mappings: Default::default(), - kind: file.kind(db), + kind: match top_level_macro_kind { + TopLevelMacroKind::Inline => FileKind::Expr, + TopLevelMacroKind::Attribute => FileKind::Module, + }, }) .intern(db); - files.push_back(new_file); }; diff --git a/crates/cairo-lang-lowering/src/optimizations/cancel_ops.rs b/crates/cairo-lang-lowering/src/optimizations/cancel_ops.rs index 269ba0a7bd0..eb0ae178546 100644 --- a/crates/cairo-lang-lowering/src/optimizations/cancel_ops.rs +++ b/crates/cairo-lang-lowering/src/optimizations/cancel_ops.rs @@ -58,7 +58,7 @@ pub struct CancelOpsContext<'a> { lowered: &'a FlatLowered, /// Maps a variable to the use sites of that variable. - /// Note that a remapping is cosidered as usage here. + /// Note that a remapping is considered as usage here. use_sites: UnorderedHashMap>, /// Maps a variable to the variable that it was renamed to. diff --git a/crates/cairo-lang-parser/src/lib.rs b/crates/cairo-lang-parser/src/lib.rs index 3c503c6e54a..0da3c206e0b 100644 --- a/crates/cairo-lang-parser/src/lib.rs +++ b/crates/cairo-lang-parser/src/lib.rs @@ -7,6 +7,7 @@ pub mod colored_printer; pub mod db; pub mod diagnostic; pub mod lexer; +pub mod macro_helpers; pub mod operators; pub mod parser; pub mod printer; diff --git a/crates/cairo-lang-parser/src/macro_helpers.rs b/crates/cairo-lang-parser/src/macro_helpers.rs new file mode 100644 index 00000000000..e9d930a52dc --- /dev/null +++ b/crates/cairo-lang-parser/src/macro_helpers.rs @@ -0,0 +1,98 @@ +use cairo_lang_diagnostics::DiagnosticsBuilder; +use cairo_lang_syntax::node::ast::{ + AttributeListGreen, ExprInlineMacro, ExprPathGreen, ItemInlineMacro, LegacyExprInlineMacro, + LegacyItemInlineMacro, TerminalIdentifierGreen, TerminalNotGreen, TerminalSemicolonGreen, + TokenTreeNode, WrappedArgListGreen, +}; +use cairo_lang_syntax::node::db::SyntaxGroup; +use cairo_lang_syntax::node::kind::SyntaxKind; +use cairo_lang_syntax::node::{SyntaxNode, TypedSyntaxNode}; + +use crate::ParserDiagnostic; +use crate::diagnostic::ParserDiagnosticKind; +use crate::parser::{Parser, SkippedError}; +use crate::recovery::is_of_kind; + +/// Takes a token tree syntax node, which is assumed to be parsable as a wrapped argument list, try +/// to parse it as such and return the result. +pub fn token_tree_as_wrapped_arg_list( + token_tree: TokenTreeNode, + db: &dyn SyntaxGroup, +) -> Option { + let mut diagnostics: DiagnosticsBuilder = DiagnosticsBuilder::default(); + let node_text = token_tree.as_syntax_node().get_text(db); + let file_id = token_tree.stable_ptr().0.file_id(db); + let mut parser = Parser::new(db, file_id, &node_text, &mut diagnostics); + let wrapped_arg_list_green = parser.parse_wrapped_arg_list(); + if let Err(SkippedError(span)) = parser.skip_until(is_of_kind!()) { + parser.add_diagnostic( + ParserDiagnosticKind::SkippedElement { element_name: "end arg list".into() }, + span, + ); + }; + let diagnostics = diagnostics.build(); + if !diagnostics.get_all().is_empty() { + return None; + } + Some(wrapped_arg_list_green) +} + +/// Trait for converting inline macros with token tree syntax as the argument to legacy inline which +/// must have a wrapped argument list syntax node. +pub trait AsLegacyInlineMacro { + /// The corresponding legacy inline macro type. + type LegacyType; + /// Converts the inline macro to the legacy inline macro. + fn as_legacy_inline_macro(&self, db: &dyn SyntaxGroup) -> Option; +} + +impl AsLegacyInlineMacro for ExprInlineMacro { + type LegacyType = LegacyExprInlineMacro; + + fn as_legacy_inline_macro(&self, db: &dyn SyntaxGroup) -> Option { + let green_node = self.as_syntax_node().green_node(db); + let [macro_name, bang, _arguments] = green_node.children() else { + return None; + }; + let macro_name = ExprPathGreen(*macro_name); + let bang = TerminalNotGreen(*bang); + let wrapped_arg_list = token_tree_as_wrapped_arg_list(self.arguments(db), db)?; + let legacy_green = LegacyExprInlineMacro::new_green(db, macro_name, bang, wrapped_arg_list); + let file_id = self.stable_ptr().0.file_id(db); + let offset = self.stable_ptr().0.lookup(db).offset(); + Some(LegacyExprInlineMacro::from_syntax_node( + db, + SyntaxNode::new_root_with_offset(db, file_id, legacy_green.0, offset), + )) + } +} + +impl AsLegacyInlineMacro for ItemInlineMacro { + type LegacyType = LegacyItemInlineMacro; + + fn as_legacy_inline_macro(&self, db: &dyn SyntaxGroup) -> Option { + let green_node = self.as_syntax_node().green_node(db); + let [attributes, macro_name, bang, _arguments, semicolon] = green_node.children() else { + return None; + }; + let attributes = AttributeListGreen(*attributes); + let macro_name = TerminalIdentifierGreen(*macro_name); + let bang = TerminalNotGreen(*bang); + let wrapped_arg_list = token_tree_as_wrapped_arg_list(self.arguments(db), db)?; + let semicolon = TerminalSemicolonGreen(*semicolon); + let legacy_green = LegacyItemInlineMacro::new_green( + db, + attributes, + macro_name, + bang, + wrapped_arg_list, + semicolon, + ); + let file_id = self.stable_ptr().0.file_id(db); + let offset = self.stable_ptr().0.lookup(db).offset(); + Some(LegacyItemInlineMacro::from_syntax_node( + db, + SyntaxNode::new_root_with_offset(db, file_id, legacy_green.0, offset), + )) + } +} diff --git a/crates/cairo-lang-parser/src/parser.rs b/crates/cairo-lang-parser/src/parser.rs index c662f43bd72..12d744e9187 100644 --- a/crates/cairo-lang-parser/src/parser.rs +++ b/crates/cairo-lang-parser/src/parser.rs @@ -101,7 +101,7 @@ macro_rules! or_an_attribute { impl<'a> Parser<'a> { /// Creates a new parser. - fn new( + pub fn new( db: &'a dyn SyntaxGroup, file_id: FileId, text: &'a str, @@ -124,7 +124,7 @@ impl<'a> Parser<'a> { } /// Adds a diagnostic to the parser diagnostics collection. - fn add_diagnostic(&mut self, kind: ParserDiagnosticKind, span: TextSpan) { + pub fn add_diagnostic(&mut self, kind: ParserDiagnosticKind, span: TextSpan) { self.diagnostics.add(ParserDiagnostic { file_id: self.file_id, kind, span }); } @@ -1013,9 +1013,9 @@ impl<'a> Parser<'a> { name: TerminalIdentifierGreen, bang: TerminalNotGreen, ) -> ItemInlineMacroGreen { - let arguments = self.parse_wrapped_arg_list(); + let token_tree_node = self.parse_token_tree_node(); let semicolon = self.parse_token::(); - ItemInlineMacro::new_green(self.db, attributes, name, bang, arguments, semicolon) + ItemInlineMacro::new_green(self.db, attributes, name, bang, token_tree_node, semicolon) } // ------------------------------- Expressions ------------------------------- @@ -1313,13 +1313,167 @@ impl<'a> Parser<'a> { fn expect_macro_call(&mut self, path: ExprPathGreen) -> ExprInlineMacroGreen { let bang = self.take::(); let macro_name = path; - let wrapped_expr_list = self.parse_wrapped_arg_list(); - ExprInlineMacro::new_green(self.db, macro_name, bang, wrapped_expr_list) + let token_tree_node = self.parse_token_tree_node(); + ExprInlineMacro::new_green(self.db, macro_name, bang, token_tree_node) } + /// Either parses a leaf of the tree (i.e. any non-parenthesis token) or an inner node (i.e. a + /// parenthesized stream of tokens). + fn parse_token_tree(&mut self) -> TokenTreeGreen { + match self.peek().kind { + SyntaxKind::TerminalLBrace + | SyntaxKind::TerminalLParen + | SyntaxKind::TerminalLBrack => self.parse_token_tree_node().into(), + _ => self.parse_token_tree_leaf().into(), + } + } + + fn parse_token_tree_leaf(&mut self) -> TokenTreeLeafGreen { + let token_node = self.take_token_node(); + TokenTreeLeaf::new_green(self.db, token_node) + } + + fn parse_token_tree_node(&mut self) -> TokenTreeNodeGreen { + let wrapped_token_tree = match self.peek().kind { + SyntaxKind::TerminalLBrace => self + .expect_wrapped_token_tree::( + BracedTokenTree::new_green, + ) + .into(), + SyntaxKind::TerminalLParen => self + .expect_wrapped_token_tree::( + ParenthesizedTokenTree::new_green, + ) + .into(), + SyntaxKind::TerminalLBrack => self + .expect_wrapped_token_tree::( + BracketedTokenTree::new_green, + ) + .into(), + _ => { + return self.create_and_report_missing::( + ParserDiagnosticKind::MissingWrappedArgList, + ); + } + }; + TokenTreeNode::new_green(self.db, wrapped_token_tree) + } + + /// Assumes the current token is LTerminal. + /// Expected pattern: `[LTerminal](,)*?[RTerminal]` + /// Gets `new_green` a green id node builder for the list of the requested type, applies it to + /// the parsed list and returns the result. + fn expect_wrapped_token_tree< + LTerminal: syntax::node::Terminal, + RTerminal: syntax::node::Terminal, + ListGreen, + NewGreen: Fn(&dyn SyntaxGroup, LTerminal::Green, TokenListGreen, RTerminal::Green) -> ListGreen, + >( + &mut self, + new_green: NewGreen, + ) -> ListGreen { + let l_term = self.take::(); + let mut tokens: Vec = vec![]; + while !matches!( + self.peek().kind, + SyntaxKind::TerminalRParen + | SyntaxKind::TerminalRBrace + | SyntaxKind::TerminalRBrack + | SyntaxKind::TerminalEndOfFile + ) { + let token_tree = self.parse_token_tree(); + tokens.push(token_tree); + } + let r_term: ::Green = self.parse_token::(); + new_green(self.db, l_term, TokenList::new_green(self.db, tokens), r_term) + } + + /// Takes a TokenNode according to the current SyntaxKind. + fn take_token_node(&mut self) -> TokenNodeGreen { + match self.peek().kind { + SyntaxKind::TerminalIdentifier => self.take::().into(), + SyntaxKind::TerminalLiteralNumber => self.take::().into(), + SyntaxKind::TerminalShortString => self.take::().into(), + SyntaxKind::TerminalString => self.take::().into(), + SyntaxKind::TerminalAs => self.take::().into(), + SyntaxKind::TerminalConst => self.take::().into(), + SyntaxKind::TerminalElse => self.take::().into(), + SyntaxKind::TerminalEnum => self.take::().into(), + SyntaxKind::TerminalExtern => self.take::().into(), + SyntaxKind::TerminalFalse => self.take::().into(), + SyntaxKind::TerminalFunction => self.take::().into(), + SyntaxKind::TerminalIf => self.take::().into(), + SyntaxKind::TerminalWhile => self.take::().into(), + SyntaxKind::TerminalFor => self.take::().into(), + SyntaxKind::TerminalLoop => self.take::().into(), + SyntaxKind::TerminalImpl => self.take::().into(), + SyntaxKind::TerminalImplicits => self.take::().into(), + SyntaxKind::TerminalLet => self.take::().into(), + SyntaxKind::TerminalMatch => self.take::().into(), + SyntaxKind::TerminalModule => self.take::().into(), + SyntaxKind::TerminalMut => self.take::().into(), + SyntaxKind::TerminalNoPanic => self.take::().into(), + SyntaxKind::TerminalOf => self.take::().into(), + SyntaxKind::TerminalRef => self.take::().into(), + SyntaxKind::TerminalContinue => self.take::().into(), + SyntaxKind::TerminalReturn => self.take::().into(), + SyntaxKind::TerminalBreak => self.take::().into(), + SyntaxKind::TerminalStruct => self.take::().into(), + SyntaxKind::TerminalTrait => self.take::().into(), + SyntaxKind::TerminalTrue => self.take::().into(), + SyntaxKind::TerminalType => self.take::().into(), + SyntaxKind::TerminalUse => self.take::().into(), + SyntaxKind::TerminalPub => self.take::().into(), + SyntaxKind::TerminalAnd => self.take::().into(), + SyntaxKind::TerminalAndAnd => self.take::().into(), + SyntaxKind::TerminalArrow => self.take::().into(), + SyntaxKind::TerminalAt => self.take::().into(), + SyntaxKind::TerminalBadCharacters => self.take::().into(), + SyntaxKind::TerminalColon => self.take::().into(), + SyntaxKind::TerminalColonColon => self.take::().into(), + SyntaxKind::TerminalComma => self.take::().into(), + SyntaxKind::TerminalDiv => self.take::().into(), + SyntaxKind::TerminalDivEq => self.take::().into(), + SyntaxKind::TerminalDot => self.take::().into(), + SyntaxKind::TerminalDotDot => self.take::().into(), + SyntaxKind::TerminalEndOfFile => self.take::().into(), + SyntaxKind::TerminalEq => self.take::().into(), + SyntaxKind::TerminalEqEq => self.take::().into(), + SyntaxKind::TerminalGE => self.take::().into(), + SyntaxKind::TerminalGT => self.take::().into(), + SyntaxKind::TerminalHash => self.take::().into(), + SyntaxKind::TerminalLBrace => self.take::().into(), + SyntaxKind::TerminalLBrack => self.take::().into(), + SyntaxKind::TerminalLE => self.take::().into(), + SyntaxKind::TerminalLParen => self.take::().into(), + SyntaxKind::TerminalLT => self.take::().into(), + SyntaxKind::TerminalMatchArrow => self.take::().into(), + SyntaxKind::TerminalMinus => self.take::().into(), + SyntaxKind::TerminalMinusEq => self.take::().into(), + SyntaxKind::TerminalMod => self.take::().into(), + SyntaxKind::TerminalModEq => self.take::().into(), + SyntaxKind::TerminalMul => self.take::().into(), + SyntaxKind::TerminalMulEq => self.take::().into(), + SyntaxKind::TerminalNeq => self.take::().into(), + SyntaxKind::TerminalNot => self.take::().into(), + SyntaxKind::TerminalBitNot => self.take::().into(), + SyntaxKind::TerminalOr => self.take::().into(), + SyntaxKind::TerminalOrOr => self.take::().into(), + SyntaxKind::TerminalPlus => self.take::().into(), + SyntaxKind::TerminalPlusEq => self.take::().into(), + SyntaxKind::TerminalQuestionMark => self.take::().into(), + SyntaxKind::TerminalRBrace => self.take::().into(), + SyntaxKind::TerminalRBrack => self.take::().into(), + SyntaxKind::TerminalRParen => self.take::().into(), + SyntaxKind::TerminalSemicolon => self.take::().into(), + SyntaxKind::TerminalUnderscore => self.take::().into(), + SyntaxKind::TerminalXor => self.take::().into(), + _ => unreachable!("Lexer syntax kinds should be a terminal."), + } + } /// Returns a GreenId of a node with an ArgListParenthesized|ArgListBracketed|ArgListBraced kind /// or TryParseFailure if such an argument list can't be parsed. - fn parse_wrapped_arg_list(&mut self) -> WrappedArgListGreen { + pub(crate) fn parse_wrapped_arg_list(&mut self) -> WrappedArgListGreen { let current_token = self.peek().kind; match current_token { SyntaxKind::TerminalLParen => self @@ -2803,7 +2957,10 @@ impl<'a> Parser<'a> { /// Skips terminals until `should_stop` returns `true`. /// /// Returns the span of the skipped terminals, if any. - fn skip_until(&mut self, should_stop: fn(SyntaxKind) -> bool) -> Result<(), SkippedError> { + pub(crate) fn skip_until( + &mut self, + should_stop: fn(SyntaxKind) -> bool, + ) -> Result<(), SkippedError> { let mut diag_start = None; let mut diag_end = None; while !should_stop(self.peek().kind) { @@ -2983,7 +3140,7 @@ enum LbraceAllowed { } /// Indicates that [Parser::skip_until] skipped some terminals. -struct SkippedError(TextSpan); +pub(crate) struct SkippedError(pub(crate) TextSpan); /// Defines the parser behavior in the case of a parsing error. struct ErrorRecovery { diff --git a/crates/cairo-lang-parser/src/parser_test_data/full_trees/test1 b/crates/cairo-lang-parser/src/parser_test_data/full_trees/test1 index e0e6cd1ab00..27e86d594b6 100644 --- a/crates/cairo-lang-parser/src/parser_test_data/full_trees/test1 +++ b/crates/cairo-lang-parser/src/parser_test_data/full_trees/test1 @@ -805,30 +805,21 @@ error: Missing tokens. Expected an item after attributes. │ │ ├── attributes (kind: AttributeList) [] │ │ ├── name (kind: TokenIdentifier): 'inline_item_macro' │ │ ├── bang (kind: TokenNot): '!' - │ │ ├── arguments (kind: ArgListParenthesized) - │ │ │ ├── lparen (kind: TokenLParen): '(' - │ │ │ ├── arguments (kind: ArgList) - │ │ │ │ ├── item #0 (kind: Arg) - │ │ │ │ │ ├── modifiers (kind: ModifierList) [] - │ │ │ │ │ └── arg_clause (kind: ArgClauseUnnamed) - │ │ │ │ │ └── value (kind: ExprPath) - │ │ │ │ │ └── item #0 (kind: PathSegmentSimple) - │ │ │ │ │ └── ident (kind: TokenIdentifier): 'x' - │ │ │ │ ├── separator #0 (kind: TokenComma): ',' - │ │ │ │ ├── item #1 (kind: Arg) - │ │ │ │ │ ├── modifiers (kind: ModifierList) [] - │ │ │ │ │ └── arg_clause (kind: ArgClauseUnnamed) - │ │ │ │ │ └── value (kind: ExprPath) - │ │ │ │ │ └── item #0 (kind: PathSegmentSimple) - │ │ │ │ │ └── ident (kind: TokenIdentifier): 'y' - │ │ │ │ ├── separator #1 (kind: TokenComma): ',' - │ │ │ │ └── item #2 (kind: Arg) - │ │ │ │ ├── modifiers (kind: ModifierList) [] - │ │ │ │ └── arg_clause (kind: ArgClauseUnnamed) - │ │ │ │ └── value (kind: ExprPath) - │ │ │ │ └── item #0 (kind: PathSegmentSimple) - │ │ │ │ └── ident (kind: TokenIdentifier): 'z' - │ │ │ └── rparen (kind: TokenRParen): ')' + │ │ ├── arguments (kind: TokenTreeNode) + │ │ │ └── subtree (kind: ParenthesizedTokenTree) + │ │ │ ├── lparen (kind: TokenLParen): '(' + │ │ │ ├── tokens (kind: TokenList) + │ │ │ │ ├── child #0 (kind: TokenTreeLeaf) + │ │ │ │ │ └── leaf (kind: TokenIdentifier): 'x' + │ │ │ │ ├── child #1 (kind: TokenTreeLeaf) + │ │ │ │ │ └── leaf (kind: TokenComma): ',' + │ │ │ │ ├── child #2 (kind: TokenTreeLeaf) + │ │ │ │ │ └── leaf (kind: TokenIdentifier): 'y' + │ │ │ │ ├── child #3 (kind: TokenTreeLeaf) + │ │ │ │ │ └── leaf (kind: TokenComma): ',' + │ │ │ │ └── child #4 (kind: TokenTreeLeaf) + │ │ │ │ └── leaf (kind: TokenIdentifier): 'z' + │ │ │ └── rparen (kind: TokenRParen): ')' │ │ └── semicolon (kind: TokenSemicolon): ';' │ └── child #12: Missing [] └── eof (kind: TokenEndOfFile). @@ -2897,53 +2888,44 @@ error: Missing tokens. Expected an item after attributes. │ │ │ ├── leading_trivia (kind: Trivia) [] │ │ │ ├── token (kind: TokenNot): '!' │ │ │ └── trailing_trivia (kind: Trivia) [] - │ │ ├── arguments (kind: ArgListParenthesized) - │ │ │ ├── lparen (kind: TerminalLParen) - │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ ├── token (kind: TokenLParen): '(' - │ │ │ │ └── trailing_trivia (kind: Trivia) [] - │ │ │ ├── arguments (kind: ArgList) - │ │ │ │ ├── item #0 (kind: Arg) - │ │ │ │ │ ├── modifiers (kind: ModifierList) [] - │ │ │ │ │ └── arg_clause (kind: ArgClauseUnnamed) - │ │ │ │ │ └── value (kind: ExprPath) - │ │ │ │ │ └── item #0 (kind: PathSegmentSimple) - │ │ │ │ │ └── ident (kind: TerminalIdentifier) - │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ │ ├── token (kind: TokenIdentifier): 'x' - │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] - │ │ │ │ ├── separator #0 (kind: TerminalComma) - │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ │ ├── token (kind: TokenComma): ',' - │ │ │ │ │ └── trailing_trivia (kind: Trivia) - │ │ │ │ │ └── child #0 (kind: TokenWhitespace). - │ │ │ │ ├── item #1 (kind: Arg) - │ │ │ │ │ ├── modifiers (kind: ModifierList) [] - │ │ │ │ │ └── arg_clause (kind: ArgClauseUnnamed) - │ │ │ │ │ └── value (kind: ExprPath) - │ │ │ │ │ └── item #0 (kind: PathSegmentSimple) - │ │ │ │ │ └── ident (kind: TerminalIdentifier) - │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ │ ├── token (kind: TokenIdentifier): 'y' - │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] - │ │ │ │ ├── separator #1 (kind: TerminalComma) - │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ │ ├── token (kind: TokenComma): ',' - │ │ │ │ │ └── trailing_trivia (kind: Trivia) - │ │ │ │ │ └── child #0 (kind: TokenWhitespace). - │ │ │ │ └── item #2 (kind: Arg) - │ │ │ │ ├── modifiers (kind: ModifierList) [] - │ │ │ │ └── arg_clause (kind: ArgClauseUnnamed) - │ │ │ │ └── value (kind: ExprPath) - │ │ │ │ └── item #0 (kind: PathSegmentSimple) - │ │ │ │ └── ident (kind: TerminalIdentifier) - │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ ├── token (kind: TokenIdentifier): 'z' - │ │ │ │ └── trailing_trivia (kind: Trivia) [] - │ │ │ └── rparen (kind: TerminalRParen) - │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ ├── token (kind: TokenRParen): ')' - │ │ │ └── trailing_trivia (kind: Trivia) [] + │ │ ├── arguments (kind: TokenTreeNode) + │ │ │ └── subtree (kind: ParenthesizedTokenTree) + │ │ │ ├── lparen (kind: TerminalLParen) + │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ ├── token (kind: TokenLParen): '(' + │ │ │ │ └── trailing_trivia (kind: Trivia) [] + │ │ │ ├── tokens (kind: TokenList) + │ │ │ │ ├── child #0 (kind: TokenTreeLeaf) + │ │ │ │ │ └── leaf (kind: TerminalIdentifier) + │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ │ ├── token (kind: TokenIdentifier): 'x' + │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] + │ │ │ │ ├── child #1 (kind: TokenTreeLeaf) + │ │ │ │ │ └── leaf (kind: TerminalComma) + │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ │ ├── token (kind: TokenComma): ',' + │ │ │ │ │ └── trailing_trivia (kind: Trivia) + │ │ │ │ │ └── child #0 (kind: TokenWhitespace). + │ │ │ │ ├── child #2 (kind: TokenTreeLeaf) + │ │ │ │ │ └── leaf (kind: TerminalIdentifier) + │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ │ ├── token (kind: TokenIdentifier): 'y' + │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] + │ │ │ │ ├── child #3 (kind: TokenTreeLeaf) + │ │ │ │ │ └── leaf (kind: TerminalComma) + │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ │ ├── token (kind: TokenComma): ',' + │ │ │ │ │ └── trailing_trivia (kind: Trivia) + │ │ │ │ │ └── child #0 (kind: TokenWhitespace). + │ │ │ │ └── child #4 (kind: TokenTreeLeaf) + │ │ │ │ └── leaf (kind: TerminalIdentifier) + │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ ├── token (kind: TokenIdentifier): 'z' + │ │ │ │ └── trailing_trivia (kind: Trivia) [] + │ │ │ └── rparen (kind: TerminalRParen) + │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ ├── token (kind: TokenRParen): ')' + │ │ │ └── trailing_trivia (kind: Trivia) [] │ │ └── semicolon (kind: TerminalSemicolon) │ │ ├── leading_trivia (kind: Trivia) [] │ │ ├── token (kind: TokenSemicolon): ';' diff --git a/crates/cairo-lang-parser/src/parser_test_data/full_trees/test3 b/crates/cairo-lang-parser/src/parser_test_data/full_trees/test3 index aaad6e1dd2f..21473e8e0b2 100644 --- a/crates/cairo-lang-parser/src/parser_test_data/full_trees/test3 +++ b/crates/cairo-lang-parser/src/parser_test_data/full_trees/test3 @@ -154,10 +154,11 @@ false │ │ │ │ │ │ └── item #0 (kind: PathSegmentSimple) │ │ │ │ │ │ └── ident (kind: TokenIdentifier): 'some_macro' │ │ │ │ │ ├── bang (kind: TokenNot): '!' - │ │ │ │ │ └── arguments (kind: ArgListParenthesized) - │ │ │ │ │ ├── lparen (kind: TokenLParen): '(' - │ │ │ │ │ ├── arguments (kind: ArgList) [] - │ │ │ │ │ └── rparen (kind: TokenRParen): ')' + │ │ │ │ │ └── arguments (kind: TokenTreeNode) + │ │ │ │ │ └── subtree (kind: ParenthesizedTokenTree) + │ │ │ │ │ ├── lparen (kind: TokenLParen): '(' + │ │ │ │ │ ├── tokens (kind: TokenList) [] + │ │ │ │ │ └── rparen (kind: TokenRParen): ')' │ │ │ │ └── semicolon (kind: TokenSemicolon): ';' │ │ │ └── child #2 (kind: StatementExpr) │ │ │ ├── attributes (kind: AttributeList) [] @@ -757,16 +758,17 @@ false │ │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] │ │ │ │ │ │ ├── token (kind: TokenNot): '!' │ │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] - │ │ │ │ │ └── arguments (kind: ArgListParenthesized) - │ │ │ │ │ ├── lparen (kind: TerminalLParen) - │ │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ │ │ ├── token (kind: TokenLParen): '(' - │ │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] - │ │ │ │ │ ├── arguments (kind: ArgList) [] - │ │ │ │ │ └── rparen (kind: TerminalRParen) - │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ │ ├── token (kind: TokenRParen): ')' - │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] + │ │ │ │ │ └── arguments (kind: TokenTreeNode) + │ │ │ │ │ └── subtree (kind: ParenthesizedTokenTree) + │ │ │ │ │ ├── lparen (kind: TerminalLParen) + │ │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ │ │ ├── token (kind: TokenLParen): '(' + │ │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] + │ │ │ │ │ ├── tokens (kind: TokenList) [] + │ │ │ │ │ └── rparen (kind: TerminalRParen) + │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ │ ├── token (kind: TokenRParen): ')' + │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] │ │ │ │ └── semicolon (kind: TerminalSemicolon) │ │ │ │ ├── leading_trivia (kind: Trivia) [] │ │ │ │ ├── token (kind: TokenSemicolon): ';' diff --git a/crates/cairo-lang-parser/src/parser_test_data/partial_trees/inline_macro b/crates/cairo-lang-parser/src/parser_test_data/partial_trees/inline_macro index 0ab9262ee9e..ea931358136 100644 --- a/crates/cairo-lang-parser/src/parser_test_data/partial_trees/inline_macro +++ b/crates/cairo-lang-parser/src/parser_test_data/partial_trees/inline_macro @@ -21,11 +21,10 @@ ExprInlineMacro │ └── item #0 (kind: PathSegmentSimple) │ └── ident (kind: TokenIdentifier): 'println' ├── bang (kind: TokenNot): '!' - └── arguments (kind: ArgListParenthesized) - ├── lparen (kind: TokenLParen): '(' - ├── arguments (kind: ArgList) - │ └── item #0 (kind: Arg) - │ ├── modifiers (kind: ModifierList) [] - │ └── arg_clause (kind: ArgClauseUnnamed) - │ └── value (kind: TokenShortString): ''foo'' - └── rparen (kind: TokenRParen): ')' + └── arguments (kind: TokenTreeNode) + └── subtree (kind: ParenthesizedTokenTree) + ├── lparen (kind: TokenLParen): '(' + ├── tokens (kind: TokenList) + │ └── child #0 (kind: TokenTreeLeaf) + │ └── leaf (kind: TokenShortString): ''foo'' + └── rparen (kind: TokenRParen): ')' diff --git a/crates/cairo-lang-parser/src/parser_test_data/partial_trees/item_inline_macro b/crates/cairo-lang-parser/src/parser_test_data/partial_trees/item_inline_macro index 91026c56063..7461cf3d59c 100644 --- a/crates/cairo-lang-parser/src/parser_test_data/partial_trees/item_inline_macro +++ b/crates/cairo-lang-parser/src/parser_test_data/partial_trees/item_inline_macro @@ -18,19 +18,17 @@ ItemInlineMacro ├── attributes (kind: AttributeList) [] ├── name (kind: TokenIdentifier): 'inline_macro' ├── bang (kind: TokenNot): '!' - ├── arguments (kind: ArgListParenthesized) - │ ├── lparen (kind: TokenLParen): '(' - │ ├── arguments (kind: ArgList) - │ │ ├── item #0 (kind: Arg) - │ │ │ ├── modifiers (kind: ModifierList) [] - │ │ │ └── arg_clause (kind: ArgClauseUnnamed) - │ │ │ └── value (kind: TokenLiteralNumber): '1' - │ │ ├── separator #0 (kind: TokenComma): ',' - │ │ └── item #1 (kind: Arg) - │ │ ├── modifiers (kind: ModifierList) [] - │ │ └── arg_clause (kind: ArgClauseUnnamed) - │ │ └── value (kind: TokenLiteralNumber): '2' - │ └── rparen (kind: TokenRParen): ')' + ├── arguments (kind: TokenTreeNode) + │ └── subtree (kind: ParenthesizedTokenTree) + │ ├── lparen (kind: TokenLParen): '(' + │ ├── tokens (kind: TokenList) + │ │ ├── child #0 (kind: TokenTreeLeaf) + │ │ │ └── leaf (kind: TokenLiteralNumber): '1' + │ │ ├── child #1 (kind: TokenTreeLeaf) + │ │ │ └── leaf (kind: TokenComma): ',' + │ │ └── child #2 (kind: TokenTreeLeaf) + │ │ └── leaf (kind: TokenLiteralNumber): '2' + │ └── rparen (kind: TokenRParen): ')' └── semicolon (kind: TokenSemicolon): ';' //! > ========================================================================== @@ -55,19 +53,17 @@ ItemInlineMacro ├── attributes (kind: AttributeList) [] ├── name (kind: TokenIdentifier): 'inline_macro' ├── bang (kind: TokenNot): '!' - ├── arguments (kind: ArgListBracketed) - │ ├── lbrack (kind: TokenLBrack): '[' - │ ├── arguments (kind: ArgList) - │ │ ├── item #0 (kind: Arg) - │ │ │ ├── modifiers (kind: ModifierList) [] - │ │ │ └── arg_clause (kind: ArgClauseUnnamed) - │ │ │ └── value (kind: TokenLiteralNumber): '1' - │ │ ├── separator #0 (kind: TokenComma): ',' - │ │ └── item #1 (kind: Arg) - │ │ ├── modifiers (kind: ModifierList) [] - │ │ └── arg_clause (kind: ArgClauseUnnamed) - │ │ └── value (kind: TokenLiteralNumber): '2' - │ └── rbrack (kind: TokenRBrack): ']' + ├── arguments (kind: TokenTreeNode) + │ └── subtree (kind: BracketedTokenTree) + │ ├── lbrack (kind: TokenLBrack): '[' + │ ├── tokens (kind: TokenList) + │ │ ├── child #0 (kind: TokenTreeLeaf) + │ │ │ └── leaf (kind: TokenLiteralNumber): '1' + │ │ ├── child #1 (kind: TokenTreeLeaf) + │ │ │ └── leaf (kind: TokenComma): ',' + │ │ └── child #2 (kind: TokenTreeLeaf) + │ │ └── leaf (kind: TokenLiteralNumber): '2' + │ └── rbrack (kind: TokenRBrack): ']' └── semicolon (kind: TokenSemicolon): ';' //! > ========================================================================== @@ -92,19 +88,17 @@ ItemInlineMacro ├── attributes (kind: AttributeList) [] ├── name (kind: TokenIdentifier): 'inline_macro' ├── bang (kind: TokenNot): '!' - ├── arguments (kind: ArgListBraced) - │ ├── lbrace (kind: TokenLBrace): '{' - │ ├── arguments (kind: ArgList) - │ │ ├── item #0 (kind: Arg) - │ │ │ ├── modifiers (kind: ModifierList) [] - │ │ │ └── arg_clause (kind: ArgClauseUnnamed) - │ │ │ └── value (kind: TokenLiteralNumber): '1' - │ │ ├── separator #0 (kind: TokenComma): ',' - │ │ └── item #1 (kind: Arg) - │ │ ├── modifiers (kind: ModifierList) [] - │ │ └── arg_clause (kind: ArgClauseUnnamed) - │ │ └── value (kind: TokenLiteralNumber): '2' - │ └── rbrace (kind: TokenRBrace): '}' + ├── arguments (kind: TokenTreeNode) + │ └── subtree (kind: BracedTokenTree) + │ ├── lbrace (kind: TokenLBrace): '{' + │ ├── tokens (kind: TokenList) + │ │ ├── child #0 (kind: TokenTreeLeaf) + │ │ │ └── leaf (kind: TokenLiteralNumber): '1' + │ │ ├── child #1 (kind: TokenTreeLeaf) + │ │ │ └── leaf (kind: TokenComma): ',' + │ │ └── child #2 (kind: TokenTreeLeaf) + │ │ └── leaf (kind: TokenLiteralNumber): '2' + │ └── rbrace (kind: TokenRBrace): '}' └── semicolon (kind: TokenSemicolon): ';' //! > ========================================================================== @@ -138,19 +132,17 @@ ItemInlineMacro │ └── rbrack (kind: TokenRBrack): ']' ├── name (kind: TokenIdentifier): 'inline_macro' ├── bang (kind: TokenNot): '!' - ├── arguments (kind: ArgListParenthesized) - │ ├── lparen (kind: TokenLParen): '(' - │ ├── arguments (kind: ArgList) - │ │ ├── item #0 (kind: Arg) - │ │ │ ├── modifiers (kind: ModifierList) [] - │ │ │ └── arg_clause (kind: ArgClauseUnnamed) - │ │ │ └── value (kind: TokenLiteralNumber): '1' - │ │ ├── separator #0 (kind: TokenComma): ',' - │ │ └── item #1 (kind: Arg) - │ │ ├── modifiers (kind: ModifierList) [] - │ │ └── arg_clause (kind: ArgClauseUnnamed) - │ │ └── value (kind: TokenLiteralNumber): '2' - │ └── rparen (kind: TokenRParen): ')' + ├── arguments (kind: TokenTreeNode) + │ └── subtree (kind: ParenthesizedTokenTree) + │ ├── lparen (kind: TokenLParen): '(' + │ ├── tokens (kind: TokenList) + │ │ ├── child #0 (kind: TokenTreeLeaf) + │ │ │ └── leaf (kind: TokenLiteralNumber): '1' + │ │ ├── child #1 (kind: TokenTreeLeaf) + │ │ │ └── leaf (kind: TokenComma): ',' + │ │ └── child #2 (kind: TokenTreeLeaf) + │ │ └── leaf (kind: TokenLiteralNumber): '2' + │ └── rparen (kind: TokenRParen): ')' └── semicolon (kind: TokenSemicolon): ';' //! > ========================================================================== @@ -180,19 +172,17 @@ inline_macro(1,2); ├── attributes (kind: AttributeList) [] ├── name (kind: TokenIdentifier): 'inline_macro' ├── bang: Missing - ├── arguments (kind: ArgListParenthesized) - │ ├── lparen (kind: TokenLParen): '(' - │ ├── arguments (kind: ArgList) - │ │ ├── item #0 (kind: Arg) - │ │ │ ├── modifiers (kind: ModifierList) [] - │ │ │ └── arg_clause (kind: ArgClauseUnnamed) - │ │ │ └── value (kind: TokenLiteralNumber): '1' - │ │ ├── separator #0 (kind: TokenComma): ',' - │ │ └── item #1 (kind: Arg) - │ │ ├── modifiers (kind: ModifierList) [] - │ │ └── arg_clause (kind: ArgClauseUnnamed) - │ │ └── value (kind: TokenLiteralNumber): '2' - │ └── rparen (kind: TokenRParen): ')' + ├── arguments (kind: TokenTreeNode) + │ └── subtree (kind: ParenthesizedTokenTree) + │ ├── lparen (kind: TokenLParen): '(' + │ ├── tokens (kind: TokenList) + │ │ ├── child #0 (kind: TokenTreeLeaf) + │ │ │ └── leaf (kind: TokenLiteralNumber): '1' + │ │ ├── child #1 (kind: TokenTreeLeaf) + │ │ │ └── leaf (kind: TokenComma): ',' + │ │ └── child #2 (kind: TokenTreeLeaf) + │ │ └── leaf (kind: TokenLiteralNumber): '2' + │ └── rparen (kind: TokenRParen): ')' └── semicolon (kind: TokenSemicolon): ';' //! > ========================================================================== @@ -222,19 +212,17 @@ inline_macro{1,2}; ├── attributes (kind: AttributeList) [] ├── name (kind: TokenIdentifier): 'inline_macro' ├── bang: Missing - ├── arguments (kind: ArgListBraced) - │ ├── lbrace (kind: TokenLBrace): '{' - │ ├── arguments (kind: ArgList) - │ │ ├── item #0 (kind: Arg) - │ │ │ ├── modifiers (kind: ModifierList) [] - │ │ │ └── arg_clause (kind: ArgClauseUnnamed) - │ │ │ └── value (kind: TokenLiteralNumber): '1' - │ │ ├── separator #0 (kind: TokenComma): ',' - │ │ └── item #1 (kind: Arg) - │ │ ├── modifiers (kind: ModifierList) [] - │ │ └── arg_clause (kind: ArgClauseUnnamed) - │ │ └── value (kind: TokenLiteralNumber): '2' - │ └── rbrace (kind: TokenRBrace): '}' + ├── arguments (kind: TokenTreeNode) + │ └── subtree (kind: BracedTokenTree) + │ ├── lbrace (kind: TokenLBrace): '{' + │ ├── tokens (kind: TokenList) + │ │ ├── child #0 (kind: TokenTreeLeaf) + │ │ │ └── leaf (kind: TokenLiteralNumber): '1' + │ │ ├── child #1 (kind: TokenTreeLeaf) + │ │ │ └── leaf (kind: TokenComma): ',' + │ │ └── child #2 (kind: TokenTreeLeaf) + │ │ └── leaf (kind: TokenLiteralNumber): '2' + │ └── rbrace (kind: TokenRBrace): '}' └── semicolon (kind: TokenSemicolon): ';' //! > ========================================================================== @@ -264,19 +252,17 @@ inline_macro[1,2]; ├── attributes (kind: AttributeList) [] ├── name (kind: TokenIdentifier): 'inline_macro' ├── bang: Missing - ├── arguments (kind: ArgListBracketed) - │ ├── lbrack (kind: TokenLBrack): '[' - │ ├── arguments (kind: ArgList) - │ │ ├── item #0 (kind: Arg) - │ │ │ ├── modifiers (kind: ModifierList) [] - │ │ │ └── arg_clause (kind: ArgClauseUnnamed) - │ │ │ └── value (kind: TokenLiteralNumber): '1' - │ │ ├── separator #0 (kind: TokenComma): ',' - │ │ └── item #1 (kind: Arg) - │ │ ├── modifiers (kind: ModifierList) [] - │ │ └── arg_clause (kind: ArgClauseUnnamed) - │ │ └── value (kind: TokenLiteralNumber): '2' - │ └── rbrack (kind: TokenRBrack): ']' + ├── arguments (kind: TokenTreeNode) + │ └── subtree (kind: BracketedTokenTree) + │ ├── lbrack (kind: TokenLBrack): '[' + │ ├── tokens (kind: TokenList) + │ │ ├── child #0 (kind: TokenTreeLeaf) + │ │ │ └── leaf (kind: TokenLiteralNumber): '1' + │ │ ├── child #1 (kind: TokenTreeLeaf) + │ │ │ └── leaf (kind: TokenComma): ',' + │ │ └── child #2 (kind: TokenTreeLeaf) + │ │ └── leaf (kind: TokenLiteralNumber): '2' + │ └── rbrack (kind: TokenRBrack): ']' └── semicolon (kind: TokenSemicolon): ';' //! > ========================================================================== @@ -357,7 +343,8 @@ macro! │ ├── attributes (kind: AttributeList) [] │ ├── name (kind: TokenIdentifier): 'macro' │ ├── bang (kind: TokenNot): '!' - │ ├── arguments: Missing [] + │ ├── arguments (kind: TokenTreeNode) + │ │ └── subtree (kind: WrappedTokenTreeMissing) [] │ └── semicolon: Missing └── child #1 (kind: FunctionWithBody) @@ -380,36 +367,47 @@ FunctionWithBody //! > expected_diagnostics error: Missing token TerminalRParen. - --> dummy_file.cairo:1:16 -identifier!(1,2 - ^ + --> dummy_file.cairo:2:12 +fn foo() {} + ^ error: Missing token TerminalSemicolon. - --> dummy_file.cairo:1:16 -identifier!(1,2 - ^ + --> dummy_file.cairo:2:12 +fn foo() {} + ^ //! > expected_tree └── Top level kind: ModuleItemList - ├── child #0 (kind: ItemInlineMacro) - │ ├── attributes (kind: AttributeList) [] - │ ├── name (kind: TokenIdentifier): 'identifier' - │ ├── bang (kind: TokenNot): '!' - │ ├── arguments (kind: ArgListParenthesized) - │ │ ├── lparen (kind: TokenLParen): '(' - │ │ ├── arguments (kind: ArgList) - │ │ │ ├── item #0 (kind: Arg) - │ │ │ │ ├── modifiers (kind: ModifierList) [] - │ │ │ │ └── arg_clause (kind: ArgClauseUnnamed) - │ │ │ │ └── value (kind: TokenLiteralNumber): '1' - │ │ │ ├── separator #0 (kind: TokenComma): ',' - │ │ │ └── item #1 (kind: Arg) - │ │ │ ├── modifiers (kind: ModifierList) [] - │ │ │ └── arg_clause (kind: ArgClauseUnnamed) - │ │ │ └── value (kind: TokenLiteralNumber): '2' - │ │ └── rparen: Missing - │ └── semicolon: Missing - └── child #1 (kind: FunctionWithBody) + └── child #0 (kind: ItemInlineMacro) + ├── attributes (kind: AttributeList) [] + ├── name (kind: TokenIdentifier): 'identifier' + ├── bang (kind: TokenNot): '!' + ├── arguments (kind: TokenTreeNode) + │ └── subtree (kind: ParenthesizedTokenTree) + │ ├── lparen (kind: TokenLParen): '(' + │ ├── tokens (kind: TokenList) + │ │ ├── child #0 (kind: TokenTreeLeaf) + │ │ │ └── leaf (kind: TokenLiteralNumber): '1' + │ │ ├── child #1 (kind: TokenTreeLeaf) + │ │ │ └── leaf (kind: TokenComma): ',' + │ │ ├── child #2 (kind: TokenTreeLeaf) + │ │ │ └── leaf (kind: TokenLiteralNumber): '2' + │ │ ├── child #3 (kind: TokenTreeLeaf) + │ │ │ └── leaf (kind: TokenFunction): 'fn' + │ │ ├── child #4 (kind: TokenTreeLeaf) + │ │ │ └── leaf (kind: TokenIdentifier): 'foo' + │ │ ├── child #5 (kind: TokenTreeNode) + │ │ │ └── subtree (kind: ParenthesizedTokenTree) + │ │ │ ├── lparen (kind: TokenLParen): '(' + │ │ │ ├── tokens (kind: TokenList) [] + │ │ │ └── rparen (kind: TokenRParen): ')' + │ │ └── child #6 (kind: TokenTreeNode) + │ │ └── subtree (kind: BracedTokenTree) + │ │ ├── lbrace (kind: TokenLBrace): '{' + │ │ ├── tokens (kind: TokenList) [] + │ │ └── rbrace (kind: TokenRBrace): '}' + │ └── rparen: Missing + └── semicolon: Missing //! > ========================================================================== @@ -440,19 +438,17 @@ identifier!(1,2) │ ├── attributes (kind: AttributeList) [] │ ├── name (kind: TokenIdentifier): 'identifier' │ ├── bang (kind: TokenNot): '!' - │ ├── arguments (kind: ArgListParenthesized) - │ │ ├── lparen (kind: TokenLParen): '(' - │ │ ├── arguments (kind: ArgList) - │ │ │ ├── item #0 (kind: Arg) - │ │ │ │ ├── modifiers (kind: ModifierList) [] - │ │ │ │ └── arg_clause (kind: ArgClauseUnnamed) - │ │ │ │ └── value (kind: TokenLiteralNumber): '1' - │ │ │ ├── separator #0 (kind: TokenComma): ',' - │ │ │ └── item #1 (kind: Arg) - │ │ │ ├── modifiers (kind: ModifierList) [] - │ │ │ └── arg_clause (kind: ArgClauseUnnamed) - │ │ │ └── value (kind: TokenLiteralNumber): '2' - │ │ └── rparen (kind: TokenRParen): ')' + │ ├── arguments (kind: TokenTreeNode) + │ │ └── subtree (kind: ParenthesizedTokenTree) + │ │ ├── lparen (kind: TokenLParen): '(' + │ │ ├── tokens (kind: TokenList) + │ │ │ ├── child #0 (kind: TokenTreeLeaf) + │ │ │ │ └── leaf (kind: TokenLiteralNumber): '1' + │ │ │ ├── child #1 (kind: TokenTreeLeaf) + │ │ │ │ └── leaf (kind: TokenComma): ',' + │ │ │ └── child #2 (kind: TokenTreeLeaf) + │ │ │ └── leaf (kind: TokenLiteralNumber): '2' + │ │ └── rparen (kind: TokenRParen): ')' │ └── semicolon: Missing └── child #1 (kind: FunctionWithBody) @@ -491,15 +487,12 @@ identifier(x) │ ├── attributes (kind: AttributeList) [] │ ├── name (kind: TokenIdentifier): 'identifier' │ ├── bang: Missing - │ ├── arguments (kind: ArgListParenthesized) - │ │ ├── lparen (kind: TokenLParen): '(' - │ │ ├── arguments (kind: ArgList) - │ │ │ └── item #0 (kind: Arg) - │ │ │ ├── modifiers (kind: ModifierList) [] - │ │ │ └── arg_clause (kind: ArgClauseUnnamed) - │ │ │ └── value (kind: ExprPath) - │ │ │ └── item #0 (kind: PathSegmentSimple) - │ │ │ └── ident (kind: TokenIdentifier): 'x' - │ │ └── rparen (kind: TokenRParen): ')' + │ ├── arguments (kind: TokenTreeNode) + │ │ └── subtree (kind: ParenthesizedTokenTree) + │ │ ├── lparen (kind: TokenLParen): '(' + │ │ ├── tokens (kind: TokenList) + │ │ │ └── child #0 (kind: TokenTreeLeaf) + │ │ │ └── leaf (kind: TokenIdentifier): 'x' + │ │ └── rparen (kind: TokenRParen): ')' │ └── semicolon: Missing └── child #1 (kind: FunctionWithBody) diff --git a/crates/cairo-lang-plugins/src/plugins/compile_error.rs b/crates/cairo-lang-plugins/src/plugins/compile_error.rs index c9573561e02..a05e1f4f5bf 100644 --- a/crates/cairo-lang-plugins/src/plugins/compile_error.rs +++ b/crates/cairo-lang-plugins/src/plugins/compile_error.rs @@ -1,6 +1,7 @@ use cairo_lang_defs::extract_macro_single_unnamed_arg; use cairo_lang_defs::plugin::{MacroPlugin, MacroPluginMetadata, PluginDiagnostic, PluginResult}; -use cairo_lang_defs::plugin_utils::PluginResultTrait; +use cairo_lang_defs::plugin_utils::{PluginResultTrait, not_legacy_macro_diagnostic}; +use cairo_lang_parser::macro_helpers::AsLegacyInlineMacro; use cairo_lang_syntax::node::db::SyntaxGroup; use cairo_lang_syntax::node::{Terminal, TypedSyntaxNode, ast}; @@ -17,22 +18,31 @@ impl MacroPlugin for CompileErrorPlugin { item_ast: ast::ModuleItem, _metadata: &MacroPluginMetadata<'_>, ) -> PluginResult { - if let ast::ModuleItem::InlineMacro(inline_macro_ast) = item_ast { - if inline_macro_ast.name(db).text(db) == "compile_error" { + let item_ast_ptr = item_ast.stable_ptr(); + if let ast::ModuleItem::InlineMacro(inline_macro_ast) = item_ast.clone() { + let Some(legacy_inline_macro_ast) = inline_macro_ast.as_legacy_inline_macro(db) else { + return PluginResult::diagnostic_only(not_legacy_macro_diagnostic( + inline_macro_ast.as_syntax_node().stable_ptr(), + )); + }; + if legacy_inline_macro_ast.name(db).text(db) == "compile_error" { let compilation_error_arg = extract_macro_single_unnamed_arg!( db, - &inline_macro_ast, - ast::WrappedArgList::ParenthesizedArgList(_) + &legacy_inline_macro_ast, + ast::WrappedArgList::ParenthesizedArgList(_), + &item_ast ); - let ast::Expr::String(err_message) = compilation_error_arg else { - return PluginResult::diagnostic_only(PluginDiagnostic::error( - &compilation_error_arg, + let ast::Expr::String(err_message) = compilation_error_arg.clone() else { + return PluginResult::diagnostic_only(PluginDiagnostic::error_with_inner_span( + db, + item_ast_ptr, + compilation_error_arg.as_syntax_node(), "`compiler_error!` argument must be an unnamed string argument." .to_string(), )); }; return PluginResult::diagnostic_only(PluginDiagnostic::error( - &inline_macro_ast, + item_ast_ptr, err_message.text(db).to_string(), )); } diff --git a/crates/cairo-lang-plugins/src/test_utils.rs b/crates/cairo-lang-plugins/src/test_utils.rs index 986855c4e25..7343669cd27 100644 --- a/crates/cairo-lang-plugins/src/test_utils.rs +++ b/crates/cairo-lang-plugins/src/test_utils.rs @@ -1,6 +1,7 @@ use cairo_lang_defs::db::DefsGroup; use cairo_lang_defs::ids::{LanguageElementId, ModuleId, ModuleItemId}; use cairo_lang_diagnostics::{DiagnosticLocation, Severity, format_diagnostics}; +use cairo_lang_filesystem::span::TextSpan; use cairo_lang_syntax::node::kind::SyntaxKind; use cairo_lang_syntax::node::{TypedStablePtr, TypedSyntaxNode, ast}; use cairo_lang_utils::unordered_hash_set::UnorderedHashSet; @@ -19,9 +20,16 @@ pub fn expand_module_text( // Collect the module diagnostics. for (file_id, diag) in db.module_plugin_diagnostics(module_id).unwrap().iter() { let syntax_node = diag.stable_ptr.lookup(syntax_db); - let location = DiagnosticLocation { - file_id: file_id.file_id(db.upcast()).unwrap(), - span: syntax_node.span_without_trivia(syntax_db), + let file_id = file_id.file_id(db.upcast()).unwrap(); + let location = match diag.inner_span { + Some((start, width)) => { + let start = syntax_node.offset().add_width(start); + let end = start.add_width(width); + DiagnosticLocation { file_id, span: TextSpan { start, end } } + } + None => { + DiagnosticLocation { file_id, span: syntax_node.span_without_trivia(db.upcast()) } + } }; diagnostics.push(format!( "{}: {}", diff --git a/crates/cairo-lang-semantic/src/db.rs b/crates/cairo-lang-semantic/src/db.rs index d1fcfa94982..ccc62f43910 100644 --- a/crates/cairo-lang-semantic/src/db.rs +++ b/crates/cairo-lang-semantic/src/db.rs @@ -1591,7 +1591,12 @@ fn module_semantic_diagnostics( let mut diagnostics = DiagnosticsBuilder::default(); for (_module_file_id, plugin_diag) in db.module_plugin_diagnostics(module_id)?.iter().cloned() { diagnostics.add(SemanticDiagnostic::new( - StableLocation::new(plugin_diag.stable_ptr), + match plugin_diag.inner_span { + None => StableLocation::new(plugin_diag.stable_ptr), + Some(inner_span) => { + StableLocation::with_inner_span(plugin_diag.stable_ptr, inner_span) + } + }, SemanticDiagnosticKind::PluginDiagnostic(plugin_diag), )); } diff --git a/crates/cairo-lang-semantic/src/diagnostic.rs b/crates/cairo-lang-semantic/src/diagnostic.rs index 290e343897b..893a546d530 100644 --- a/crates/cairo-lang-semantic/src/diagnostic.rs +++ b/crates/cairo-lang-semantic/src/diagnostic.rs @@ -11,6 +11,7 @@ use cairo_lang_diagnostics::{ DiagnosticAdded, DiagnosticEntry, DiagnosticLocation, DiagnosticsBuilder, ErrorCode, Severity, error_code, }; +use cairo_lang_filesystem::span::TextWidth; use cairo_lang_syntax as syntax; use itertools::Itertools; use smol_str::SmolStr; @@ -42,6 +43,14 @@ pub trait SemanticDiagnosticsBuilder { stable_ptr: impl Into, kind: SemanticDiagnosticKind, ) -> DiagnosticAdded; + /// Report a diagnostic in a sub-span of the location of the given ptr. The inner span is + /// specified by an offset from the start of the pointer location and a width. + fn report_with_inner_span( + &mut self, + stable_ptr: impl Into, + inner_span: (TextWidth, TextWidth), + kind: SemanticDiagnosticKind, + ) -> DiagnosticAdded; } impl SemanticDiagnosticsBuilder for SemanticDiagnostics { fn report( @@ -58,6 +67,17 @@ impl SemanticDiagnosticsBuilder for SemanticDiagnostics { ) -> DiagnosticAdded { self.add(SemanticDiagnostic::new_after(StableLocation::new(stable_ptr.into()), kind)) } + fn report_with_inner_span( + &mut self, + stable_ptr: impl Into, + inner_span: (TextWidth, TextWidth), + kind: SemanticDiagnosticKind, + ) -> DiagnosticAdded { + self.add(SemanticDiagnostic::new( + StableLocation::with_inner_span(stable_ptr.into(), inner_span), + kind, + )) + } } #[derive(Clone, Debug, Eq, Hash, PartialEq)] diff --git a/crates/cairo-lang-semantic/src/expr/compute.rs b/crates/cairo-lang-semantic/src/expr/compute.rs index 264a89a5782..424428e6e5d 100644 --- a/crates/cairo-lang-semantic/src/expr/compute.rs +++ b/crates/cairo-lang-semantic/src/expr/compute.rs @@ -463,8 +463,16 @@ fn compute_expr_inline_macro_semantic( }); let mut diag_added = None; for diagnostic in result.diagnostics { - diag_added = - Some(ctx.diagnostics.report(diagnostic.stable_ptr, PluginDiagnostic(diagnostic))); + diag_added = match diagnostic.inner_span { + None => { + Some(ctx.diagnostics.report(diagnostic.stable_ptr, PluginDiagnostic(diagnostic))) + } + Some((offset, width)) => Some(ctx.diagnostics.report_with_inner_span( + diagnostic.stable_ptr, + (offset, width), + PluginDiagnostic(diagnostic), + )), + } } let Some(code) = result.code else { diff --git a/crates/cairo-lang-semantic/src/expr/expansion_test_data/inline_macros b/crates/cairo-lang-semantic/src/expr/expansion_test_data/inline_macros index e27eb9b7f91..f1e7d9ec8c8 100644 --- a/crates/cairo-lang-semantic/src/expr/expansion_test_data/inline_macros +++ b/crates/cairo-lang-semantic/src/expr/expansion_test_data/inline_macros @@ -195,7 +195,7 @@ array![format!] array![format!] //! > diagnostics -error: Missing tokens. Expected an argument list wrapped in either parentheses, brackets, or braces. - --> lib.cairo:2:15 +error: Plugin diagnostic: Macro can not be parsed as legacy macro. Expected an argument list wrapped in either parentheses, brackets, or braces. + --> lib.cairo:2:1 array![format!] - ^ +^*************^ diff --git a/crates/cairo-lang-semantic/src/expr/test_data/inline_macros b/crates/cairo-lang-semantic/src/expr/test_data/inline_macros index 512e443013c..d8a4ae676ab 100644 --- a/crates/cairo-lang-semantic/src/expr/test_data/inline_macros +++ b/crates/cairo-lang-semantic/src/expr/test_data/inline_macros @@ -611,6 +611,11 @@ error: Plugin diagnostic: Unused argument. format!("{2}{0}", ba, 2, 1); ^ +error: Plugin diagnostic: Macro can not be parsed as legacy macro. Expected an argument list wrapped in either parentheses, brackets, or braces. + --> lib.cairo:32:5 + format!; + ^*****^ + warning[E0001]: Unused variable. Consider ignoring by prefixing with `_`. --> lib.cairo:2:9 let ba: ByteArray = "hello"; @@ -714,6 +719,11 @@ error: Plugin diagnostic: Unused argument. print!("{2}{0}", ba, 2, 1); ^ +error: Plugin diagnostic: Macro can not be parsed as legacy macro. Expected an argument list wrapped in either parentheses, brackets, or braces. + --> lib.cairo:32:5 + print!; + ^****^ + warning[E0001]: Unused variable. Consider ignoring by prefixing with `_`. --> lib.cairo:2:9 let ba: ByteArray = "hello"; @@ -817,6 +827,11 @@ error: Plugin diagnostic: Unused argument. println!("{2}{0}", ba, 2, 1); ^ +error: Plugin diagnostic: Macro can not be parsed as legacy macro. Expected an argument list wrapped in either parentheses, brackets, or braces. + --> lib.cairo:32:5 + println!; + ^******^ + warning[E0001]: Unused variable. Consider ignoring by prefixing with `_`. --> lib.cairo:2:9 let ba: ByteArray = "hello"; diff --git a/crates/cairo-lang-semantic/src/inline_macros/array.rs b/crates/cairo-lang-semantic/src/inline_macros/array.rs index 29b3b333df5..c68f7674c24 100644 --- a/crates/cairo-lang-semantic/src/inline_macros/array.rs +++ b/crates/cairo-lang-semantic/src/inline_macros/array.rs @@ -3,7 +3,10 @@ use cairo_lang_defs::plugin::{ InlineMacroExprPlugin, InlinePluginResult, MacroPluginMetadata, NamedPlugin, PluginGeneratedFile, }; -use cairo_lang_defs::plugin_utils::unsupported_bracket_diagnostic; +use cairo_lang_defs::plugin_utils::{ + PluginResultTrait, not_legacy_macro_diagnostic, unsupported_bracket_diagnostic, +}; +use cairo_lang_parser::macro_helpers::AsLegacyInlineMacro; use cairo_lang_syntax::node::db::SyntaxGroup; use cairo_lang_syntax::node::{TypedSyntaxNode, ast}; @@ -19,8 +22,13 @@ impl InlineMacroExprPlugin for ArrayMacro { syntax: &ast::ExprInlineMacro, _metadata: &MacroPluginMetadata<'_>, ) -> InlinePluginResult { - let ast::WrappedArgList::BracketedArgList(args) = syntax.arguments(db) else { - return unsupported_bracket_diagnostic(db, syntax); + let Some(legacy_inline_macro) = syntax.as_legacy_inline_macro(db) else { + return InlinePluginResult::diagnostic_only(not_legacy_macro_diagnostic( + syntax.as_syntax_node().stable_ptr(), + )); + }; + let ast::WrappedArgList::BracketedArgList(args) = legacy_inline_macro.arguments(db) else { + return unsupported_bracket_diagnostic(db, &legacy_inline_macro, syntax); }; let mut builder = PatchBuilder::new(db, syntax); builder.add_str( diff --git a/crates/cairo-lang-semantic/src/inline_macros/assert.rs b/crates/cairo-lang-semantic/src/inline_macros/assert.rs index ea576377c8e..0641101c55c 100644 --- a/crates/cairo-lang-semantic/src/inline_macros/assert.rs +++ b/crates/cairo-lang-semantic/src/inline_macros/assert.rs @@ -4,8 +4,10 @@ use cairo_lang_defs::plugin::{ PluginGeneratedFile, }; use cairo_lang_defs::plugin_utils::{ - escape_node, try_extract_unnamed_arg, unsupported_bracket_diagnostic, + PluginResultTrait, escape_node, not_legacy_macro_diagnostic, try_extract_unnamed_arg, + unsupported_bracket_diagnostic, }; +use cairo_lang_parser::macro_helpers::AsLegacyInlineMacro; use cairo_lang_syntax::node::ast::WrappedArgList; use cairo_lang_syntax::node::db::SyntaxGroup; use cairo_lang_syntax::node::{TypedStablePtr, TypedSyntaxNode, ast}; @@ -24,8 +26,15 @@ impl InlineMacroExprPlugin for AssertMacro { syntax: &ast::ExprInlineMacro, _metadata: &MacroPluginMetadata<'_>, ) -> InlinePluginResult { - let WrappedArgList::ParenthesizedArgList(arguments_syntax) = syntax.arguments(db) else { - return unsupported_bracket_diagnostic(db, syntax); + let Some(legacy_inline_macro) = syntax.as_legacy_inline_macro(db) else { + return InlinePluginResult::diagnostic_only(not_legacy_macro_diagnostic( + syntax.as_syntax_node().stable_ptr(), + )); + }; + let WrappedArgList::ParenthesizedArgList(arguments_syntax) = + legacy_inline_macro.arguments(db) + else { + return unsupported_bracket_diagnostic(db, &legacy_inline_macro, syntax); }; let arguments = arguments_syntax.arguments(db).elements(db); let Some((value, format_args)) = arguments.split_first() else { diff --git a/crates/cairo-lang-semantic/src/inline_macros/consteval_int.rs b/crates/cairo-lang-semantic/src/inline_macros/consteval_int.rs index 4825b1e33f8..f4a2a88d6c7 100644 --- a/crates/cairo-lang-semantic/src/inline_macros/consteval_int.rs +++ b/crates/cairo-lang-semantic/src/inline_macros/consteval_int.rs @@ -3,8 +3,10 @@ use cairo_lang_defs::plugin::{ InlineMacroExprPlugin, InlinePluginResult, MacroPluginMetadata, NamedPlugin, PluginDiagnostic, PluginGeneratedFile, }; +use cairo_lang_defs::plugin_utils::{PluginResultTrait, not_legacy_macro_diagnostic}; use cairo_lang_filesystem::ids::{CodeMapping, CodeOrigin}; use cairo_lang_filesystem::span::{TextOffset, TextSpan, TextWidth}; +use cairo_lang_parser::macro_helpers::AsLegacyInlineMacro; use cairo_lang_syntax::node::db::SyntaxGroup; use cairo_lang_syntax::node::{TypedStablePtr, TypedSyntaxNode, ast}; use num_bigint::BigInt; @@ -21,10 +23,16 @@ impl InlineMacroExprPlugin for ConstevalIntMacro { syntax: &ast::ExprInlineMacro, metadata: &MacroPluginMetadata<'_>, ) -> InlinePluginResult { + let Some(legacy_inline_macro) = syntax.clone().as_legacy_inline_macro(db) else { + return InlinePluginResult::diagnostic_only(not_legacy_macro_diagnostic( + syntax.as_syntax_node().stable_ptr(), + )); + }; let constant_expression = extract_macro_single_unnamed_arg!( db, - syntax, - ast::WrappedArgList::ParenthesizedArgList(_) + &legacy_inline_macro, + ast::WrappedArgList::ParenthesizedArgList(_), + syntax.stable_ptr() ); let mut diagnostics = vec![]; @@ -40,7 +48,7 @@ impl InlineMacroExprPlugin for ConstevalIntMacro { ), )); } - let code = compute_constant_expr(db, &constant_expression, &mut diagnostics); + let code = compute_constant_expr(db, &constant_expression, &mut diagnostics, syntax); InlinePluginResult { code: code.map(|x| { let content = x.to_string(); @@ -69,45 +77,48 @@ pub fn compute_constant_expr( db: &dyn SyntaxGroup, value: &ast::Expr, diagnostics: &mut Vec, + macro_ast: &ast::ExprInlineMacro, ) -> Option { match value { ast::Expr::Literal(lit) => lit.numeric_value(db), ast::Expr::Binary(bin_expr) => match bin_expr.op(db) { ast::BinaryOperator::Plus(_) => Some( - compute_constant_expr(db, &bin_expr.lhs(db), diagnostics)? - + compute_constant_expr(db, &bin_expr.rhs(db), diagnostics)?, + compute_constant_expr(db, &bin_expr.lhs(db), diagnostics, macro_ast)? + + compute_constant_expr(db, &bin_expr.rhs(db), diagnostics, macro_ast)?, ), ast::BinaryOperator::Mul(_) => Some( - compute_constant_expr(db, &bin_expr.lhs(db), diagnostics)? - * compute_constant_expr(db, &bin_expr.rhs(db), diagnostics)?, + compute_constant_expr(db, &bin_expr.lhs(db), diagnostics, macro_ast)? + * compute_constant_expr(db, &bin_expr.rhs(db), diagnostics, macro_ast)?, ), ast::BinaryOperator::Minus(_) => Some( - compute_constant_expr(db, &bin_expr.lhs(db), diagnostics)? - - compute_constant_expr(db, &bin_expr.rhs(db), diagnostics)?, + compute_constant_expr(db, &bin_expr.lhs(db), diagnostics, macro_ast)? + - compute_constant_expr(db, &bin_expr.rhs(db), diagnostics, macro_ast)?, ), ast::BinaryOperator::Div(_) => Some( - compute_constant_expr(db, &bin_expr.lhs(db), diagnostics)? - / compute_constant_expr(db, &bin_expr.rhs(db), diagnostics)?, + compute_constant_expr(db, &bin_expr.lhs(db), diagnostics, macro_ast)? + / compute_constant_expr(db, &bin_expr.rhs(db), diagnostics, macro_ast)?, ), ast::BinaryOperator::Mod(_) => Some( - compute_constant_expr(db, &bin_expr.lhs(db), diagnostics)? - % compute_constant_expr(db, &bin_expr.rhs(db), diagnostics)?, + compute_constant_expr(db, &bin_expr.lhs(db), diagnostics, macro_ast)? + % compute_constant_expr(db, &bin_expr.rhs(db), diagnostics, macro_ast)?, ), ast::BinaryOperator::And(_) => Some( - compute_constant_expr(db, &bin_expr.lhs(db), diagnostics)? - & compute_constant_expr(db, &bin_expr.rhs(db), diagnostics)?, + compute_constant_expr(db, &bin_expr.lhs(db), diagnostics, macro_ast)? + & compute_constant_expr(db, &bin_expr.rhs(db), diagnostics, macro_ast)?, ), ast::BinaryOperator::Or(_) => Some( - compute_constant_expr(db, &bin_expr.lhs(db), diagnostics)? - | compute_constant_expr(db, &bin_expr.rhs(db), diagnostics)?, + compute_constant_expr(db, &bin_expr.lhs(db), diagnostics, macro_ast)? + | compute_constant_expr(db, &bin_expr.rhs(db), diagnostics, macro_ast)?, ), ast::BinaryOperator::Xor(_) => Some( - compute_constant_expr(db, &bin_expr.lhs(db), diagnostics)? - ^ compute_constant_expr(db, &bin_expr.rhs(db), diagnostics)?, + compute_constant_expr(db, &bin_expr.lhs(db), diagnostics, macro_ast)? + ^ compute_constant_expr(db, &bin_expr.rhs(db), diagnostics, macro_ast)?, ), _ => { - diagnostics.push(PluginDiagnostic::error( - bin_expr.stable_ptr().untyped(), + diagnostics.push(PluginDiagnostic::error_with_inner_span( + db, + macro_ast.stable_ptr(), + bin_expr.as_syntax_node(), "Unsupported binary operator in consteval_int macro".to_string(), )); None @@ -115,22 +126,26 @@ pub fn compute_constant_expr( }, ast::Expr::Unary(un_expr) => match un_expr.op(db) { ast::UnaryOperator::Minus(_) => { - Some(-compute_constant_expr(db, &un_expr.expr(db), diagnostics)?) + Some(-compute_constant_expr(db, &un_expr.expr(db), diagnostics, macro_ast)?) } _ => { - diagnostics.push(PluginDiagnostic::error( - un_expr.stable_ptr().untyped(), + diagnostics.push(PluginDiagnostic::error_with_inner_span( + db, + macro_ast.stable_ptr(), + un_expr.as_syntax_node(), "Unsupported unary operator in consteval_int macro".to_string(), )); None } }, ast::Expr::Parenthesized(paren_expr) => { - compute_constant_expr(db, &paren_expr.expr(db), diagnostics) + compute_constant_expr(db, &paren_expr.expr(db), diagnostics, macro_ast) } _ => { - diagnostics.push(PluginDiagnostic::error( - value.stable_ptr().untyped(), + diagnostics.push(PluginDiagnostic::error_with_inner_span( + db, + macro_ast.stable_ptr(), + value.as_syntax_node(), "Unsupported expression in consteval_int macro".to_string(), )); None diff --git a/crates/cairo-lang-semantic/src/inline_macros/format.rs b/crates/cairo-lang-semantic/src/inline_macros/format.rs index 2abb09b4c08..2d9ddc06dc2 100644 --- a/crates/cairo-lang-semantic/src/inline_macros/format.rs +++ b/crates/cairo-lang-semantic/src/inline_macros/format.rs @@ -3,6 +3,8 @@ use cairo_lang_defs::plugin::{ InlineMacroExprPlugin, InlinePluginResult, MacroPluginMetadata, NamedPlugin, PluginGeneratedFile, }; +use cairo_lang_defs::plugin_utils::{PluginResultTrait, not_legacy_macro_diagnostic}; +use cairo_lang_parser::macro_helpers::AsLegacyInlineMacro; use cairo_lang_syntax::node::db::SyntaxGroup; use cairo_lang_syntax::node::helpers::WrappedArgListHelper; use cairo_lang_syntax::node::{TypedSyntaxNode, ast}; @@ -21,8 +23,13 @@ impl InlineMacroExprPlugin for FormatMacro { syntax: &ast::ExprInlineMacro, _metadata: &MacroPluginMetadata<'_>, ) -> InlinePluginResult { + let Some(syntax) = syntax.as_legacy_inline_macro(db) else { + return InlinePluginResult::diagnostic_only(not_legacy_macro_diagnostic( + syntax.as_syntax_node().stable_ptr(), + )); + }; let arguments = syntax.arguments(db); - let mut builder = PatchBuilder::new(db, syntax); + let mut builder = PatchBuilder::new(db, &syntax); builder.add_modified(RewriteNode::interpolate_patched( &formatdoc! { " diff --git a/crates/cairo-lang-semantic/src/inline_macros/panic.rs b/crates/cairo-lang-semantic/src/inline_macros/panic.rs index 4288a3fb07d..002cb31e786 100644 --- a/crates/cairo-lang-semantic/src/inline_macros/panic.rs +++ b/crates/cairo-lang-semantic/src/inline_macros/panic.rs @@ -3,7 +3,11 @@ use cairo_lang_defs::plugin::{ InlineMacroExprPlugin, InlinePluginResult, MacroPluginMetadata, NamedPlugin, PluginGeneratedFile, }; -use cairo_lang_defs::plugin_utils::{try_extract_unnamed_arg, unsupported_bracket_diagnostic}; +use cairo_lang_defs::plugin_utils::{ + PluginResultTrait, not_legacy_macro_diagnostic, try_extract_unnamed_arg, + unsupported_bracket_diagnostic, +}; +use cairo_lang_parser::macro_helpers::AsLegacyInlineMacro; use cairo_lang_syntax::node::ast::{Arg, WrappedArgList}; use cairo_lang_syntax::node::db::SyntaxGroup; use cairo_lang_syntax::node::{TypedSyntaxNode, ast}; @@ -78,8 +82,15 @@ impl InlineMacroExprPlugin for PanicMacro { syntax: &ast::ExprInlineMacro, _metadata: &MacroPluginMetadata<'_>, ) -> InlinePluginResult { - let WrappedArgList::ParenthesizedArgList(arguments_syntax) = syntax.arguments(db) else { - return unsupported_bracket_diagnostic(db, syntax); + let Some(legacy_inline_macro) = syntax.as_legacy_inline_macro(db) else { + return InlinePluginResult::diagnostic_only(not_legacy_macro_diagnostic( + syntax.as_syntax_node().stable_ptr(), + )); + }; + let WrappedArgList::ParenthesizedArgList(arguments_syntax) = + legacy_inline_macro.arguments(db) + else { + return unsupported_bracket_diagnostic(db, &legacy_inline_macro, syntax); }; let mut builder = PatchBuilder::new(db, syntax); diff --git a/crates/cairo-lang-semantic/src/inline_macros/print.rs b/crates/cairo-lang-semantic/src/inline_macros/print.rs index 22d953cbe30..65196852f6d 100644 --- a/crates/cairo-lang-semantic/src/inline_macros/print.rs +++ b/crates/cairo-lang-semantic/src/inline_macros/print.rs @@ -3,6 +3,8 @@ use cairo_lang_defs::plugin::{ InlineMacroExprPlugin, InlinePluginResult, MacroPluginMetadata, NamedPlugin, PluginGeneratedFile, }; +use cairo_lang_defs::plugin_utils::{PluginResultTrait, not_legacy_macro_diagnostic}; +use cairo_lang_parser::macro_helpers::AsLegacyInlineMacro; use cairo_lang_syntax::node::db::SyntaxGroup; use cairo_lang_syntax::node::helpers::WrappedArgListHelper; use cairo_lang_syntax::node::{TypedSyntaxNode, ast}; @@ -93,8 +95,13 @@ fn generate_code_inner( db: &dyn SyntaxGroup, with_newline: bool, ) -> InlinePluginResult { + let Some(syntax) = syntax.as_legacy_inline_macro(db) else { + return InlinePluginResult::diagnostic_only(not_legacy_macro_diagnostic( + syntax.as_syntax_node().stable_ptr(), + )); + }; let arguments = syntax.arguments(db); - let mut builder = PatchBuilder::new(db, syntax); + let mut builder = PatchBuilder::new(db, &syntax); builder.add_modified(RewriteNode::interpolate_patched( &formatdoc! { " diff --git a/crates/cairo-lang-semantic/src/inline_macros/write.rs b/crates/cairo-lang-semantic/src/inline_macros/write.rs index dcbbbbe1b2c..b06f0b83573 100644 --- a/crates/cairo-lang-semantic/src/inline_macros/write.rs +++ b/crates/cairo-lang-semantic/src/inline_macros/write.rs @@ -5,10 +5,13 @@ use cairo_lang_defs::plugin::{ InlineMacroExprPlugin, InlinePluginResult, MacroPluginMetadata, NamedPlugin, PluginDiagnostic, PluginGeneratedFile, }; -use cairo_lang_defs::plugin_utils::{try_extract_unnamed_arg, unsupported_bracket_diagnostic}; +use cairo_lang_defs::plugin_utils::{ + not_legacy_macro_diagnostic, try_extract_unnamed_arg, unsupported_bracket_diagnostic, +}; use cairo_lang_filesystem::span::{TextSpan, TextWidth}; +use cairo_lang_parser::macro_helpers::AsLegacyInlineMacro; use cairo_lang_syntax::node::db::SyntaxGroup; -use cairo_lang_syntax::node::{TypedStablePtr, TypedSyntaxNode, ast}; +use cairo_lang_syntax::node::{SyntaxNode, TypedSyntaxNode, ast}; use cairo_lang_utils::{OptionHelper, try_extract_matches}; use indoc::indoc; use num_bigint::{BigInt, Sign}; @@ -147,6 +150,7 @@ struct FormattingInfo { format_string: String, /// The positional arguments for the format string. args: Vec, + macro_ast: ast::ExprInlineMacro, } impl FormattingInfo { /// Extracts the arguments from a formatted string macro. @@ -154,56 +158,71 @@ impl FormattingInfo { db: &dyn SyntaxGroup, syntax: &ast::ExprInlineMacro, ) -> Result> { - let ast::WrappedArgList::ParenthesizedArgList(arguments) = syntax.arguments(db) else { - return Err(unsupported_bracket_diagnostic(db, syntax).diagnostics); + let Some(legacy_inline_macro) = syntax.as_legacy_inline_macro(db) else { + return Err(vec![not_legacy_macro_diagnostic(syntax.as_syntax_node().stable_ptr())]); + }; + let ast::WrappedArgList::ParenthesizedArgList(arguments) = + legacy_inline_macro.arguments(db) + else { + return Err( + unsupported_bracket_diagnostic(db, &legacy_inline_macro, syntax).diagnostics + ); }; let argument_list_elements = arguments.arguments(db).elements(db); let mut args_iter = argument_list_elements.iter(); + let error_with_inner_span = |inner_span: SyntaxNode, message: &str| { + PluginDiagnostic::error_with_inner_span( + db, + syntax.stable_ptr(), + inner_span, + message.to_string(), + ) + }; let Some(formatter_arg) = args_iter.next() else { - return Err(vec![PluginDiagnostic::error( - arguments.lparen(db).stable_ptr().untyped(), - "Macro expected formatter argument.".to_string(), + return Err(vec![error_with_inner_span( + arguments.lparen(db).as_syntax_node(), + "Macro expected formatter argument.", )]); }; let Some(formatter_expr) = try_extract_unnamed_arg(db, formatter_arg) else { - return Err(vec![PluginDiagnostic::error( - formatter_arg.stable_ptr().untyped(), - "Formatter argument must unnamed.".to_string(), + return Err(vec![error_with_inner_span( + formatter_arg.as_syntax_node(), + "Formatter argument must be unnamed.", )]); }; if matches!(formatter_expr, ast::Expr::String(_)) { - return Err(vec![PluginDiagnostic::error( - formatter_arg.stable_ptr().untyped(), - "Formatter argument must not be a string literal.".to_string(), + return Err(vec![error_with_inner_span( + formatter_arg.as_syntax_node(), + "Formatter argument must not be a string literal.", )]); } let Some(format_string_arg) = args_iter.next() else { - return Err(vec![PluginDiagnostic::error( - arguments.lparen(db).stable_ptr().untyped(), - "Macro expected format string argument.".to_string(), + return Err(vec![error_with_inner_span( + arguments.lparen(db).as_syntax_node(), + "Macro expected format string argument.", )]); }; let Some(format_string_expr) = try_extract_unnamed_arg(db, format_string_arg) else { - return Err(vec![PluginDiagnostic::error( - format_string_arg.stable_ptr().untyped(), - "Format string argument must be unnamed.".to_string(), + return Err(vec![error_with_inner_span( + format_string_arg.as_syntax_node(), + "Format string argument must be unnamed.", )]); }; let Some(format_string) = try_extract_matches!(format_string_expr, ast::Expr::String) .and_then(|arg| arg.string_value(db)) else { - return Err(vec![PluginDiagnostic::error( - format_string_arg.stable_ptr().untyped(), - "Format string argument must be a string literal.".to_string(), + return Err(vec![error_with_inner_span( + format_string_arg.as_syntax_node(), + "Format string argument must be a string literal.", )]); }; let mut diagnostics = vec![]; let args: Vec<_> = args_iter .filter_map(|arg| { try_extract_unnamed_arg(db, arg).on_none(|| { - diagnostics.push(PluginDiagnostic::error( - arg.stable_ptr().untyped(), - "Expected unnamed argument.".to_string(), + diagnostics.push(error_with_inner_span( + arg.as_syntax_node(), + "Argument must be unnamed.", )) }) }) @@ -217,6 +236,7 @@ impl FormattingInfo { // `unwrap` is ok because the above `on_none` ensures it's not None. format_string, args, + macro_ast: syntax.clone(), }) } @@ -256,8 +276,10 @@ impl FormattingInfo { let argument_info = match extract_placeholder_argument(&mut format_iter) { Ok(argument_info) => argument_info, Err(error_message) => { - diagnostics.push(PluginDiagnostic::error( - self.format_string_arg.as_syntax_node().stable_ptr(), + diagnostics.push(PluginDiagnostic::error_with_inner_span( + builder.db, + self.macro_ast.stable_ptr(), + self.format_string_arg.as_syntax_node(), format!("Invalid format string: {error_message}."), )); return; @@ -266,8 +288,10 @@ impl FormattingInfo { match argument_info.source { PlaceholderArgumentSource::Positional(positional) => { let Some(arg) = self.args.get(positional) else { - diagnostics.push(PluginDiagnostic::error( - self.format_string_arg.as_syntax_node().stable_ptr(), + diagnostics.push(PluginDiagnostic::error_with_inner_span( + builder.db, + self.macro_ast.stable_ptr(), + self.format_string_arg.as_syntax_node(), format!( "Invalid reference to positional argument {positional} (there \ are {} arguments).", @@ -331,8 +355,10 @@ impl FormattingInfo { pending_chars.push('}'); format_iter.next(); } else { - diagnostics.push(PluginDiagnostic::error( - self.format_string_arg.as_syntax_node().stable_ptr(), + diagnostics.push(PluginDiagnostic::error_with_inner_span( + builder.db, + self.macro_ast.stable_ptr(), + self.format_string_arg.as_syntax_node(), "Closing `}` without a matching `{`.".to_string(), )); } @@ -341,8 +367,10 @@ impl FormattingInfo { } } if missing_args > 0 { - diagnostics.push(PluginDiagnostic::error( - self.format_string_arg.as_syntax_node().stable_ptr(), + diagnostics.push(PluginDiagnostic::error_with_inner_span( + builder.db, + self.macro_ast.stable_ptr(), + self.format_string_arg.as_syntax_node(), format!( "{} positional arguments in format string, but only {} arguments.", self.args.len() + missing_args, @@ -371,8 +399,10 @@ impl FormattingInfo { builder.add_str("}\n"); for (position, used) in arg_used.into_iter().enumerate() { if !used { - diagnostics.push(PluginDiagnostic::error( - self.args[position].as_syntax_node().stable_ptr(), + diagnostics.push(PluginDiagnostic::error_with_inner_span( + builder.db, + self.macro_ast.stable_ptr(), + self.args[position].as_syntax_node(), "Unused argument.".to_string(), )); } diff --git a/crates/cairo-lang-starknet/Cargo.toml b/crates/cairo-lang-starknet/Cargo.toml index 1b20c82dbf8..83e31c0b05b 100644 --- a/crates/cairo-lang-starknet/Cargo.toml +++ b/crates/cairo-lang-starknet/Cargo.toml @@ -13,13 +13,16 @@ cairo-lang-defs = { path = "../cairo-lang-defs", version = "~2.8.4" } cairo-lang-diagnostics = { path = "../cairo-lang-diagnostics", version = "~2.8.4" } cairo-lang-filesystem = { path = "../cairo-lang-filesystem", version = "~2.8.4" } cairo-lang-lowering = { path = "../cairo-lang-lowering", version = "~2.8.4" } +cairo-lang-parser = { path = "../cairo-lang-parser", version = "~2.8.4" } cairo-lang-plugins = { path = "../cairo-lang-plugins", version = "~2.8.4" } cairo-lang-semantic = { path = "../cairo-lang-semantic", version = "~2.8.4" } cairo-lang-sierra = { path = "../cairo-lang-sierra", version = "~2.8.4" } cairo-lang-sierra-generator = { path = "../cairo-lang-sierra-generator", version = "~2.8.4" } cairo-lang-starknet-classes = { path = "../cairo-lang-starknet-classes", version = "~2.8.4" } cairo-lang-syntax = { path = "../cairo-lang-syntax", version = "~2.8.4" } -cairo-lang-utils = { path = "../cairo-lang-utils", version = "~2.8.4", features = ["serde"] } +cairo-lang-utils = { path = "../cairo-lang-utils", version = "~2.8.4", features = [ + "serde", +] } const_format.workspace = true indoc.workspace = true itertools = { workspace = true, default-features = true } @@ -34,8 +37,12 @@ indent.workspace = true cairo-lang-debug = { path = "../cairo-lang-debug" } cairo-lang-diagnostics = { path = "../cairo-lang-diagnostics" } cairo-lang-plugins = { path = "../cairo-lang-plugins", features = ["testing"] } -cairo-lang-semantic = { path = "../cairo-lang-semantic", features = ["testing"] } -cairo-lang-test-utils = { path = "../cairo-lang-test-utils", features = ["testing"] } +cairo-lang-semantic = { path = "../cairo-lang-semantic", features = [ + "testing", +] } +cairo-lang-test-utils = { path = "../cairo-lang-test-utils", features = [ + "testing", +] } env_logger.workspace = true pretty_assertions.workspace = true test-case.workspace = true diff --git a/crates/cairo-lang-starknet/src/inline_macros/get_dep_component.rs b/crates/cairo-lang-starknet/src/inline_macros/get_dep_component.rs index 3c0509b82e9..f49ddbea0e6 100644 --- a/crates/cairo-lang-starknet/src/inline_macros/get_dep_component.rs +++ b/crates/cairo-lang-starknet/src/inline_macros/get_dep_component.rs @@ -4,8 +4,10 @@ use cairo_lang_defs::plugin::{ InlineMacroExprPlugin, InlinePluginResult, MacroPluginMetadata, NamedPlugin, PluginDiagnostic, PluginGeneratedFile, }; +use cairo_lang_defs::plugin_utils::{PluginResultTrait, not_legacy_macro_diagnostic}; +use cairo_lang_parser::macro_helpers::AsLegacyInlineMacro; use cairo_lang_syntax::node::db::SyntaxGroup; -use cairo_lang_syntax::node::{TypedStablePtr, TypedSyntaxNode, ast}; +use cairo_lang_syntax::node::{TypedSyntaxNode, ast}; use cairo_lang_utils::extract_matches; /// Macro for getting a component given a contract state that has it. @@ -49,24 +51,38 @@ fn get_dep_component_generate_code_helper( syntax: &ast::ExprInlineMacro, is_mut: bool, ) -> InlinePluginResult { - let [contract_arg, component_impl_arg] = - extract_macro_unnamed_args!(db, syntax, 2, ast::WrappedArgList::ParenthesizedArgList(_)); + let Some(legacy_inline_macro) = syntax.as_legacy_inline_macro(db) else { + return InlinePluginResult::diagnostic_only(not_legacy_macro_diagnostic( + syntax.as_syntax_node().stable_ptr(), + )); + }; + let [contract_arg, component_impl_arg] = extract_macro_unnamed_args!( + db, + &legacy_inline_macro, + 2, + ast::WrappedArgList::ParenthesizedArgList(_), + syntax.stable_ptr() + ); if is_mut { // `extract_macro_unnamed_args` above guarantees that we have `ParenthesizedArgList`. - let contract_arg_modifiers = - extract_matches!(syntax.arguments(db), ast::WrappedArgList::ParenthesizedArgList) - .arguments(db) - .elements(db)[0] - .modifiers(db) - .elements(db); + let contract_arg_modifiers = extract_matches!( + legacy_inline_macro.arguments(db), + ast::WrappedArgList::ParenthesizedArgList + ) + .arguments(db) + .elements(db)[0] + .modifiers(db) + .elements(db); // Verify the first element has only a `ref` modifier. if !matches!(&contract_arg_modifiers[..], &[ast::Modifier::Ref(_)]) { // TODO(Gil): The generated diagnostics points to the whole inline macro, it should // point to the arg. - let diagnostics = vec![PluginDiagnostic::error( - contract_arg.stable_ptr().untyped(), + let diagnostics = vec![PluginDiagnostic::error_with_inner_span( + db, + syntax.stable_ptr(), + contract_arg.as_syntax_node(), format!( "The first argument of `{}` macro must have only a `ref` modifier.", GetDepComponentMutMacro::NAME diff --git a/crates/cairo-lang-starknet/src/inline_macros/selector.rs b/crates/cairo-lang-starknet/src/inline_macros/selector.rs index c2ca49daa89..13e76018e58 100644 --- a/crates/cairo-lang-starknet/src/inline_macros/selector.rs +++ b/crates/cairo-lang-starknet/src/inline_macros/selector.rs @@ -3,6 +3,8 @@ use cairo_lang_defs::plugin::{ InlineMacroExprPlugin, InlinePluginResult, MacroPluginMetadata, NamedPlugin, PluginDiagnostic, PluginGeneratedFile, }; +use cairo_lang_defs::plugin_utils::{PluginResultTrait, not_legacy_macro_diagnostic}; +use cairo_lang_parser::macro_helpers::AsLegacyInlineMacro; use cairo_lang_starknet_classes::keccak::starknet_keccak; use cairo_lang_syntax::node::db::SyntaxGroup; use cairo_lang_syntax::node::{TypedStablePtr, TypedSyntaxNode, ast}; @@ -20,10 +22,16 @@ impl InlineMacroExprPlugin for SelectorMacro { syntax: &ast::ExprInlineMacro, _metadata: &MacroPluginMetadata<'_>, ) -> InlinePluginResult { + let Some(legacy_inline_macro) = syntax.as_legacy_inline_macro(db) else { + return InlinePluginResult::diagnostic_only(not_legacy_macro_diagnostic( + syntax.as_syntax_node().stable_ptr(), + )); + }; let arg = extract_macro_single_unnamed_arg!( db, - syntax, - ast::WrappedArgList::ParenthesizedArgList(_) + &legacy_inline_macro, + ast::WrappedArgList::ParenthesizedArgList(_), + syntax.stable_ptr() ); let ast::Expr::String(input_string) = arg else { diff --git a/crates/cairo-lang-starknet/src/plugin/starknet_module/contract.rs b/crates/cairo-lang-starknet/src/plugin/starknet_module/contract.rs index e9839da6f0b..dd78370f081 100644 --- a/crates/cairo-lang-starknet/src/plugin/starknet_module/contract.rs +++ b/crates/cairo-lang-starknet/src/plugin/starknet_module/contract.rs @@ -1,11 +1,14 @@ use cairo_lang_defs::patcher::RewriteNode; use cairo_lang_defs::plugin::{MacroPluginMetadata, PluginDiagnostic, PluginResult}; +use cairo_lang_defs::plugin_utils::not_legacy_macro_diagnostic; +use cairo_lang_parser::macro_helpers::AsLegacyInlineMacro; use cairo_lang_plugins::plugins::HasItemsInCfgEx; use cairo_lang_starknet_classes::keccak::starknet_keccak; use cairo_lang_syntax::node::db::SyntaxGroup; use cairo_lang_syntax::node::helpers::{ GetIdentifier, PathSegmentEx, QueryAttrs, is_single_arg_attr, }; +use cairo_lang_syntax::node::ids::SyntaxStablePtrId; use cairo_lang_syntax::node::{Terminal, TypedStablePtr, TypedSyntaxNode, ast}; use const_format::formatcp; use indoc::formatdoc; @@ -62,7 +65,7 @@ impl ComponentsGenerationData { for NestedComponent { component_path, storage_name, event_name, node } in self.components.iter() { - if !self.validate_component(db, diagnostics, storage_name, event_name) { + if !self.validate_component(db, diagnostics, storage_name, node, event_name) { // Don't generate the code for the impl of HasComponent. continue; } @@ -137,14 +140,17 @@ impl ComponentsGenerationData { db: &dyn SyntaxGroup, diagnostics: &mut Vec, storage_name: &ast::ExprPath, + component_macro: &ast::ItemInlineMacro, event_name: &ast::ExprPath, ) -> bool { let mut is_valid = true; let storage_name_syntax_node = storage_name.as_syntax_node(); if !self.substorage_members.contains(&storage_name_syntax_node.get_text(db)) { - diagnostics.push(PluginDiagnostic::error( - storage_name.stable_ptr().untyped(), + diagnostics.push(PluginDiagnostic::error_with_inner_span( + db, + component_macro.stable_ptr().untyped(), + storage_name.as_syntax_node(), format!( "`{0}` is not a substorage member in the contract's \ `{STORAGE_STRUCT_NAME}`.\nConsider adding to \ @@ -159,8 +165,10 @@ impl ComponentsGenerationData { let event_name_str = event_name.as_syntax_node().get_text_without_trivia(db); if !self.nested_event_variants.contains(&event_name_str.clone().into()) { - diagnostics.push(PluginDiagnostic::error( - event_name.stable_ptr().untyped(), + diagnostics.push(PluginDiagnostic::error_with_inner_span( + db, + component_macro.stable_ptr().untyped(), + event_name.as_syntax_node(), format!( "`{event_name_str}` is not a nested event in the contract's \ `{EVENT_TYPE_NAME}` enum.\nConsider adding to the `{EVENT_TYPE_NAME}` \ @@ -575,7 +583,12 @@ pub fn handle_component_inline_macro( component_macro_ast: &ast::ItemInlineMacro, data: &mut ContractSpecificGenerationData, ) { - let macro_args = match component_macro_ast.arguments(db) { + let Some(legacy_component_macro_ast) = component_macro_ast.as_legacy_inline_macro(db) else { + diagnostics + .push(not_legacy_macro_diagnostic(component_macro_ast.as_syntax_node().stable_ptr())); + return; + }; + let macro_args = match legacy_component_macro_ast.arguments(db) { ast::WrappedArgList::ParenthesizedArgList(args) => args.arguments(db), _ => { diagnostics.push(invalid_macro_diagnostic(component_macro_ast)); @@ -589,9 +602,30 @@ pub fn handle_component_inline_macro( }; let (Some(component_path), Some(storage_name), Some(event_name)) = ( - try_extract_named_macro_argument(db, diagnostics, path_arg, "path", false), - try_extract_named_macro_argument(db, diagnostics, storage_arg, "storage", true), - try_extract_named_macro_argument(db, diagnostics, event_arg, "event", true), + try_extract_named_macro_argument( + db, + diagnostics, + path_arg, + "path", + false, + component_macro_ast, + ), + try_extract_named_macro_argument( + db, + diagnostics, + storage_arg, + "storage", + true, + component_macro_ast, + ), + try_extract_named_macro_argument( + db, + diagnostics, + event_arg, + "event", + true, + component_macro_ast, + ), ) else { return; }; @@ -660,6 +694,7 @@ fn try_extract_named_macro_argument( arg_ast: &ast::Arg, arg_name: &str, only_simple_identifier: bool, + component_macro_stable_ptr: impl Into, ) -> Option { match arg_ast.arg_clause(db) { ast::ArgClause::Named(clause) if clause.name(db).text(db) == arg_name => { @@ -672,8 +707,10 @@ fn try_extract_named_macro_argument( if elements.len() != 1 || !matches!(elements.last().unwrap(), ast::PathSegment::Simple(_)) { - diagnostics.push(PluginDiagnostic::error( - path.stable_ptr().untyped(), + diagnostics.push(PluginDiagnostic::error_with_inner_span( + db, + component_macro_stable_ptr, + path.as_syntax_node(), format!( "Component macro argument `{arg_name}` must be a simple \ identifier.", @@ -684,8 +721,10 @@ fn try_extract_named_macro_argument( Some(path) } value => { - diagnostics.push(PluginDiagnostic::error( - value.stable_ptr().untyped(), + diagnostics.push(PluginDiagnostic::error_with_inner_span( + db, + component_macro_stable_ptr, + value.as_syntax_node(), format!( "Component macro argument `{arg_name}` must be a path expression.", ), @@ -695,8 +734,10 @@ fn try_extract_named_macro_argument( } } _ => { - diagnostics.push(PluginDiagnostic::error( - arg_ast.stable_ptr().untyped(), + diagnostics.push(PluginDiagnostic::error_with_inner_span( + db, + component_macro_stable_ptr, + arg_ast.as_syntax_node(), format!("Invalid component macro argument. Expected `{0}: `", arg_name), )); None diff --git a/crates/cairo-lang-syntax-codegen/src/cairo_spec.rs b/crates/cairo-lang-syntax-codegen/src/cairo_spec.rs index 35c5ed76c60..7ecb667d07f 100644 --- a/crates/cairo-lang-syntax-codegen/src/cairo_spec.rs +++ b/crates/cairo-lang-syntax-codegen/src/cairo_spec.rs @@ -217,11 +217,6 @@ pub fn get_spec() -> Vec { .node("index_expr", "Expr") .node("rbrack", "TerminalRBrack") ) - .add_struct(StructBuilder::new("ExprInlineMacro") - .node("path", "ExprPath") - .node("bang", "TerminalNot") - .node("arguments", "WrappedArgList") - ) .add_struct(StructBuilder::new("ExprFixedSizeArray") .node("lbrack", "TerminalLBrack") .node("exprs", "ExprList") @@ -616,12 +611,6 @@ pub fn get_spec() -> Vec { .node("of_kw", "TerminalOf") .node("trait_path", "ExprPath") .node("body", "MaybeImplBody") - ).add_struct(StructBuilder::new("ItemInlineMacro") - .node("attributes" ,"AttributeList") - .node("name", "TerminalIdentifier") - .node("bang", "TerminalNot") - .node("arguments", "WrappedArgList") - .node("semicolon", "TerminalSemicolon") ) // Empty struct, which is used for separating the first comments in a file or module from the // rest of the items. This is needed to prevent the first comments from being moved by the @@ -793,6 +782,70 @@ pub fn get_spec() -> Vec { .node("minus", "TerminalMinus") .node("trait_path", "ExprPath") ) + // --- Macros --- + // The structure of a token tree is: + // TokenTree: TokenNode | WrappedTokenTree + // Where WrappedTokenTree is: + // WrappedTokenTree: (TokenTree*) | {TokenTree*} | [TokenTree*] + // TokenTree* is represented as TokenList. + .add_list("TokenList", "TokenTree") + .add_struct(StructBuilder::new("TokenTreeLeaf") + .node("leaf", "TokenNode") + ) + .add_struct(StructBuilder::new("TokenTreeNode") + .node("subtree", "WrappedTokenTree") + ) + .add_enum(EnumBuilder::new("TokenTree") + .missing("Missing") + .node_with_explicit_kind("Token", "TokenTreeLeaf") + .node_with_explicit_kind("Subtree", "TokenTreeNode") + ) + .add_struct(StructBuilder::new("TokenTreeMissing")) + .add_enum(EnumBuilder::new("WrappedTokenTree") + .missing("Missing") + .node_with_explicit_kind("Parenthesized", "ParenthesizedTokenTree") + .node_with_explicit_kind("Braced", "BracedTokenTree") + .node_with_explicit_kind("Bracketed", "BracketedTokenTree") + ) + .add_struct(StructBuilder::new("WrappedTokenTreeMissing")) + .add_struct(StructBuilder::new("ParenthesizedTokenTree") + .node("lparen", "TerminalLParen") + .node("tokens", "TokenList") + .node("rparen", "TerminalRParen") + ) + .add_struct(StructBuilder::new("BracedTokenTree") + .node("lbrace", "TerminalLBrace") + .node("tokens", "TokenList") + .node("rbrace", "TerminalRBrace") + ) + .add_struct(StructBuilder::new("BracketedTokenTree") + .node("lbrack", "TerminalLBrack") + .node("tokens", "TokenList") + .node("rbrack", "TerminalRBrack") + ) + .add_struct(StructBuilder::new("ExprInlineMacro") + .node("path", "ExprPath") + .node("bang", "TerminalNot") + .node("arguments", "TokenTreeNode") + ) + .add_struct(StructBuilder::new("ItemInlineMacro") + .node("attributes" ,"AttributeList") + .node("name", "TerminalIdentifier") + .node("bang", "TerminalNot") + .node("arguments", "TokenTreeNode") + .node("semicolon", "TerminalSemicolon")) + // TODO + .add_struct(StructBuilder::new("LegacyExprInlineMacro") + .node("path", "ExprPath") + .node("bang", "TerminalNot") + .node("arguments", "WrappedArgList") + ) + .add_struct(StructBuilder::new("LegacyItemInlineMacro") + .node("attributes" ,"AttributeList") + .node("name", "TerminalIdentifier") + .node("bang", "TerminalNot") + .node("arguments", "WrappedArgList") + .node("semicolon", "TerminalSemicolon")) // --- Skipped nodes --- // A wrapper for the skipped node enum as the enum nodes (Trivium) can't have enum as a child directly. .add_struct(StructBuilder::new("TriviumSkippedNode") diff --git a/crates/cairo-lang-syntax/src/node/ast.rs b/crates/cairo-lang-syntax/src/node/ast.rs index 97c858f9fa2..d1cdf0ca3d4 100644 --- a/crates/cairo-lang-syntax/src/node/ast.rs +++ b/crates/cairo-lang-syntax/src/node/ast.rs @@ -5150,106 +5150,6 @@ impl From<&ExprIndexed> for SyntaxStablePtrId { } } #[derive(Clone, Debug, Eq, Hash, PartialEq)] -pub struct ExprInlineMacro { - node: SyntaxNode, - children: Arc<[SyntaxNode]>, -} -impl ExprInlineMacro { - pub const INDEX_PATH: usize = 0; - pub const INDEX_BANG: usize = 1; - pub const INDEX_ARGUMENTS: usize = 2; - pub fn new_green( - db: &dyn SyntaxGroup, - path: ExprPathGreen, - bang: TerminalNotGreen, - arguments: WrappedArgListGreen, - ) -> ExprInlineMacroGreen { - let children: Vec = vec![path.0, bang.0, arguments.0]; - let width = children.iter().copied().map(|id| id.lookup_intern(db).width()).sum(); - ExprInlineMacroGreen( - Arc::new(GreenNode { - kind: SyntaxKind::ExprInlineMacro, - details: GreenNodeDetails::Node { children, width }, - }) - .intern(db), - ) - } -} -impl ExprInlineMacro { - pub fn path(&self, db: &dyn SyntaxGroup) -> ExprPath { - ExprPath::from_syntax_node(db, self.children[0].clone()) - } - pub fn bang(&self, db: &dyn SyntaxGroup) -> TerminalNot { - TerminalNot::from_syntax_node(db, self.children[1].clone()) - } - pub fn arguments(&self, db: &dyn SyntaxGroup) -> WrappedArgList { - WrappedArgList::from_syntax_node(db, self.children[2].clone()) - } -} -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct ExprInlineMacroPtr(pub SyntaxStablePtrId); -impl ExprInlineMacroPtr {} -impl TypedStablePtr for ExprInlineMacroPtr { - type SyntaxNode = ExprInlineMacro; - fn untyped(&self) -> SyntaxStablePtrId { - self.0 - } - fn lookup(&self, db: &dyn SyntaxGroup) -> ExprInlineMacro { - ExprInlineMacro::from_syntax_node(db, self.0.lookup(db)) - } -} -impl From for SyntaxStablePtrId { - fn from(ptr: ExprInlineMacroPtr) -> Self { - ptr.untyped() - } -} -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct ExprInlineMacroGreen(pub GreenId); -impl TypedSyntaxNode for ExprInlineMacro { - const OPTIONAL_KIND: Option = Some(SyntaxKind::ExprInlineMacro); - type StablePtr = ExprInlineMacroPtr; - type Green = ExprInlineMacroGreen; - fn missing(db: &dyn SyntaxGroup) -> Self::Green { - ExprInlineMacroGreen( - Arc::new(GreenNode { - kind: SyntaxKind::ExprInlineMacro, - details: GreenNodeDetails::Node { - children: vec![ - ExprPath::missing(db).0, - TerminalNot::missing(db).0, - WrappedArgList::missing(db).0, - ], - width: TextWidth::default(), - }, - }) - .intern(db), - ) - } - fn from_syntax_node(db: &dyn SyntaxGroup, node: SyntaxNode) -> Self { - let kind = node.kind(db); - assert_eq!( - kind, - SyntaxKind::ExprInlineMacro, - "Unexpected SyntaxKind {:?}. Expected {:?}.", - kind, - SyntaxKind::ExprInlineMacro - ); - let children = db.get_children(node.clone()); - Self { node, children } - } - fn as_syntax_node(&self) -> SyntaxNode { - self.node.clone() - } - fn stable_ptr(&self) -> Self::StablePtr { - ExprInlineMacroPtr(self.node.0.stable_ptr) - } -} -impl From<&ExprInlineMacro> for SyntaxStablePtrId { - fn from(node: &ExprInlineMacro) -> Self { - node.stable_ptr().untyped() - } -} -#[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct ExprFixedSizeArray { node: SyntaxNode, children: Arc<[SyntaxNode]>, @@ -15049,118 +14949,6 @@ impl From<&ItemImpl> for SyntaxStablePtrId { } } #[derive(Clone, Debug, Eq, Hash, PartialEq)] -pub struct ItemInlineMacro { - node: SyntaxNode, - children: Arc<[SyntaxNode]>, -} -impl ItemInlineMacro { - pub const INDEX_ATTRIBUTES: usize = 0; - pub const INDEX_NAME: usize = 1; - pub const INDEX_BANG: usize = 2; - pub const INDEX_ARGUMENTS: usize = 3; - pub const INDEX_SEMICOLON: usize = 4; - pub fn new_green( - db: &dyn SyntaxGroup, - attributes: AttributeListGreen, - name: TerminalIdentifierGreen, - bang: TerminalNotGreen, - arguments: WrappedArgListGreen, - semicolon: TerminalSemicolonGreen, - ) -> ItemInlineMacroGreen { - let children: Vec = vec![attributes.0, name.0, bang.0, arguments.0, semicolon.0]; - let width = children.iter().copied().map(|id| id.lookup_intern(db).width()).sum(); - ItemInlineMacroGreen( - Arc::new(GreenNode { - kind: SyntaxKind::ItemInlineMacro, - details: GreenNodeDetails::Node { children, width }, - }) - .intern(db), - ) - } -} -impl ItemInlineMacro { - pub fn attributes(&self, db: &dyn SyntaxGroup) -> AttributeList { - AttributeList::from_syntax_node(db, self.children[0].clone()) - } - pub fn name(&self, db: &dyn SyntaxGroup) -> TerminalIdentifier { - TerminalIdentifier::from_syntax_node(db, self.children[1].clone()) - } - pub fn bang(&self, db: &dyn SyntaxGroup) -> TerminalNot { - TerminalNot::from_syntax_node(db, self.children[2].clone()) - } - pub fn arguments(&self, db: &dyn SyntaxGroup) -> WrappedArgList { - WrappedArgList::from_syntax_node(db, self.children[3].clone()) - } - pub fn semicolon(&self, db: &dyn SyntaxGroup) -> TerminalSemicolon { - TerminalSemicolon::from_syntax_node(db, self.children[4].clone()) - } -} -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct ItemInlineMacroPtr(pub SyntaxStablePtrId); -impl ItemInlineMacroPtr {} -impl TypedStablePtr for ItemInlineMacroPtr { - type SyntaxNode = ItemInlineMacro; - fn untyped(&self) -> SyntaxStablePtrId { - self.0 - } - fn lookup(&self, db: &dyn SyntaxGroup) -> ItemInlineMacro { - ItemInlineMacro::from_syntax_node(db, self.0.lookup(db)) - } -} -impl From for SyntaxStablePtrId { - fn from(ptr: ItemInlineMacroPtr) -> Self { - ptr.untyped() - } -} -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct ItemInlineMacroGreen(pub GreenId); -impl TypedSyntaxNode for ItemInlineMacro { - const OPTIONAL_KIND: Option = Some(SyntaxKind::ItemInlineMacro); - type StablePtr = ItemInlineMacroPtr; - type Green = ItemInlineMacroGreen; - fn missing(db: &dyn SyntaxGroup) -> Self::Green { - ItemInlineMacroGreen( - Arc::new(GreenNode { - kind: SyntaxKind::ItemInlineMacro, - details: GreenNodeDetails::Node { - children: vec![ - AttributeList::missing(db).0, - TerminalIdentifier::missing(db).0, - TerminalNot::missing(db).0, - WrappedArgList::missing(db).0, - TerminalSemicolon::missing(db).0, - ], - width: TextWidth::default(), - }, - }) - .intern(db), - ) - } - fn from_syntax_node(db: &dyn SyntaxGroup, node: SyntaxNode) -> Self { - let kind = node.kind(db); - assert_eq!( - kind, - SyntaxKind::ItemInlineMacro, - "Unexpected SyntaxKind {:?}. Expected {:?}.", - kind, - SyntaxKind::ItemInlineMacro - ); - let children = db.get_children(node.clone()); - Self { node, children } - } - fn as_syntax_node(&self) -> SyntaxNode { - self.node.clone() - } - fn stable_ptr(&self) -> Self::StablePtr { - ItemInlineMacroPtr(self.node.0.stable_ptr) - } -} -impl From<&ItemInlineMacro> for SyntaxStablePtrId { - fn from(node: &ItemInlineMacro) -> Self { - node.stable_ptr().untyped() - } -} -#[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct ItemHeaderDoc { node: SyntaxNode, children: Arc<[SyntaxNode]>, @@ -18904,6 +18692,1338 @@ impl From<&GenericParamNegativeImpl> for SyntaxStablePtrId { } } #[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct TokenList(ElementList); +impl Deref for TokenList { + type Target = ElementList; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl TokenList { + pub fn new_green(db: &dyn SyntaxGroup, children: Vec) -> TokenListGreen { + let width = children.iter().map(|id| id.0.lookup_intern(db).width()).sum(); + TokenListGreen( + Arc::new(GreenNode { + kind: SyntaxKind::TokenList, + details: GreenNodeDetails::Node { + children: children.iter().map(|x| x.0).collect(), + width, + }, + }) + .intern(db), + ) + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct TokenListPtr(pub SyntaxStablePtrId); +impl TypedStablePtr for TokenListPtr { + type SyntaxNode = TokenList; + fn untyped(&self) -> SyntaxStablePtrId { + self.0 + } + fn lookup(&self, db: &dyn SyntaxGroup) -> TokenList { + TokenList::from_syntax_node(db, self.0.lookup(db)) + } +} +impl From for SyntaxStablePtrId { + fn from(ptr: TokenListPtr) -> Self { + ptr.untyped() + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct TokenListGreen(pub GreenId); +impl TypedSyntaxNode for TokenList { + const OPTIONAL_KIND: Option = Some(SyntaxKind::TokenList); + type StablePtr = TokenListPtr; + type Green = TokenListGreen; + fn missing(db: &dyn SyntaxGroup) -> Self::Green { + TokenListGreen( + Arc::new(GreenNode { + kind: SyntaxKind::TokenList, + details: GreenNodeDetails::Node { children: vec![], width: TextWidth::default() }, + }) + .intern(db), + ) + } + fn from_syntax_node(db: &dyn SyntaxGroup, node: SyntaxNode) -> Self { + Self(ElementList::new(node)) + } + fn as_syntax_node(&self) -> SyntaxNode { + self.node.clone() + } + fn stable_ptr(&self) -> Self::StablePtr { + TokenListPtr(self.node.0.stable_ptr) + } +} +impl From<&TokenList> for SyntaxStablePtrId { + fn from(node: &TokenList) -> Self { + node.stable_ptr().untyped() + } +} +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct TokenTreeLeaf { + node: SyntaxNode, + children: Arc<[SyntaxNode]>, +} +impl TokenTreeLeaf { + pub const INDEX_LEAF: usize = 0; + pub fn new_green(db: &dyn SyntaxGroup, leaf: TokenNodeGreen) -> TokenTreeLeafGreen { + let children: Vec = vec![leaf.0]; + let width = children.iter().copied().map(|id| id.lookup_intern(db).width()).sum(); + TokenTreeLeafGreen( + Arc::new(GreenNode { + kind: SyntaxKind::TokenTreeLeaf, + details: GreenNodeDetails::Node { children, width }, + }) + .intern(db), + ) + } +} +impl TokenTreeLeaf { + pub fn leaf(&self, db: &dyn SyntaxGroup) -> TokenNode { + TokenNode::from_syntax_node(db, self.children[0].clone()) + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct TokenTreeLeafPtr(pub SyntaxStablePtrId); +impl TokenTreeLeafPtr {} +impl TypedStablePtr for TokenTreeLeafPtr { + type SyntaxNode = TokenTreeLeaf; + fn untyped(&self) -> SyntaxStablePtrId { + self.0 + } + fn lookup(&self, db: &dyn SyntaxGroup) -> TokenTreeLeaf { + TokenTreeLeaf::from_syntax_node(db, self.0.lookup(db)) + } +} +impl From for SyntaxStablePtrId { + fn from(ptr: TokenTreeLeafPtr) -> Self { + ptr.untyped() + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct TokenTreeLeafGreen(pub GreenId); +impl TypedSyntaxNode for TokenTreeLeaf { + const OPTIONAL_KIND: Option = Some(SyntaxKind::TokenTreeLeaf); + type StablePtr = TokenTreeLeafPtr; + type Green = TokenTreeLeafGreen; + fn missing(db: &dyn SyntaxGroup) -> Self::Green { + TokenTreeLeafGreen( + Arc::new(GreenNode { + kind: SyntaxKind::TokenTreeLeaf, + details: GreenNodeDetails::Node { + children: vec![TokenNode::missing(db).0], + width: TextWidth::default(), + }, + }) + .intern(db), + ) + } + fn from_syntax_node(db: &dyn SyntaxGroup, node: SyntaxNode) -> Self { + let kind = node.kind(db); + assert_eq!( + kind, + SyntaxKind::TokenTreeLeaf, + "Unexpected SyntaxKind {:?}. Expected {:?}.", + kind, + SyntaxKind::TokenTreeLeaf + ); + let children = db.get_children(node.clone()); + Self { node, children } + } + fn as_syntax_node(&self) -> SyntaxNode { + self.node.clone() + } + fn stable_ptr(&self) -> Self::StablePtr { + TokenTreeLeafPtr(self.node.0.stable_ptr) + } +} +impl From<&TokenTreeLeaf> for SyntaxStablePtrId { + fn from(node: &TokenTreeLeaf) -> Self { + node.stable_ptr().untyped() + } +} +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct TokenTreeNode { + node: SyntaxNode, + children: Arc<[SyntaxNode]>, +} +impl TokenTreeNode { + pub const INDEX_SUBTREE: usize = 0; + pub fn new_green(db: &dyn SyntaxGroup, subtree: WrappedTokenTreeGreen) -> TokenTreeNodeGreen { + let children: Vec = vec![subtree.0]; + let width = children.iter().copied().map(|id| id.lookup_intern(db).width()).sum(); + TokenTreeNodeGreen( + Arc::new(GreenNode { + kind: SyntaxKind::TokenTreeNode, + details: GreenNodeDetails::Node { children, width }, + }) + .intern(db), + ) + } +} +impl TokenTreeNode { + pub fn subtree(&self, db: &dyn SyntaxGroup) -> WrappedTokenTree { + WrappedTokenTree::from_syntax_node(db, self.children[0].clone()) + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct TokenTreeNodePtr(pub SyntaxStablePtrId); +impl TokenTreeNodePtr {} +impl TypedStablePtr for TokenTreeNodePtr { + type SyntaxNode = TokenTreeNode; + fn untyped(&self) -> SyntaxStablePtrId { + self.0 + } + fn lookup(&self, db: &dyn SyntaxGroup) -> TokenTreeNode { + TokenTreeNode::from_syntax_node(db, self.0.lookup(db)) + } +} +impl From for SyntaxStablePtrId { + fn from(ptr: TokenTreeNodePtr) -> Self { + ptr.untyped() + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct TokenTreeNodeGreen(pub GreenId); +impl TypedSyntaxNode for TokenTreeNode { + const OPTIONAL_KIND: Option = Some(SyntaxKind::TokenTreeNode); + type StablePtr = TokenTreeNodePtr; + type Green = TokenTreeNodeGreen; + fn missing(db: &dyn SyntaxGroup) -> Self::Green { + TokenTreeNodeGreen( + Arc::new(GreenNode { + kind: SyntaxKind::TokenTreeNode, + details: GreenNodeDetails::Node { + children: vec![WrappedTokenTree::missing(db).0], + width: TextWidth::default(), + }, + }) + .intern(db), + ) + } + fn from_syntax_node(db: &dyn SyntaxGroup, node: SyntaxNode) -> Self { + let kind = node.kind(db); + assert_eq!( + kind, + SyntaxKind::TokenTreeNode, + "Unexpected SyntaxKind {:?}. Expected {:?}.", + kind, + SyntaxKind::TokenTreeNode + ); + let children = db.get_children(node.clone()); + Self { node, children } + } + fn as_syntax_node(&self) -> SyntaxNode { + self.node.clone() + } + fn stable_ptr(&self) -> Self::StablePtr { + TokenTreeNodePtr(self.node.0.stable_ptr) + } +} +impl From<&TokenTreeNode> for SyntaxStablePtrId { + fn from(node: &TokenTreeNode) -> Self { + node.stable_ptr().untyped() + } +} +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub enum TokenTree { + Token(TokenTreeLeaf), + Subtree(TokenTreeNode), + Missing(TokenTreeMissing), +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct TokenTreePtr(pub SyntaxStablePtrId); +impl TypedStablePtr for TokenTreePtr { + type SyntaxNode = TokenTree; + fn untyped(&self) -> SyntaxStablePtrId { + self.0 + } + fn lookup(&self, db: &dyn SyntaxGroup) -> TokenTree { + TokenTree::from_syntax_node(db, self.0.lookup(db)) + } +} +impl From for SyntaxStablePtrId { + fn from(ptr: TokenTreePtr) -> Self { + ptr.untyped() + } +} +impl From for TokenTreePtr { + fn from(value: TokenTreeLeafPtr) -> Self { + Self(value.0) + } +} +impl From for TokenTreePtr { + fn from(value: TokenTreeNodePtr) -> Self { + Self(value.0) + } +} +impl From for TokenTreePtr { + fn from(value: TokenTreeMissingPtr) -> Self { + Self(value.0) + } +} +impl From for TokenTreeGreen { + fn from(value: TokenTreeLeafGreen) -> Self { + Self(value.0) + } +} +impl From for TokenTreeGreen { + fn from(value: TokenTreeNodeGreen) -> Self { + Self(value.0) + } +} +impl From for TokenTreeGreen { + fn from(value: TokenTreeMissingGreen) -> Self { + Self(value.0) + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct TokenTreeGreen(pub GreenId); +impl TypedSyntaxNode for TokenTree { + const OPTIONAL_KIND: Option = None; + type StablePtr = TokenTreePtr; + type Green = TokenTreeGreen; + fn missing(db: &dyn SyntaxGroup) -> Self::Green { + TokenTreeGreen(TokenTreeMissing::missing(db).0) + } + fn from_syntax_node(db: &dyn SyntaxGroup, node: SyntaxNode) -> Self { + let kind = node.kind(db); + match kind { + SyntaxKind::TokenTreeLeaf => { + TokenTree::Token(TokenTreeLeaf::from_syntax_node(db, node)) + } + SyntaxKind::TokenTreeNode => { + TokenTree::Subtree(TokenTreeNode::from_syntax_node(db, node)) + } + SyntaxKind::TokenTreeMissing => { + TokenTree::Missing(TokenTreeMissing::from_syntax_node(db, node)) + } + _ => panic!("Unexpected syntax kind {:?} when constructing {}.", kind, "TokenTree"), + } + } + fn as_syntax_node(&self) -> SyntaxNode { + match self { + TokenTree::Token(x) => x.as_syntax_node(), + TokenTree::Subtree(x) => x.as_syntax_node(), + TokenTree::Missing(x) => x.as_syntax_node(), + } + } + fn stable_ptr(&self) -> Self::StablePtr { + TokenTreePtr(self.as_syntax_node().0.stable_ptr) + } +} +impl From<&TokenTree> for SyntaxStablePtrId { + fn from(node: &TokenTree) -> Self { + node.stable_ptr().untyped() + } +} +impl TokenTree { + /// Checks if a kind of a variant of [TokenTree]. + pub fn is_variant(kind: SyntaxKind) -> bool { + matches!( + kind, + SyntaxKind::TokenTreeLeaf | SyntaxKind::TokenTreeNode | SyntaxKind::TokenTreeMissing + ) + } +} +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct TokenTreeMissing { + node: SyntaxNode, + children: Arc<[SyntaxNode]>, +} +impl TokenTreeMissing { + pub fn new_green(db: &dyn SyntaxGroup) -> TokenTreeMissingGreen { + let children: Vec = vec![]; + let width = children.iter().copied().map(|id| id.lookup_intern(db).width()).sum(); + TokenTreeMissingGreen( + Arc::new(GreenNode { + kind: SyntaxKind::TokenTreeMissing, + details: GreenNodeDetails::Node { children, width }, + }) + .intern(db), + ) + } +} +impl TokenTreeMissing {} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct TokenTreeMissingPtr(pub SyntaxStablePtrId); +impl TokenTreeMissingPtr {} +impl TypedStablePtr for TokenTreeMissingPtr { + type SyntaxNode = TokenTreeMissing; + fn untyped(&self) -> SyntaxStablePtrId { + self.0 + } + fn lookup(&self, db: &dyn SyntaxGroup) -> TokenTreeMissing { + TokenTreeMissing::from_syntax_node(db, self.0.lookup(db)) + } +} +impl From for SyntaxStablePtrId { + fn from(ptr: TokenTreeMissingPtr) -> Self { + ptr.untyped() + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct TokenTreeMissingGreen(pub GreenId); +impl TypedSyntaxNode for TokenTreeMissing { + const OPTIONAL_KIND: Option = Some(SyntaxKind::TokenTreeMissing); + type StablePtr = TokenTreeMissingPtr; + type Green = TokenTreeMissingGreen; + fn missing(db: &dyn SyntaxGroup) -> Self::Green { + TokenTreeMissingGreen( + Arc::new(GreenNode { + kind: SyntaxKind::TokenTreeMissing, + details: GreenNodeDetails::Node { children: vec![], width: TextWidth::default() }, + }) + .intern(db), + ) + } + fn from_syntax_node(db: &dyn SyntaxGroup, node: SyntaxNode) -> Self { + let kind = node.kind(db); + assert_eq!( + kind, + SyntaxKind::TokenTreeMissing, + "Unexpected SyntaxKind {:?}. Expected {:?}.", + kind, + SyntaxKind::TokenTreeMissing + ); + let children = db.get_children(node.clone()); + Self { node, children } + } + fn as_syntax_node(&self) -> SyntaxNode { + self.node.clone() + } + fn stable_ptr(&self) -> Self::StablePtr { + TokenTreeMissingPtr(self.node.0.stable_ptr) + } +} +impl From<&TokenTreeMissing> for SyntaxStablePtrId { + fn from(node: &TokenTreeMissing) -> Self { + node.stable_ptr().untyped() + } +} +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub enum WrappedTokenTree { + Parenthesized(ParenthesizedTokenTree), + Braced(BracedTokenTree), + Bracketed(BracketedTokenTree), + Missing(WrappedTokenTreeMissing), +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct WrappedTokenTreePtr(pub SyntaxStablePtrId); +impl TypedStablePtr for WrappedTokenTreePtr { + type SyntaxNode = WrappedTokenTree; + fn untyped(&self) -> SyntaxStablePtrId { + self.0 + } + fn lookup(&self, db: &dyn SyntaxGroup) -> WrappedTokenTree { + WrappedTokenTree::from_syntax_node(db, self.0.lookup(db)) + } +} +impl From for SyntaxStablePtrId { + fn from(ptr: WrappedTokenTreePtr) -> Self { + ptr.untyped() + } +} +impl From for WrappedTokenTreePtr { + fn from(value: ParenthesizedTokenTreePtr) -> Self { + Self(value.0) + } +} +impl From for WrappedTokenTreePtr { + fn from(value: BracedTokenTreePtr) -> Self { + Self(value.0) + } +} +impl From for WrappedTokenTreePtr { + fn from(value: BracketedTokenTreePtr) -> Self { + Self(value.0) + } +} +impl From for WrappedTokenTreePtr { + fn from(value: WrappedTokenTreeMissingPtr) -> Self { + Self(value.0) + } +} +impl From for WrappedTokenTreeGreen { + fn from(value: ParenthesizedTokenTreeGreen) -> Self { + Self(value.0) + } +} +impl From for WrappedTokenTreeGreen { + fn from(value: BracedTokenTreeGreen) -> Self { + Self(value.0) + } +} +impl From for WrappedTokenTreeGreen { + fn from(value: BracketedTokenTreeGreen) -> Self { + Self(value.0) + } +} +impl From for WrappedTokenTreeGreen { + fn from(value: WrappedTokenTreeMissingGreen) -> Self { + Self(value.0) + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct WrappedTokenTreeGreen(pub GreenId); +impl TypedSyntaxNode for WrappedTokenTree { + const OPTIONAL_KIND: Option = None; + type StablePtr = WrappedTokenTreePtr; + type Green = WrappedTokenTreeGreen; + fn missing(db: &dyn SyntaxGroup) -> Self::Green { + WrappedTokenTreeGreen(WrappedTokenTreeMissing::missing(db).0) + } + fn from_syntax_node(db: &dyn SyntaxGroup, node: SyntaxNode) -> Self { + let kind = node.kind(db); + match kind { + SyntaxKind::ParenthesizedTokenTree => { + WrappedTokenTree::Parenthesized(ParenthesizedTokenTree::from_syntax_node(db, node)) + } + SyntaxKind::BracedTokenTree => { + WrappedTokenTree::Braced(BracedTokenTree::from_syntax_node(db, node)) + } + SyntaxKind::BracketedTokenTree => { + WrappedTokenTree::Bracketed(BracketedTokenTree::from_syntax_node(db, node)) + } + SyntaxKind::WrappedTokenTreeMissing => { + WrappedTokenTree::Missing(WrappedTokenTreeMissing::from_syntax_node(db, node)) + } + _ => panic!( + "Unexpected syntax kind {:?} when constructing {}.", + kind, "WrappedTokenTree" + ), + } + } + fn as_syntax_node(&self) -> SyntaxNode { + match self { + WrappedTokenTree::Parenthesized(x) => x.as_syntax_node(), + WrappedTokenTree::Braced(x) => x.as_syntax_node(), + WrappedTokenTree::Bracketed(x) => x.as_syntax_node(), + WrappedTokenTree::Missing(x) => x.as_syntax_node(), + } + } + fn stable_ptr(&self) -> Self::StablePtr { + WrappedTokenTreePtr(self.as_syntax_node().0.stable_ptr) + } +} +impl From<&WrappedTokenTree> for SyntaxStablePtrId { + fn from(node: &WrappedTokenTree) -> Self { + node.stable_ptr().untyped() + } +} +impl WrappedTokenTree { + /// Checks if a kind of a variant of [WrappedTokenTree]. + pub fn is_variant(kind: SyntaxKind) -> bool { + matches!( + kind, + SyntaxKind::ParenthesizedTokenTree + | SyntaxKind::BracedTokenTree + | SyntaxKind::BracketedTokenTree + | SyntaxKind::WrappedTokenTreeMissing + ) + } +} +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct WrappedTokenTreeMissing { + node: SyntaxNode, + children: Arc<[SyntaxNode]>, +} +impl WrappedTokenTreeMissing { + pub fn new_green(db: &dyn SyntaxGroup) -> WrappedTokenTreeMissingGreen { + let children: Vec = vec![]; + let width = children.iter().copied().map(|id| id.lookup_intern(db).width()).sum(); + WrappedTokenTreeMissingGreen( + Arc::new(GreenNode { + kind: SyntaxKind::WrappedTokenTreeMissing, + details: GreenNodeDetails::Node { children, width }, + }) + .intern(db), + ) + } +} +impl WrappedTokenTreeMissing {} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct WrappedTokenTreeMissingPtr(pub SyntaxStablePtrId); +impl WrappedTokenTreeMissingPtr {} +impl TypedStablePtr for WrappedTokenTreeMissingPtr { + type SyntaxNode = WrappedTokenTreeMissing; + fn untyped(&self) -> SyntaxStablePtrId { + self.0 + } + fn lookup(&self, db: &dyn SyntaxGroup) -> WrappedTokenTreeMissing { + WrappedTokenTreeMissing::from_syntax_node(db, self.0.lookup(db)) + } +} +impl From for SyntaxStablePtrId { + fn from(ptr: WrappedTokenTreeMissingPtr) -> Self { + ptr.untyped() + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct WrappedTokenTreeMissingGreen(pub GreenId); +impl TypedSyntaxNode for WrappedTokenTreeMissing { + const OPTIONAL_KIND: Option = Some(SyntaxKind::WrappedTokenTreeMissing); + type StablePtr = WrappedTokenTreeMissingPtr; + type Green = WrappedTokenTreeMissingGreen; + fn missing(db: &dyn SyntaxGroup) -> Self::Green { + WrappedTokenTreeMissingGreen( + Arc::new(GreenNode { + kind: SyntaxKind::WrappedTokenTreeMissing, + details: GreenNodeDetails::Node { children: vec![], width: TextWidth::default() }, + }) + .intern(db), + ) + } + fn from_syntax_node(db: &dyn SyntaxGroup, node: SyntaxNode) -> Self { + let kind = node.kind(db); + assert_eq!( + kind, + SyntaxKind::WrappedTokenTreeMissing, + "Unexpected SyntaxKind {:?}. Expected {:?}.", + kind, + SyntaxKind::WrappedTokenTreeMissing + ); + let children = db.get_children(node.clone()); + Self { node, children } + } + fn as_syntax_node(&self) -> SyntaxNode { + self.node.clone() + } + fn stable_ptr(&self) -> Self::StablePtr { + WrappedTokenTreeMissingPtr(self.node.0.stable_ptr) + } +} +impl From<&WrappedTokenTreeMissing> for SyntaxStablePtrId { + fn from(node: &WrappedTokenTreeMissing) -> Self { + node.stable_ptr().untyped() + } +} +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct ParenthesizedTokenTree { + node: SyntaxNode, + children: Arc<[SyntaxNode]>, +} +impl ParenthesizedTokenTree { + pub const INDEX_LPAREN: usize = 0; + pub const INDEX_TOKENS: usize = 1; + pub const INDEX_RPAREN: usize = 2; + pub fn new_green( + db: &dyn SyntaxGroup, + lparen: TerminalLParenGreen, + tokens: TokenListGreen, + rparen: TerminalRParenGreen, + ) -> ParenthesizedTokenTreeGreen { + let children: Vec = vec![lparen.0, tokens.0, rparen.0]; + let width = children.iter().copied().map(|id| id.lookup_intern(db).width()).sum(); + ParenthesizedTokenTreeGreen( + Arc::new(GreenNode { + kind: SyntaxKind::ParenthesizedTokenTree, + details: GreenNodeDetails::Node { children, width }, + }) + .intern(db), + ) + } +} +impl ParenthesizedTokenTree { + pub fn lparen(&self, db: &dyn SyntaxGroup) -> TerminalLParen { + TerminalLParen::from_syntax_node(db, self.children[0].clone()) + } + pub fn tokens(&self, db: &dyn SyntaxGroup) -> TokenList { + TokenList::from_syntax_node(db, self.children[1].clone()) + } + pub fn rparen(&self, db: &dyn SyntaxGroup) -> TerminalRParen { + TerminalRParen::from_syntax_node(db, self.children[2].clone()) + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct ParenthesizedTokenTreePtr(pub SyntaxStablePtrId); +impl ParenthesizedTokenTreePtr {} +impl TypedStablePtr for ParenthesizedTokenTreePtr { + type SyntaxNode = ParenthesizedTokenTree; + fn untyped(&self) -> SyntaxStablePtrId { + self.0 + } + fn lookup(&self, db: &dyn SyntaxGroup) -> ParenthesizedTokenTree { + ParenthesizedTokenTree::from_syntax_node(db, self.0.lookup(db)) + } +} +impl From for SyntaxStablePtrId { + fn from(ptr: ParenthesizedTokenTreePtr) -> Self { + ptr.untyped() + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct ParenthesizedTokenTreeGreen(pub GreenId); +impl TypedSyntaxNode for ParenthesizedTokenTree { + const OPTIONAL_KIND: Option = Some(SyntaxKind::ParenthesizedTokenTree); + type StablePtr = ParenthesizedTokenTreePtr; + type Green = ParenthesizedTokenTreeGreen; + fn missing(db: &dyn SyntaxGroup) -> Self::Green { + ParenthesizedTokenTreeGreen( + Arc::new(GreenNode { + kind: SyntaxKind::ParenthesizedTokenTree, + details: GreenNodeDetails::Node { + children: vec![ + TerminalLParen::missing(db).0, + TokenList::missing(db).0, + TerminalRParen::missing(db).0, + ], + width: TextWidth::default(), + }, + }) + .intern(db), + ) + } + fn from_syntax_node(db: &dyn SyntaxGroup, node: SyntaxNode) -> Self { + let kind = node.kind(db); + assert_eq!( + kind, + SyntaxKind::ParenthesizedTokenTree, + "Unexpected SyntaxKind {:?}. Expected {:?}.", + kind, + SyntaxKind::ParenthesizedTokenTree + ); + let children = db.get_children(node.clone()); + Self { node, children } + } + fn as_syntax_node(&self) -> SyntaxNode { + self.node.clone() + } + fn stable_ptr(&self) -> Self::StablePtr { + ParenthesizedTokenTreePtr(self.node.0.stable_ptr) + } +} +impl From<&ParenthesizedTokenTree> for SyntaxStablePtrId { + fn from(node: &ParenthesizedTokenTree) -> Self { + node.stable_ptr().untyped() + } +} +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct BracedTokenTree { + node: SyntaxNode, + children: Arc<[SyntaxNode]>, +} +impl BracedTokenTree { + pub const INDEX_LBRACE: usize = 0; + pub const INDEX_TOKENS: usize = 1; + pub const INDEX_RBRACE: usize = 2; + pub fn new_green( + db: &dyn SyntaxGroup, + lbrace: TerminalLBraceGreen, + tokens: TokenListGreen, + rbrace: TerminalRBraceGreen, + ) -> BracedTokenTreeGreen { + let children: Vec = vec![lbrace.0, tokens.0, rbrace.0]; + let width = children.iter().copied().map(|id| id.lookup_intern(db).width()).sum(); + BracedTokenTreeGreen( + Arc::new(GreenNode { + kind: SyntaxKind::BracedTokenTree, + details: GreenNodeDetails::Node { children, width }, + }) + .intern(db), + ) + } +} +impl BracedTokenTree { + pub fn lbrace(&self, db: &dyn SyntaxGroup) -> TerminalLBrace { + TerminalLBrace::from_syntax_node(db, self.children[0].clone()) + } + pub fn tokens(&self, db: &dyn SyntaxGroup) -> TokenList { + TokenList::from_syntax_node(db, self.children[1].clone()) + } + pub fn rbrace(&self, db: &dyn SyntaxGroup) -> TerminalRBrace { + TerminalRBrace::from_syntax_node(db, self.children[2].clone()) + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct BracedTokenTreePtr(pub SyntaxStablePtrId); +impl BracedTokenTreePtr {} +impl TypedStablePtr for BracedTokenTreePtr { + type SyntaxNode = BracedTokenTree; + fn untyped(&self) -> SyntaxStablePtrId { + self.0 + } + fn lookup(&self, db: &dyn SyntaxGroup) -> BracedTokenTree { + BracedTokenTree::from_syntax_node(db, self.0.lookup(db)) + } +} +impl From for SyntaxStablePtrId { + fn from(ptr: BracedTokenTreePtr) -> Self { + ptr.untyped() + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct BracedTokenTreeGreen(pub GreenId); +impl TypedSyntaxNode for BracedTokenTree { + const OPTIONAL_KIND: Option = Some(SyntaxKind::BracedTokenTree); + type StablePtr = BracedTokenTreePtr; + type Green = BracedTokenTreeGreen; + fn missing(db: &dyn SyntaxGroup) -> Self::Green { + BracedTokenTreeGreen( + Arc::new(GreenNode { + kind: SyntaxKind::BracedTokenTree, + details: GreenNodeDetails::Node { + children: vec![ + TerminalLBrace::missing(db).0, + TokenList::missing(db).0, + TerminalRBrace::missing(db).0, + ], + width: TextWidth::default(), + }, + }) + .intern(db), + ) + } + fn from_syntax_node(db: &dyn SyntaxGroup, node: SyntaxNode) -> Self { + let kind = node.kind(db); + assert_eq!( + kind, + SyntaxKind::BracedTokenTree, + "Unexpected SyntaxKind {:?}. Expected {:?}.", + kind, + SyntaxKind::BracedTokenTree + ); + let children = db.get_children(node.clone()); + Self { node, children } + } + fn as_syntax_node(&self) -> SyntaxNode { + self.node.clone() + } + fn stable_ptr(&self) -> Self::StablePtr { + BracedTokenTreePtr(self.node.0.stable_ptr) + } +} +impl From<&BracedTokenTree> for SyntaxStablePtrId { + fn from(node: &BracedTokenTree) -> Self { + node.stable_ptr().untyped() + } +} +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct BracketedTokenTree { + node: SyntaxNode, + children: Arc<[SyntaxNode]>, +} +impl BracketedTokenTree { + pub const INDEX_LBRACK: usize = 0; + pub const INDEX_TOKENS: usize = 1; + pub const INDEX_RBRACK: usize = 2; + pub fn new_green( + db: &dyn SyntaxGroup, + lbrack: TerminalLBrackGreen, + tokens: TokenListGreen, + rbrack: TerminalRBrackGreen, + ) -> BracketedTokenTreeGreen { + let children: Vec = vec![lbrack.0, tokens.0, rbrack.0]; + let width = children.iter().copied().map(|id| id.lookup_intern(db).width()).sum(); + BracketedTokenTreeGreen( + Arc::new(GreenNode { + kind: SyntaxKind::BracketedTokenTree, + details: GreenNodeDetails::Node { children, width }, + }) + .intern(db), + ) + } +} +impl BracketedTokenTree { + pub fn lbrack(&self, db: &dyn SyntaxGroup) -> TerminalLBrack { + TerminalLBrack::from_syntax_node(db, self.children[0].clone()) + } + pub fn tokens(&self, db: &dyn SyntaxGroup) -> TokenList { + TokenList::from_syntax_node(db, self.children[1].clone()) + } + pub fn rbrack(&self, db: &dyn SyntaxGroup) -> TerminalRBrack { + TerminalRBrack::from_syntax_node(db, self.children[2].clone()) + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct BracketedTokenTreePtr(pub SyntaxStablePtrId); +impl BracketedTokenTreePtr {} +impl TypedStablePtr for BracketedTokenTreePtr { + type SyntaxNode = BracketedTokenTree; + fn untyped(&self) -> SyntaxStablePtrId { + self.0 + } + fn lookup(&self, db: &dyn SyntaxGroup) -> BracketedTokenTree { + BracketedTokenTree::from_syntax_node(db, self.0.lookup(db)) + } +} +impl From for SyntaxStablePtrId { + fn from(ptr: BracketedTokenTreePtr) -> Self { + ptr.untyped() + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct BracketedTokenTreeGreen(pub GreenId); +impl TypedSyntaxNode for BracketedTokenTree { + const OPTIONAL_KIND: Option = Some(SyntaxKind::BracketedTokenTree); + type StablePtr = BracketedTokenTreePtr; + type Green = BracketedTokenTreeGreen; + fn missing(db: &dyn SyntaxGroup) -> Self::Green { + BracketedTokenTreeGreen( + Arc::new(GreenNode { + kind: SyntaxKind::BracketedTokenTree, + details: GreenNodeDetails::Node { + children: vec![ + TerminalLBrack::missing(db).0, + TokenList::missing(db).0, + TerminalRBrack::missing(db).0, + ], + width: TextWidth::default(), + }, + }) + .intern(db), + ) + } + fn from_syntax_node(db: &dyn SyntaxGroup, node: SyntaxNode) -> Self { + let kind = node.kind(db); + assert_eq!( + kind, + SyntaxKind::BracketedTokenTree, + "Unexpected SyntaxKind {:?}. Expected {:?}.", + kind, + SyntaxKind::BracketedTokenTree + ); + let children = db.get_children(node.clone()); + Self { node, children } + } + fn as_syntax_node(&self) -> SyntaxNode { + self.node.clone() + } + fn stable_ptr(&self) -> Self::StablePtr { + BracketedTokenTreePtr(self.node.0.stable_ptr) + } +} +impl From<&BracketedTokenTree> for SyntaxStablePtrId { + fn from(node: &BracketedTokenTree) -> Self { + node.stable_ptr().untyped() + } +} +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct ExprInlineMacro { + node: SyntaxNode, + children: Arc<[SyntaxNode]>, +} +impl ExprInlineMacro { + pub const INDEX_PATH: usize = 0; + pub const INDEX_BANG: usize = 1; + pub const INDEX_ARGUMENTS: usize = 2; + pub fn new_green( + db: &dyn SyntaxGroup, + path: ExprPathGreen, + bang: TerminalNotGreen, + arguments: TokenTreeNodeGreen, + ) -> ExprInlineMacroGreen { + let children: Vec = vec![path.0, bang.0, arguments.0]; + let width = children.iter().copied().map(|id| id.lookup_intern(db).width()).sum(); + ExprInlineMacroGreen( + Arc::new(GreenNode { + kind: SyntaxKind::ExprInlineMacro, + details: GreenNodeDetails::Node { children, width }, + }) + .intern(db), + ) + } +} +impl ExprInlineMacro { + pub fn path(&self, db: &dyn SyntaxGroup) -> ExprPath { + ExprPath::from_syntax_node(db, self.children[0].clone()) + } + pub fn bang(&self, db: &dyn SyntaxGroup) -> TerminalNot { + TerminalNot::from_syntax_node(db, self.children[1].clone()) + } + pub fn arguments(&self, db: &dyn SyntaxGroup) -> TokenTreeNode { + TokenTreeNode::from_syntax_node(db, self.children[2].clone()) + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct ExprInlineMacroPtr(pub SyntaxStablePtrId); +impl ExprInlineMacroPtr {} +impl TypedStablePtr for ExprInlineMacroPtr { + type SyntaxNode = ExprInlineMacro; + fn untyped(&self) -> SyntaxStablePtrId { + self.0 + } + fn lookup(&self, db: &dyn SyntaxGroup) -> ExprInlineMacro { + ExprInlineMacro::from_syntax_node(db, self.0.lookup(db)) + } +} +impl From for SyntaxStablePtrId { + fn from(ptr: ExprInlineMacroPtr) -> Self { + ptr.untyped() + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct ExprInlineMacroGreen(pub GreenId); +impl TypedSyntaxNode for ExprInlineMacro { + const OPTIONAL_KIND: Option = Some(SyntaxKind::ExprInlineMacro); + type StablePtr = ExprInlineMacroPtr; + type Green = ExprInlineMacroGreen; + fn missing(db: &dyn SyntaxGroup) -> Self::Green { + ExprInlineMacroGreen( + Arc::new(GreenNode { + kind: SyntaxKind::ExprInlineMacro, + details: GreenNodeDetails::Node { + children: vec![ + ExprPath::missing(db).0, + TerminalNot::missing(db).0, + TokenTreeNode::missing(db).0, + ], + width: TextWidth::default(), + }, + }) + .intern(db), + ) + } + fn from_syntax_node(db: &dyn SyntaxGroup, node: SyntaxNode) -> Self { + let kind = node.kind(db); + assert_eq!( + kind, + SyntaxKind::ExprInlineMacro, + "Unexpected SyntaxKind {:?}. Expected {:?}.", + kind, + SyntaxKind::ExprInlineMacro + ); + let children = db.get_children(node.clone()); + Self { node, children } + } + fn as_syntax_node(&self) -> SyntaxNode { + self.node.clone() + } + fn stable_ptr(&self) -> Self::StablePtr { + ExprInlineMacroPtr(self.node.0.stable_ptr) + } +} +impl From<&ExprInlineMacro> for SyntaxStablePtrId { + fn from(node: &ExprInlineMacro) -> Self { + node.stable_ptr().untyped() + } +} +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct ItemInlineMacro { + node: SyntaxNode, + children: Arc<[SyntaxNode]>, +} +impl ItemInlineMacro { + pub const INDEX_ATTRIBUTES: usize = 0; + pub const INDEX_NAME: usize = 1; + pub const INDEX_BANG: usize = 2; + pub const INDEX_ARGUMENTS: usize = 3; + pub const INDEX_SEMICOLON: usize = 4; + pub fn new_green( + db: &dyn SyntaxGroup, + attributes: AttributeListGreen, + name: TerminalIdentifierGreen, + bang: TerminalNotGreen, + arguments: TokenTreeNodeGreen, + semicolon: TerminalSemicolonGreen, + ) -> ItemInlineMacroGreen { + let children: Vec = vec![attributes.0, name.0, bang.0, arguments.0, semicolon.0]; + let width = children.iter().copied().map(|id| id.lookup_intern(db).width()).sum(); + ItemInlineMacroGreen( + Arc::new(GreenNode { + kind: SyntaxKind::ItemInlineMacro, + details: GreenNodeDetails::Node { children, width }, + }) + .intern(db), + ) + } +} +impl ItemInlineMacro { + pub fn attributes(&self, db: &dyn SyntaxGroup) -> AttributeList { + AttributeList::from_syntax_node(db, self.children[0].clone()) + } + pub fn name(&self, db: &dyn SyntaxGroup) -> TerminalIdentifier { + TerminalIdentifier::from_syntax_node(db, self.children[1].clone()) + } + pub fn bang(&self, db: &dyn SyntaxGroup) -> TerminalNot { + TerminalNot::from_syntax_node(db, self.children[2].clone()) + } + pub fn arguments(&self, db: &dyn SyntaxGroup) -> TokenTreeNode { + TokenTreeNode::from_syntax_node(db, self.children[3].clone()) + } + pub fn semicolon(&self, db: &dyn SyntaxGroup) -> TerminalSemicolon { + TerminalSemicolon::from_syntax_node(db, self.children[4].clone()) + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct ItemInlineMacroPtr(pub SyntaxStablePtrId); +impl ItemInlineMacroPtr {} +impl TypedStablePtr for ItemInlineMacroPtr { + type SyntaxNode = ItemInlineMacro; + fn untyped(&self) -> SyntaxStablePtrId { + self.0 + } + fn lookup(&self, db: &dyn SyntaxGroup) -> ItemInlineMacro { + ItemInlineMacro::from_syntax_node(db, self.0.lookup(db)) + } +} +impl From for SyntaxStablePtrId { + fn from(ptr: ItemInlineMacroPtr) -> Self { + ptr.untyped() + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct ItemInlineMacroGreen(pub GreenId); +impl TypedSyntaxNode for ItemInlineMacro { + const OPTIONAL_KIND: Option = Some(SyntaxKind::ItemInlineMacro); + type StablePtr = ItemInlineMacroPtr; + type Green = ItemInlineMacroGreen; + fn missing(db: &dyn SyntaxGroup) -> Self::Green { + ItemInlineMacroGreen( + Arc::new(GreenNode { + kind: SyntaxKind::ItemInlineMacro, + details: GreenNodeDetails::Node { + children: vec![ + AttributeList::missing(db).0, + TerminalIdentifier::missing(db).0, + TerminalNot::missing(db).0, + TokenTreeNode::missing(db).0, + TerminalSemicolon::missing(db).0, + ], + width: TextWidth::default(), + }, + }) + .intern(db), + ) + } + fn from_syntax_node(db: &dyn SyntaxGroup, node: SyntaxNode) -> Self { + let kind = node.kind(db); + assert_eq!( + kind, + SyntaxKind::ItemInlineMacro, + "Unexpected SyntaxKind {:?}. Expected {:?}.", + kind, + SyntaxKind::ItemInlineMacro + ); + let children = db.get_children(node.clone()); + Self { node, children } + } + fn as_syntax_node(&self) -> SyntaxNode { + self.node.clone() + } + fn stable_ptr(&self) -> Self::StablePtr { + ItemInlineMacroPtr(self.node.0.stable_ptr) + } +} +impl From<&ItemInlineMacro> for SyntaxStablePtrId { + fn from(node: &ItemInlineMacro) -> Self { + node.stable_ptr().untyped() + } +} +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct LegacyExprInlineMacro { + node: SyntaxNode, + children: Arc<[SyntaxNode]>, +} +impl LegacyExprInlineMacro { + pub const INDEX_PATH: usize = 0; + pub const INDEX_BANG: usize = 1; + pub const INDEX_ARGUMENTS: usize = 2; + pub fn new_green( + db: &dyn SyntaxGroup, + path: ExprPathGreen, + bang: TerminalNotGreen, + arguments: WrappedArgListGreen, + ) -> LegacyExprInlineMacroGreen { + let children: Vec = vec![path.0, bang.0, arguments.0]; + let width = children.iter().copied().map(|id| id.lookup_intern(db).width()).sum(); + LegacyExprInlineMacroGreen( + Arc::new(GreenNode { + kind: SyntaxKind::LegacyExprInlineMacro, + details: GreenNodeDetails::Node { children, width }, + }) + .intern(db), + ) + } +} +impl LegacyExprInlineMacro { + pub fn path(&self, db: &dyn SyntaxGroup) -> ExprPath { + ExprPath::from_syntax_node(db, self.children[0].clone()) + } + pub fn bang(&self, db: &dyn SyntaxGroup) -> TerminalNot { + TerminalNot::from_syntax_node(db, self.children[1].clone()) + } + pub fn arguments(&self, db: &dyn SyntaxGroup) -> WrappedArgList { + WrappedArgList::from_syntax_node(db, self.children[2].clone()) + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct LegacyExprInlineMacroPtr(pub SyntaxStablePtrId); +impl LegacyExprInlineMacroPtr {} +impl TypedStablePtr for LegacyExprInlineMacroPtr { + type SyntaxNode = LegacyExprInlineMacro; + fn untyped(&self) -> SyntaxStablePtrId { + self.0 + } + fn lookup(&self, db: &dyn SyntaxGroup) -> LegacyExprInlineMacro { + LegacyExprInlineMacro::from_syntax_node(db, self.0.lookup(db)) + } +} +impl From for SyntaxStablePtrId { + fn from(ptr: LegacyExprInlineMacroPtr) -> Self { + ptr.untyped() + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct LegacyExprInlineMacroGreen(pub GreenId); +impl TypedSyntaxNode for LegacyExprInlineMacro { + const OPTIONAL_KIND: Option = Some(SyntaxKind::LegacyExprInlineMacro); + type StablePtr = LegacyExprInlineMacroPtr; + type Green = LegacyExprInlineMacroGreen; + fn missing(db: &dyn SyntaxGroup) -> Self::Green { + LegacyExprInlineMacroGreen( + Arc::new(GreenNode { + kind: SyntaxKind::LegacyExprInlineMacro, + details: GreenNodeDetails::Node { + children: vec![ + ExprPath::missing(db).0, + TerminalNot::missing(db).0, + WrappedArgList::missing(db).0, + ], + width: TextWidth::default(), + }, + }) + .intern(db), + ) + } + fn from_syntax_node(db: &dyn SyntaxGroup, node: SyntaxNode) -> Self { + let kind = node.kind(db); + assert_eq!( + kind, + SyntaxKind::LegacyExprInlineMacro, + "Unexpected SyntaxKind {:?}. Expected {:?}.", + kind, + SyntaxKind::LegacyExprInlineMacro + ); + let children = db.get_children(node.clone()); + Self { node, children } + } + fn as_syntax_node(&self) -> SyntaxNode { + self.node.clone() + } + fn stable_ptr(&self) -> Self::StablePtr { + LegacyExprInlineMacroPtr(self.node.0.stable_ptr) + } +} +impl From<&LegacyExprInlineMacro> for SyntaxStablePtrId { + fn from(node: &LegacyExprInlineMacro) -> Self { + node.stable_ptr().untyped() + } +} +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct LegacyItemInlineMacro { + node: SyntaxNode, + children: Arc<[SyntaxNode]>, +} +impl LegacyItemInlineMacro { + pub const INDEX_ATTRIBUTES: usize = 0; + pub const INDEX_NAME: usize = 1; + pub const INDEX_BANG: usize = 2; + pub const INDEX_ARGUMENTS: usize = 3; + pub const INDEX_SEMICOLON: usize = 4; + pub fn new_green( + db: &dyn SyntaxGroup, + attributes: AttributeListGreen, + name: TerminalIdentifierGreen, + bang: TerminalNotGreen, + arguments: WrappedArgListGreen, + semicolon: TerminalSemicolonGreen, + ) -> LegacyItemInlineMacroGreen { + let children: Vec = vec![attributes.0, name.0, bang.0, arguments.0, semicolon.0]; + let width = children.iter().copied().map(|id| id.lookup_intern(db).width()).sum(); + LegacyItemInlineMacroGreen( + Arc::new(GreenNode { + kind: SyntaxKind::LegacyItemInlineMacro, + details: GreenNodeDetails::Node { children, width }, + }) + .intern(db), + ) + } +} +impl LegacyItemInlineMacro { + pub fn attributes(&self, db: &dyn SyntaxGroup) -> AttributeList { + AttributeList::from_syntax_node(db, self.children[0].clone()) + } + pub fn name(&self, db: &dyn SyntaxGroup) -> TerminalIdentifier { + TerminalIdentifier::from_syntax_node(db, self.children[1].clone()) + } + pub fn bang(&self, db: &dyn SyntaxGroup) -> TerminalNot { + TerminalNot::from_syntax_node(db, self.children[2].clone()) + } + pub fn arguments(&self, db: &dyn SyntaxGroup) -> WrappedArgList { + WrappedArgList::from_syntax_node(db, self.children[3].clone()) + } + pub fn semicolon(&self, db: &dyn SyntaxGroup) -> TerminalSemicolon { + TerminalSemicolon::from_syntax_node(db, self.children[4].clone()) + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct LegacyItemInlineMacroPtr(pub SyntaxStablePtrId); +impl LegacyItemInlineMacroPtr {} +impl TypedStablePtr for LegacyItemInlineMacroPtr { + type SyntaxNode = LegacyItemInlineMacro; + fn untyped(&self) -> SyntaxStablePtrId { + self.0 + } + fn lookup(&self, db: &dyn SyntaxGroup) -> LegacyItemInlineMacro { + LegacyItemInlineMacro::from_syntax_node(db, self.0.lookup(db)) + } +} +impl From for SyntaxStablePtrId { + fn from(ptr: LegacyItemInlineMacroPtr) -> Self { + ptr.untyped() + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct LegacyItemInlineMacroGreen(pub GreenId); +impl TypedSyntaxNode for LegacyItemInlineMacro { + const OPTIONAL_KIND: Option = Some(SyntaxKind::LegacyItemInlineMacro); + type StablePtr = LegacyItemInlineMacroPtr; + type Green = LegacyItemInlineMacroGreen; + fn missing(db: &dyn SyntaxGroup) -> Self::Green { + LegacyItemInlineMacroGreen( + Arc::new(GreenNode { + kind: SyntaxKind::LegacyItemInlineMacro, + details: GreenNodeDetails::Node { + children: vec![ + AttributeList::missing(db).0, + TerminalIdentifier::missing(db).0, + TerminalNot::missing(db).0, + WrappedArgList::missing(db).0, + TerminalSemicolon::missing(db).0, + ], + width: TextWidth::default(), + }, + }) + .intern(db), + ) + } + fn from_syntax_node(db: &dyn SyntaxGroup, node: SyntaxNode) -> Self { + let kind = node.kind(db); + assert_eq!( + kind, + SyntaxKind::LegacyItemInlineMacro, + "Unexpected SyntaxKind {:?}. Expected {:?}.", + kind, + SyntaxKind::LegacyItemInlineMacro + ); + let children = db.get_children(node.clone()); + Self { node, children } + } + fn as_syntax_node(&self) -> SyntaxNode { + self.node.clone() + } + fn stable_ptr(&self) -> Self::StablePtr { + LegacyItemInlineMacroPtr(self.node.0.stable_ptr) + } +} +impl From<&LegacyItemInlineMacro> for SyntaxStablePtrId { + fn from(node: &LegacyItemInlineMacro) -> Self { + node.stable_ptr().untyped() + } +} +#[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct TriviumSkippedNode { node: SyntaxNode, children: Arc<[SyntaxNode]>, diff --git a/crates/cairo-lang-syntax/src/node/key_fields.rs b/crates/cairo-lang-syntax/src/node/key_fields.rs index b179d4774cb..566f91280ca 100644 --- a/crates/cairo-lang-syntax/src/node/key_fields.rs +++ b/crates/cairo-lang-syntax/src/node/key_fields.rs @@ -105,9 +105,6 @@ pub fn get_key_fields(kind: SyntaxKind, children: &[GreenId]) -> Vec { SyntaxKind::ExprIndexed => { vec![] } - SyntaxKind::ExprInlineMacro => { - vec![] - } SyntaxKind::ExprFixedSizeArray => { vec![] } @@ -307,9 +304,6 @@ pub fn get_key_fields(kind: SyntaxKind, children: &[GreenId]) -> Vec { SyntaxKind::ItemImpl => { vec![/* name */ children[3]] } - SyntaxKind::ItemInlineMacro => { - vec![] - } SyntaxKind::ItemHeaderDoc => { vec![] } @@ -386,6 +380,40 @@ pub fn get_key_fields(kind: SyntaxKind, children: &[GreenId]) -> Vec { SyntaxKind::GenericParamNegativeImpl => { vec![] } + SyntaxKind::TokenList => vec![], + SyntaxKind::TokenTreeLeaf => { + vec![] + } + SyntaxKind::TokenTreeNode => { + vec![] + } + SyntaxKind::TokenTreeMissing => { + vec![] + } + SyntaxKind::WrappedTokenTreeMissing => { + vec![] + } + SyntaxKind::ParenthesizedTokenTree => { + vec![] + } + SyntaxKind::BracedTokenTree => { + vec![] + } + SyntaxKind::BracketedTokenTree => { + vec![] + } + SyntaxKind::ExprInlineMacro => { + vec![] + } + SyntaxKind::ItemInlineMacro => { + vec![] + } + SyntaxKind::LegacyExprInlineMacro => { + vec![] + } + SyntaxKind::LegacyItemInlineMacro => { + vec![] + } SyntaxKind::TriviumSkippedNode => { vec![] } diff --git a/crates/cairo-lang-syntax/src/node/kind.rs b/crates/cairo-lang-syntax/src/node/kind.rs index a1a6cf49e1d..41eef1ec0a7 100644 --- a/crates/cairo-lang-syntax/src/node/kind.rs +++ b/crates/cairo-lang-syntax/src/node/kind.rs @@ -38,7 +38,6 @@ pub enum SyntaxKind { OptionElseClauseEmpty, ExprErrorPropagate, ExprIndexed, - ExprInlineMacro, ExprFixedSizeArray, FixedSizeArraySize, OptionFixedSizeArraySizeEmpty, @@ -114,7 +113,6 @@ pub enum SyntaxKind { TraitItemConstant, TraitItemImpl, ItemImpl, - ItemInlineMacro, ItemHeaderDoc, ImplBody, ImplItemList, @@ -143,6 +141,18 @@ pub enum SyntaxKind { GenericParamImplNamed, GenericParamImplAnonymous, GenericParamNegativeImpl, + TokenList, + TokenTreeLeaf, + TokenTreeNode, + TokenTreeMissing, + WrappedTokenTreeMissing, + ParenthesizedTokenTree, + BracedTokenTree, + BracketedTokenTree, + ExprInlineMacro, + ItemInlineMacro, + LegacyExprInlineMacro, + LegacyItemInlineMacro, TriviumSkippedNode, TokenIdentifier, TerminalIdentifier, diff --git a/crates/cairo-lang-syntax/src/node/mod.rs b/crates/cairo-lang-syntax/src/node/mod.rs index e9a55f94cd3..6943737add1 100644 --- a/crates/cairo-lang-syntax/src/node/mod.rs +++ b/crates/cairo-lang-syntax/src/node/mod.rs @@ -55,6 +55,21 @@ impl SyntaxNode { Self(Arc::new(inner)) } + pub fn new_root_with_offset( + db: &dyn SyntaxGroup, + file_id: FileId, + green: GreenId, + offset: TextOffset, + ) -> Self { + let inner = SyntaxNodeInner { + green, + offset, + parent: None, + stable_ptr: SyntaxStablePtr::Root(file_id, green).intern(db), + }; + Self(Arc::new(inner)) + } + pub fn offset(&self) -> TextOffset { self.0.offset } diff --git a/crates/cairo-lang-test-plugin/Cargo.toml b/crates/cairo-lang-test-plugin/Cargo.toml index 6820abe1d27..9f9786010f4 100644 --- a/crates/cairo-lang-test-plugin/Cargo.toml +++ b/crates/cairo-lang-test-plugin/Cargo.toml @@ -13,6 +13,7 @@ cairo-lang-debug = { path = "../cairo-lang-debug", version = "~2.8.4" } cairo-lang-defs = { path = "../cairo-lang-defs", version = "~2.8.4" } cairo-lang-filesystem = { path = "../cairo-lang-filesystem", version = "~2.8.4" } cairo-lang-lowering = { path = "../cairo-lang-lowering", version = "~2.8.4" } +cairo-lang-parser = { path = "../cairo-lang-parser", version = "~2.8.4" } cairo-lang-semantic = { path = "../cairo-lang-semantic", version = "~2.8.4" } cairo-lang-sierra = { path = "../cairo-lang-sierra", version = "~2.8.4" } cairo-lang-sierra-generator = { path = "../cairo-lang-sierra-generator", version = "~2.8.4" } diff --git a/crates/cairo-lang-test-plugin/src/inline_macros/assert.rs b/crates/cairo-lang-test-plugin/src/inline_macros/assert.rs index 547237afd59..1ef8ec62a31 100644 --- a/crates/cairo-lang-test-plugin/src/inline_macros/assert.rs +++ b/crates/cairo-lang-test-plugin/src/inline_macros/assert.rs @@ -4,9 +4,11 @@ use cairo_lang_defs::plugin::{ PluginGeneratedFile, }; use cairo_lang_defs::plugin_utils::{ - escape_node, try_extract_unnamed_arg, unsupported_bracket_diagnostic, + PluginResultTrait, escape_node, not_legacy_macro_diagnostic, try_extract_unnamed_arg, + unsupported_bracket_diagnostic, }; use cairo_lang_filesystem::cfg::Cfg; +use cairo_lang_parser::macro_helpers::AsLegacyInlineMacro; use cairo_lang_syntax::node::ast::WrappedArgList; use cairo_lang_syntax::node::db::SyntaxGroup; use cairo_lang_syntax::node::{TypedSyntaxNode, ast}; @@ -22,8 +24,15 @@ trait CompareAssertionPlugin: NamedPlugin { syntax: &ast::ExprInlineMacro, metadata: &MacroPluginMetadata<'_>, ) -> InlinePluginResult { - let WrappedArgList::ParenthesizedArgList(arguments_syntax) = syntax.arguments(db) else { - return unsupported_bracket_diagnostic(db, syntax); + let Some(legacy_inline_macro) = syntax.as_legacy_inline_macro(db) else { + return InlinePluginResult::diagnostic_only(not_legacy_macro_diagnostic( + syntax.as_syntax_node().stable_ptr(), + )); + }; + let WrappedArgList::ParenthesizedArgList(arguments_syntax) = + legacy_inline_macro.arguments(db) + else { + return unsupported_bracket_diagnostic(db, &legacy_inline_macro, syntax); }; let arguments = arguments_syntax.arguments(db).elements(db); if arguments.len() < 2 { @@ -40,8 +49,10 @@ trait CompareAssertionPlugin: NamedPlugin { let Some(lhs) = try_extract_unnamed_arg(db, lhs) else { return InlinePluginResult { code: None, - diagnostics: vec![PluginDiagnostic::error( - lhs, + diagnostics: vec![PluginDiagnostic::error_with_inner_span( + db, + syntax.stable_ptr(), + lhs.as_syntax_node(), format!("Macro `{}` requires the first argument to be unnamed.", Self::NAME), )], }; @@ -49,8 +60,10 @@ trait CompareAssertionPlugin: NamedPlugin { let Some(rhs) = try_extract_unnamed_arg(db, rhs) else { return InlinePluginResult { code: None, - diagnostics: vec![PluginDiagnostic::error( - rhs, + diagnostics: vec![PluginDiagnostic::error_with_inner_span( + db, + syntax.stable_ptr(), + rhs.as_syntax_node(), format!("Macro `{}` requires the second argument to be unnamed.", Self::NAME), )], }; @@ -170,7 +183,7 @@ trait CompareAssertionPlugin: NamedPlugin { && !metadata.cfg_set.contains(&Cfg::name("test")) { diagnostics.push(PluginDiagnostic::error( - syntax, + syntax.stable_ptr(), format!("`{}` macro is only available in test mode.", Self::NAME), )); }