cairo_lang_defs/
diagnostic_utils.rs

1use std::fmt;
2
3use cairo_lang_debug::DebugWithDb;
4use cairo_lang_filesystem::ids::{FileId, SpanInFile};
5use cairo_lang_filesystem::span::{TextSpan, TextWidth};
6use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
7use cairo_lang_syntax::node::{SyntaxNode, TypedSyntaxNode};
8use salsa::Database;
9
10/// A stable location of a real, concrete syntax.
11#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, salsa::Update)]
12pub struct StableLocation<'db> {
13    stable_ptr: SyntaxStablePtrId<'db>,
14    /// An optional inner span of the stable location. Useful for diagnostics caused by inline
15    /// macros, see [crate::plugin::PluginDiagnostic] for more information. The tuple is (offset,
16    /// width).
17    inner_span: Option<(TextWidth, TextWidth)>,
18}
19
20impl<'db> StableLocation<'db> {
21    pub fn new(stable_ptr: SyntaxStablePtrId<'db>) -> Self {
22        Self { stable_ptr, inner_span: None }
23    }
24
25    pub fn with_inner_span(
26        stable_ptr: SyntaxStablePtrId<'db>,
27        inner_span: (TextWidth, TextWidth),
28    ) -> Self {
29        Self { stable_ptr, inner_span: Some(inner_span) }
30    }
31
32    pub fn file_id(&self, db: &'db dyn Database) -> FileId<'db> {
33        self.stable_ptr.file_id(db)
34    }
35
36    pub fn from_ast<TNode: TypedSyntaxNode<'db>>(db: &'db dyn Database, node: &TNode) -> Self {
37        Self::new(node.as_syntax_node().stable_ptr(db))
38    }
39
40    /// Returns the [SyntaxNode] that corresponds to the [StableLocation].
41    pub fn syntax_node(&self, db: &'db dyn Database) -> SyntaxNode<'db> {
42        self.stable_ptr.lookup(db)
43    }
44
45    /// Returns the [SyntaxStablePtrId] of the [StableLocation].
46    pub fn stable_ptr(&self) -> SyntaxStablePtrId<'db> {
47        self.stable_ptr
48    }
49
50    /// Returns the [SpanInFile] that corresponds to the [StableLocation].
51    pub fn span_in_file(&self, db: &'db dyn Database) -> SpanInFile<'db> {
52        match self.inner_span {
53            Some((start, width)) => {
54                let start = self.syntax_node(db).offset(db).add_width(start);
55                SpanInFile {
56                    file_id: self.file_id(db),
57                    span: TextSpan::new_with_width(start, width),
58                }
59            }
60            None => {
61                let syntax_node = self.syntax_node(db);
62                SpanInFile { file_id: self.file_id(db), span: syntax_node.span_without_trivia(db) }
63            }
64        }
65    }
66
67    /// Returns the [SpanInFile] that starts at the [StableLocation], and ends at the given
68    /// [SyntaxStablePtrId].
69    pub fn span_in_file_until(
70        &self,
71        db: &'db dyn Database,
72        until_stable_ptr: SyntaxStablePtrId<'db>,
73    ) -> SpanInFile<'db> {
74        let start = self.stable_ptr.lookup(db).span_start_without_trivia(db);
75        let end = until_stable_ptr.lookup(db).span_end_without_trivia(db);
76        SpanInFile { file_id: self.stable_ptr.file_id(db), span: TextSpan::new(start, end) }
77    }
78}
79
80impl<'db> DebugWithDb<'db> for StableLocation<'db> {
81    type Db = dyn Database;
82
83    fn fmt(&self, f: &mut fmt::Formatter<'_>, db: &'db dyn Database) -> fmt::Result {
84        let diag_location = self.span_in_file(db);
85        diag_location.fmt_location(f, db)
86    }
87}