diff --git a/crates/cairo-lang-sierra-generator/src/debug_info/function_debug_info.rs b/crates/cairo-lang-sierra-generator/src/debug_info/function_debug_info.rs index 07bbf3b6c97..a7d4b8249d9 100644 --- a/crates/cairo-lang-sierra-generator/src/debug_info/function_debug_info.rs +++ b/crates/cairo-lang-sierra-generator/src/debug_info/function_debug_info.rs @@ -1,6 +1,69 @@ +use std::collections::HashMap; + +use cairo_lang_defs::db::get_all_path_leaves; +use cairo_lang_defs::diagnostic_utils::StableLocation; +use cairo_lang_defs::ids::{ + ConstantLongId, EnumLongId, ExternFunctionLongId, ExternTypeLongId, FreeFunctionLongId, + ImplAliasLongId, ImplConstantDefLongId, ImplDefLongId, ImplFunctionLongId, ImplItemId, + LanguageElementId, LookupItemId, MacroDeclarationLongId, ModuleId, ModuleItemId, + ModuleTypeAliasLongId, StructLongId, TraitConstantLongId, TraitFunctionLongId, TraitImplLongId, + TraitItemId, TraitLongId, TraitTypeLongId, UseLongId, +}; +use cairo_lang_filesystem::db::FilesGroup; +use cairo_lang_lowering::Location; use cairo_lang_lowering::ids::LocationId; -use cairo_lang_sierra::ids::VarId; +use cairo_lang_semantic::Expr; +use cairo_lang_semantic::db::SemanticGroup; +use cairo_lang_semantic::expr::pattern::QueryPatternVariablesFromDb; +use cairo_lang_semantic::items::function_with_body::{ + FunctionWithBodySemantic, SemanticExprLookup, +}; +use cairo_lang_semantic::lookup_item::LookupItemEx; +use cairo_lang_semantic::lsp_helpers::LspHelpers; +use cairo_lang_semantic::resolve::ResolvedGenericItem; +use cairo_lang_sierra::ids::{FunctionId, VarId}; +use cairo_lang_syntax::node::ast::{ + BinaryOperator, ExprBinary, ParamList, StatementLet, TerminalIdentifier, +}; +use cairo_lang_syntax::node::kind::SyntaxKind; +use cairo_lang_syntax::node::{SyntaxNode, Terminal, TypedStablePtr, TypedSyntaxNode, ast}; +use cairo_lang_utils::Intern; use cairo_lang_utils::ordered_hash_map::OrderedHashMap; +use salsa::Database; + +use crate::debug_info::function_debug_info::serializable::{ + CairoVariableName, SerializableAllFunctionsDebugInfo, SerializableFunctionDebugInfo, +}; +use crate::debug_info::{SourceCodeSpan, SourceFileFullPath, maybe_code_location}; + +pub mod serializable; + +/// The debug info of all sierra functions in the program. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AllFunctionsDebugInfo<'db>(OrderedHashMap>); + +impl<'db> AllFunctionsDebugInfo<'db> { + pub fn new(value: OrderedHashMap>) -> Self { + Self(value) + } + + pub fn extract_serializable_debug_info( + &self, + db: &'db dyn Database, + ) -> SerializableAllFunctionsDebugInfo { + SerializableAllFunctionsDebugInfo( + self.0 + .iter() + .filter_map(|(function_id, function_debug_info)| { + Some(( + function_id.clone(), + function_debug_info.extract_serializable_debug_info(db)?, + )) + }) + .collect(), + ) + } +} /// The debug info of a sierra function. /// Contains a signature location and locations of sierra variables of this function. @@ -9,3 +72,447 @@ pub struct FunctionDebugInfo<'db> { pub signature_location: LocationId<'db>, pub variables_locations: OrderedHashMap>, } + +impl<'db> FunctionDebugInfo<'db> { + fn extract_serializable_debug_info( + &self, + db: &'db dyn Database, + ) -> Option { + let (function_file_path, function_code_span) = self.extract_location(db)?; + let sierra_to_cairo_variable = + self.extract_variables_mapping(db, &function_file_path, &function_code_span); + + Some(SerializableFunctionDebugInfo { + function_file_path, + function_code_span, + sierra_to_cairo_variable, + }) + } + + /// Extracts mapping from a sierra variable to a cairo variable (its name and definition span). + /// The sierra variable value corresponds to the cairo variable value at some point during + /// execution of the function code. + fn extract_variables_mapping( + &self, + db: &'db dyn Database, + function_file_path: &SourceFileFullPath, + function_code_span: &SourceCodeSpan, + ) -> HashMap { + self.variables_locations + .iter() + .filter_map(|(sierra_var, code_location)| { + // Ignore inline locations for now as this function is supposed to be called only + // on when sierra was compiled with inlining set to `avoid`. + // TODO(pm): handle them to make sure user function marked #[inline(always)] work. + // https://github.com/software-mansion-labs/cairo-debugger/issues/41 + let Location { + stable_location: sierra_var_location, + notes: _, + inline_locations: _, + } = code_location.long(db); + + let identifier = find_identifier_corresponding_to_assigned_variable( + db, + sierra_var_location.syntax_node(db), + )?; + + let module_id = db.find_module_containing_node(identifier.as_syntax_node())?; + + let lookup_items: Vec<_> = identifier + .as_syntax_node() + .ancestors_with_self(db) + .flat_map(|node| lookup_item_from_ast(db, module_id, node).unwrap_or_default()) + .collect(); + + let definition = + try_variable_declaration(db, &identifier, &lookup_items).or_else(|| { + lookup_variable_in_resolved_items(db, &identifier, &lookup_items) + })?; + + let location = match definition { + // Extract only param name. + cairo_lang_defs::ids::VarId::Param(param) => StableLocation::new( + param.stable_ptr(db).lookup(db).name(db).stable_ptr(db).untyped(), + ), + x => x.stable_location(db), + }; + + let (path, span, _) = maybe_code_location(db, location)?; + + // Sanity check. + // TODO(pm): check if it occurs at all except for inlined function calls. + if !(function_file_path == &path && function_code_span.contains(&span)) { + return None; + } + + let user_location = location.span_in_file(db).user_location(db); + let var_name = user_location + .span + .take(db.file_content(user_location.file_id).unwrap()) + .to_string(); + + Some((sierra_var.clone(), (var_name, span))) + }) + .collect() + } + + /// Extracts the location of the function - path to the user file it comes from and its span. + fn extract_location( + &self, + db: &'db dyn Database, + ) -> Option<(SourceFileFullPath, SourceCodeSpan)> { + let function_ptr = self + .signature_location + .long(db) + .stable_location + .syntax_node(db) + .ancestor_of_kinds(db, &[SyntaxKind::FunctionWithBody, SyntaxKind::TraitItemFunction])? + .stable_ptr(db); + let (function_file_path, function_code_span, _) = + maybe_code_location(db, StableLocation::new(function_ptr))?; + + Some((function_file_path, function_code_span)) + } +} + +/// This function gets a node that a sierra variable was mapped to. +/// It tries to make an educated guess to find an identifier corresponding to a cairo variable +/// which value in the given location is represented by the sierra variable. +/// +/// The algorithm is to find an identifier corresponding to a variable which either: +/// - is assigned a value in the statement the `node` is a part of, +/// - is part of a function's param, and `node` is a part of the function's params list. +/// +/// If there is no such identifier - `None` is returned. +fn find_identifier_corresponding_to_assigned_variable<'db>( + db: &'db dyn Database, + node: SyntaxNode<'db>, +) -> Option> { + let is_param = node.ancestor_of_type::>(db).is_some(); + if is_param { + return find_identifier_in_node(db, node); + } + + let assignment_expr = node.ancestors_with_self(db).find_map(|node| { + let binary = ExprBinary::cast(db, node)?; + match binary.op(db) { + BinaryOperator::MulEq(_) + | BinaryOperator::DivEq(_) + | BinaryOperator::ModEq(_) + | BinaryOperator::PlusEq(_) + | BinaryOperator::MinusEq(_) + | BinaryOperator::Eq(_) => Some(binary), + _ => None, + } + }); + + if let Some(assignment_expr) = assignment_expr { + let lhs = assignment_expr.lhs(db).as_syntax_node(); + + // Case when sierra var maps to a part of lhs. + if node.ancestors_with_self(db).any(|node| node == lhs) { + return find_identifier_in_node(db, node); + } + + // Sometimes a sierra var maps to the whole expression like `x += y`. + if node == assignment_expr.as_syntax_node() { + return find_identifier_in_node(db, lhs); + } + + // If the original node covers whole rhs of the assignment and the operator is a simple eq, + // we should try to search for the identifier on the lhs. + if assignment_expr.op(db).as_syntax_node().kind(db) == SyntaxKind::TerminalEq + && node == assignment_expr.rhs(db).as_syntax_node() + { + return find_identifier_in_node(db, lhs); + } + } + + if let Some(let_statement) = node.ancestors(db).find_map(|node| StatementLet::cast(db, node)) { + let pattern = let_statement.pattern(db).as_syntax_node(); + + let is_on_pattern = node.ancestors_with_self(db).any(|node| node == pattern); + if is_on_pattern { + return find_identifier_in_node(db, node); + } + + // If the original node covers whole rhs of the let statement, we should try to search + // for the identifier on the pattern. + if node == let_statement.rhs(db).as_syntax_node() { + return find_identifier_in_node(db, pattern); + } + } + + None +} + +/// Tries to find an identifier in the node unambiguously, meaning if either: +/// - the node is an identifier +/// - the node is a token of an identifier, +/// - there is exactly one identifier in descendants of the node, +/// +/// it returns the aforementioned identifier. +fn find_identifier_in_node<'db>( + db: &'db dyn Database, + node: SyntaxNode<'db>, +) -> Option> { + TerminalIdentifier::cast_token(db, node).or_else(|| TerminalIdentifier::cast(db, node)).or_else( + || { + let mut terminal_descendants: Vec<_> = node + .descendants(db) + .filter_map(|node| TerminalIdentifier::cast_token(db, node)) + .collect(); + if terminal_descendants.len() == 1 { terminal_descendants.pop() } else { None } + }, + ) +} + +/// If the ast node is a lookup item, return corresponding ids. +/// Otherwise, returns `None`. +/// +/// Code from . +fn lookup_item_from_ast<'db>( + db: &'db dyn Database, + module_id: ModuleId<'db>, + node: SyntaxNode<'db>, +) -> Option>> { + let syntax_db = db; + + let is_in_impl = node.ancestor_of_kind(syntax_db, SyntaxKind::ItemImpl).is_some(); + + Some(match node.kind(syntax_db) { + SyntaxKind::ItemConstant => { + if is_in_impl { + vec![LookupItemId::ImplItem(ImplItemId::Constant( + ImplConstantDefLongId( + module_id, + ast::ItemConstant::from_syntax_node(syntax_db, node).stable_ptr(syntax_db), + ) + .intern(db), + ))] + } else { + vec![LookupItemId::ModuleItem(ModuleItemId::Constant( + ConstantLongId( + module_id, + ast::ItemConstant::from_syntax_node(db, node).stable_ptr(db), + ) + .intern(db), + ))] + } + } + SyntaxKind::FunctionWithBody => { + if is_in_impl { + vec![LookupItemId::ImplItem(ImplItemId::Function( + ImplFunctionLongId( + module_id, + ast::FunctionWithBody::from_syntax_node(db, node).stable_ptr(db), + ) + .intern(db), + ))] + } else { + vec![LookupItemId::ModuleItem(ModuleItemId::FreeFunction( + FreeFunctionLongId( + module_id, + ast::FunctionWithBody::from_syntax_node(db, node).stable_ptr(db), + ) + .intern(db), + ))] + } + } + SyntaxKind::ItemExternFunction => { + vec![LookupItemId::ModuleItem(ModuleItemId::ExternFunction( + ExternFunctionLongId( + module_id, + ast::ItemExternFunction::from_syntax_node(db, node).stable_ptr(db), + ) + .intern(db), + ))] + } + SyntaxKind::ItemExternType => vec![LookupItemId::ModuleItem(ModuleItemId::ExternType( + ExternTypeLongId( + module_id, + ast::ItemExternType::from_syntax_node(db, node).stable_ptr(db), + ) + .intern(db), + ))], + SyntaxKind::ItemTrait => { + vec![LookupItemId::ModuleItem(ModuleItemId::Trait( + TraitLongId(module_id, ast::ItemTrait::from_syntax_node(db, node).stable_ptr(db)) + .intern(db), + ))] + } + SyntaxKind::TraitItemConstant => { + vec![LookupItemId::TraitItem(TraitItemId::Constant( + TraitConstantLongId( + module_id, + ast::TraitItemConstant::from_syntax_node(db, node).stable_ptr(db), + ) + .intern(db), + ))] + } + SyntaxKind::TraitItemFunction => { + vec![LookupItemId::TraitItem(TraitItemId::Function( + TraitFunctionLongId( + module_id, + ast::TraitItemFunction::from_syntax_node(db, node).stable_ptr(db), + ) + .intern(db), + ))] + } + SyntaxKind::TraitItemImpl => { + vec![LookupItemId::TraitItem(TraitItemId::Impl( + TraitImplLongId( + module_id, + ast::TraitItemImpl::from_syntax_node(db, node).stable_ptr(db), + ) + .intern(db), + ))] + } + SyntaxKind::TraitItemType => { + vec![LookupItemId::TraitItem(TraitItemId::Type( + TraitTypeLongId( + module_id, + ast::TraitItemType::from_syntax_node(db, node).stable_ptr(db), + ) + .intern(db), + ))] + } + SyntaxKind::ItemImpl => { + vec![LookupItemId::ModuleItem(ModuleItemId::Impl( + ImplDefLongId(module_id, ast::ItemImpl::from_syntax_node(db, node).stable_ptr(db)) + .intern(db), + ))] + } + SyntaxKind::ItemStruct => { + vec![LookupItemId::ModuleItem(ModuleItemId::Struct( + StructLongId(module_id, ast::ItemStruct::from_syntax_node(db, node).stable_ptr(db)) + .intern(db), + ))] + } + SyntaxKind::ItemEnum => { + vec![LookupItemId::ModuleItem(ModuleItemId::Enum( + EnumLongId(module_id, ast::ItemEnum::from_syntax_node(db, node).stable_ptr(db)) + .intern(db), + ))] + } + SyntaxKind::ItemUse => { + // Item use is not a lookup item, so we need to collect all UseLeaf, which are lookup + // items. + let item_use = ast::ItemUse::from_syntax_node(db, node); + get_all_path_leaves(db, &item_use) + .into_iter() + .map(|leaf| { + let use_long_id = UseLongId(module_id, leaf.stable_ptr(syntax_db)); + LookupItemId::ModuleItem(ModuleItemId::Use(use_long_id.intern(db))) + }) + .collect() + } + SyntaxKind::ItemTypeAlias => vec![LookupItemId::ModuleItem(ModuleItemId::TypeAlias( + ModuleTypeAliasLongId( + module_id, + ast::ItemTypeAlias::from_syntax_node(db, node).stable_ptr(db), + ) + .intern(db), + ))], + SyntaxKind::ItemImplAlias => vec![LookupItemId::ModuleItem(ModuleItemId::ImplAlias( + ImplAliasLongId( + module_id, + ast::ItemImplAlias::from_syntax_node(db, node).stable_ptr(db), + ) + .intern(db), + ))], + SyntaxKind::ItemMacroDeclaration => { + vec![LookupItemId::ModuleItem(ModuleItemId::MacroDeclaration( + MacroDeclarationLongId( + module_id, + ast::ItemMacroDeclaration::from_syntax_node(db, node).stable_ptr(db), + ) + .intern(db), + ))] + } + _ => return None, + }) +} + +/// Lookups if the identifier is a declaration of a variable/param in one of the lookup items. +/// +/// Declaration identifiers aren't kept in `ResolvedData`, which is searched for by +/// `lookup_resolved_generic_item_by_ptr` and `lookup_resolved_concrete_item_by_ptr`. +/// Therefore, we have to look for these ourselves. +/// +/// Code from . +fn try_variable_declaration<'db>( + db: &'db dyn Database, + identifier: &TerminalIdentifier<'db>, + lookup_items: &[LookupItemId<'db>], +) -> Option> { + let function_id = lookup_items.first()?.function_with_body()?; + + // Look at function parameters. + if let Some(param) = identifier + .as_syntax_node() + .parent_of_type::>(db) + .filter(|param| param.name(db) == *identifier) + { + // Closures have different semantic model structures than regular functions. + let params = if let Some(expr_closure_ast) = + param.as_syntax_node().ancestor_of_type::>(db) + { + let expr_id = + db.lookup_expr_by_ptr(function_id, expr_closure_ast.stable_ptr(db).into()).ok()?; + + let Expr::ExprClosure(expr_closure_semantic) = db.expr_semantic(function_id, expr_id) + else { + // Break in case Expr::Missing was here. + return None; + }; + expr_closure_semantic.params + } else { + let signature = db.function_with_body_signature(function_id).ok()?; + signature.params.clone() + }; + + if let Some(param) = + params.into_iter().find(|param| param.stable_ptr == identifier.stable_ptr(db)) + { + let var_id = cairo_lang_defs::ids::VarId::Param(param.id); + return Some(var_id); + } + } + + // Look at identifier patterns in the function body. + if let Some(pattern_ast) = identifier.as_syntax_node().ancestor_of_type::>(db) + { + let pattern_id = db.lookup_pattern_by_ptr(function_id, pattern_ast.stable_ptr(db)).ok()?; + let pattern = db.pattern_semantic(function_id, pattern_id); + + let pattern_variable = pattern + .variables(&QueryPatternVariablesFromDb(db, function_id)) + .into_iter() + .find(|var| var.name == identifier.text(db))?; + let var_id = cairo_lang_defs::ids::VarId::Local(pattern_variable.var.id); + return Some(var_id); + } + + None +} + +/// Searches for a variable corresponding to the identifier using +/// `lookup_resolved_generic_item_by_ptr`. +fn lookup_variable_in_resolved_items<'db>( + db: &'db dyn Database, + identifier: &TerminalIdentifier<'db>, + lookup_items: &[LookupItemId<'db>], +) -> Option> { + let ptr = identifier.stable_ptr(db); + + for &lookup_item_id in lookup_items { + if let Some(ResolvedGenericItem::Variable(var)) = + db.lookup_resolved_generic_item_by_ptr(lookup_item_id, ptr) + { + return Some(var); + } + } + + None +} diff --git a/crates/cairo-lang-sierra-generator/src/debug_info/function_debug_info/serializable.rs b/crates/cairo-lang-sierra-generator/src/debug_info/function_debug_info/serializable.rs new file mode 100644 index 00000000000..59512cee1be --- /dev/null +++ b/crates/cairo-lang-sierra-generator/src/debug_info/function_debug_info/serializable.rs @@ -0,0 +1,38 @@ +use std::collections::HashMap; + +use cairo_lang_sierra::debug_info::Annotations; +use cairo_lang_sierra::ids::{FunctionId, VarId}; +use cairo_lang_utils::ordered_hash_map::OrderedHashMap; +use serde::{Deserialize, Serialize}; + +use crate::debug_info::{SourceCodeSpan, SourceFileFullPath}; + +/// The serializable debug info of all sierra functions in the program. +pub struct SerializableAllFunctionsDebugInfo( + pub(super) HashMap, +); + +impl From for Annotations { + fn from(value: SerializableAllFunctionsDebugInfo) -> Self { + let mapping = serde_json::to_value(value.0).unwrap(); + OrderedHashMap::from([( + "github.com/software-mansion-labs/cairo-debugger".to_string(), + serde_json::Value::from_iter([("functions_info", mapping)]), + )]) + } +} + +/// The serializable debug info of a sierra function. +#[derive(Serialize, Deserialize)] +pub struct SerializableFunctionDebugInfo { + /// Path to the user file the function comes from. + pub function_file_path: SourceFileFullPath, + /// Span of the function in the user file it comes from. + pub function_code_span: SourceCodeSpan, + /// Mapping from a sierra variable to a cairo variable (its name and definition span). + /// The sierra variable value corresponds to the cairo variable value at some point during + /// execution of the function code. + pub sierra_to_cairo_variable: HashMap, +} + +pub type CairoVariableName = String; diff --git a/crates/cairo-lang-sierra-generator/src/debug_info/mod.rs b/crates/cairo-lang-sierra-generator/src/debug_info/mod.rs index bfd86ec3356..f2a834a7059 100644 --- a/crates/cairo-lang-sierra-generator/src/debug_info/mod.rs +++ b/crates/cairo-lang-sierra-generator/src/debug_info/mod.rs @@ -1,18 +1,69 @@ -use cairo_lang_sierra::ids::FunctionId; -use cairo_lang_utils::ordered_hash_map::OrderedHashMap; +use cairo_lang_defs::diagnostic_utils::StableLocation; +use cairo_lang_filesystem::ids::FileLongId; +use salsa::Database; +use serde::{Deserialize, Serialize}; mod function_debug_info; mod statements_locations; -pub use function_debug_info::FunctionDebugInfo; -pub use statements_locations::StatementsLocations; -pub use statements_locations::statements_code_locations::{ - SourceCodeLocation, SourceCodeSpan, SourceFileFullPath, StatementsSourceCodeLocations, +pub use function_debug_info::serializable::{ + SerializableAllFunctionsDebugInfo, SerializableFunctionDebugInfo, }; +pub use function_debug_info::{AllFunctionsDebugInfo, FunctionDebugInfo}; +pub use statements_locations::StatementsLocations; +pub use statements_locations::statements_code_locations::StatementsSourceCodeLocations; pub use statements_locations::statements_functions::StatementsFunctions; #[derive(Clone, Debug, Eq, PartialEq)] pub struct SierraProgramDebugInfo<'db> { pub statements_locations: StatementsLocations<'db>, - pub functions_info: OrderedHashMap>, + pub functions_info: AllFunctionsDebugInfo<'db>, +} + +/// A full path to a Cairo source file. +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +pub struct SourceFileFullPath(pub String); + +/// A location in a Cairo source file. +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +pub struct SourceCodeLocation { + /// Line index, 0 based. + pub line: usize, + /// Character index inside the line, 0 based. + pub col: usize, +} + +/// A location in a Cairo source file. +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +pub struct SourceCodeSpan { + /// Beginning of the text span in the Cairo source file. + pub start: SourceCodeLocation, + /// End of the text span in the Cairo source file, not included. + pub end: SourceCodeLocation, +} + +impl SourceCodeSpan { + pub fn contains(&self, other: &Self) -> bool { + self.start <= other.start && self.end >= other.end + } +} + +/// Returns a location in the user file corresponding to the given [StableLocation]. +/// It consists of a full path to the file, a text span in the file and a boolean indicating +/// if the location is a part of a macro expansion. +fn maybe_code_location<'db>( + db: &'db dyn Database, + location: StableLocation<'db>, +) -> Option<(SourceFileFullPath, SourceCodeSpan, bool)> { + let is_macro = + matches!(location.file_id(db).long(db), FileLongId::Virtual(_) | FileLongId::External(_)); + let location = location.span_in_file(db).user_location(db); + let file_full_path = location.file_id.full_path(db); + let position = location.span.position_in_file(db, location.file_id)?; + let source_location = SourceCodeSpan { + start: SourceCodeLocation { col: position.start.col, line: position.start.line }, + end: SourceCodeLocation { col: position.end.col, line: position.end.line }, + }; + + Some((SourceFileFullPath(file_full_path), source_location, is_macro)) } diff --git a/crates/cairo-lang-sierra-generator/src/debug_info/statements_locations.rs b/crates/cairo-lang-sierra-generator/src/debug_info/statements_locations.rs index 0d505610af8..2c15cfe98a1 100644 --- a/crates/cairo-lang-sierra-generator/src/debug_info/statements_locations.rs +++ b/crates/cairo-lang-sierra-generator/src/debug_info/statements_locations.rs @@ -6,11 +6,10 @@ use cairo_lang_utils::unordered_hash_map::UnorderedHashMap; use itertools::Itertools; use salsa::{Database, par_map}; -use crate::debug_info::statements_locations::statements_code_locations::maybe_code_location; use crate::debug_info::statements_locations::statements_functions::{ maybe_containing_function_identifier, maybe_containing_function_identifier_for_tests, }; -use crate::debug_info::{StatementsFunctions, StatementsSourceCodeLocations}; +use crate::debug_info::{StatementsFunctions, StatementsSourceCodeLocations, maybe_code_location}; pub mod statements_code_locations; pub mod statements_functions; diff --git a/crates/cairo-lang-sierra-generator/src/debug_info/statements_locations/statements_code_locations.rs b/crates/cairo-lang-sierra-generator/src/debug_info/statements_locations/statements_code_locations.rs index 745cefaca4f..f5905ffd266 100644 --- a/crates/cairo-lang-sierra-generator/src/debug_info/statements_locations/statements_code_locations.rs +++ b/crates/cairo-lang-sierra-generator/src/debug_info/statements_locations/statements_code_locations.rs @@ -1,34 +1,11 @@ use std::collections::HashMap; -use cairo_lang_defs::diagnostic_utils::StableLocation; -use cairo_lang_filesystem::ids::FileLongId; use cairo_lang_sierra::debug_info::Annotations; use cairo_lang_sierra::program::StatementIdx; use cairo_lang_utils::ordered_hash_map::OrderedHashMap; -use salsa::Database; use serde::{Deserialize, Serialize}; -/// A full path to a Cairo source file. -#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -pub struct SourceFileFullPath(pub String); - -/// A location in a Cairo source file. -#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -pub struct SourceCodeLocation { - /// Line index, 0 based. - pub line: usize, - /// Character index inside the line, 0 based. - pub col: usize, -} - -/// A location in a Cairo source file. -#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -pub struct SourceCodeSpan { - /// Beginning of the text span in the Cairo source file. - pub start: SourceCodeLocation, - /// End of the text span in the Cairo source file, not included. - pub end: SourceCodeLocation, -} +use crate::debug_info::{SourceCodeSpan, SourceFileFullPath}; /// The mapping between Sierra statement indexes and locations in Cairo code /// (if obtainable) which caused the statement to be generated. Contains the additional information @@ -51,23 +28,3 @@ impl From for Annotations { )]) } } - -/// Returns a location in the user file corresponding to the given [StableLocation]. -/// It consists of a full path to the file, a text span in the file and a boolean indicating -/// if the location is a part of a macro expansion. -pub fn maybe_code_location<'db>( - db: &'db dyn Database, - location: StableLocation<'db>, -) -> Option<(SourceFileFullPath, SourceCodeSpan, bool)> { - let is_macro = - matches!(location.file_id(db).long(db), FileLongId::Virtual(_) | FileLongId::External(_)); - let location = location.span_in_file(db).user_location(db); - let file_full_path = location.file_id.full_path(db); - let position = location.span.position_in_file(db, location.file_id)?; - let source_location = SourceCodeSpan { - start: SourceCodeLocation { col: position.start.col, line: position.start.line }, - end: SourceCodeLocation { col: position.end.col, line: position.end.line }, - }; - - Some((SourceFileFullPath(file_full_path), source_location, is_macro)) -} diff --git a/crates/cairo-lang-sierra-generator/src/program_generator.rs b/crates/cairo-lang-sierra-generator/src/program_generator.rs index eeff7636325..dd617f6b1a1 100644 --- a/crates/cairo-lang-sierra-generator/src/program_generator.rs +++ b/crates/cairo-lang-sierra-generator/src/program_generator.rs @@ -18,7 +18,9 @@ use itertools::{Itertools, chain}; use salsa::Database; use crate::db::{SierraGenGroup, sierra_concrete_long_id}; -use crate::debug_info::{FunctionDebugInfo, SierraProgramDebugInfo, StatementsLocations}; +use crate::debug_info::{ + AllFunctionsDebugInfo, FunctionDebugInfo, SierraProgramDebugInfo, StatementsLocations, +}; use crate::extra_sierra_info::type_has_const_size; use crate::pre_sierra; use crate::replace_ids::{DebugReplacer, SierraIdReplacer}; @@ -289,7 +291,7 @@ pub fn get_sierra_program_for_functions<'db>( program, debug_info: SierraProgramDebugInfo { statements_locations: StatementsLocations::from_locations_vec(db, statements_locations), - functions_info, + functions_info: AllFunctionsDebugInfo::new(functions_info), }, }) }