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