oq3_syntax/
ptr.rs

1// Copyright contributors to the openqasm-parser project
2// SPDX-License-Identifier: Apache-2.0
3
4//! In rust-analyzer, syntax trees are transient objects.
5//!
6//! That means that we create trees when we need them, and tear them down to
7//! save memory. In this architecture, hanging on to a particular syntax node
8//! for a long time is ill-advisable, as that keeps the whole tree resident.
9//!
10//! Instead, we provide a [`SyntaxNodePtr`] type, which stores information about
11//! *location* of a particular syntax node in a tree. Its a small type which can
12//! be cheaply stored, and which can be resolved to a real [`SyntaxNode`] when
13//! necessary.
14
15use std::{
16    hash::{Hash, Hasher},
17    marker::PhantomData,
18};
19
20use rowan::TextRange;
21
22use crate::{syntax_node::OpenQASM3Language, AstNode, SyntaxNode};
23
24/// A "pointer" to a [`SyntaxNode`], via location in the source code.
25pub type SyntaxNodePtr = rowan::ast::SyntaxNodePtr<OpenQASM3Language>;
26
27/// Like `SyntaxNodePtr`, but remembers the type of node.
28#[derive(Debug)]
29pub struct AstPtr<N: AstNode> {
30    raw: SyntaxNodePtr,
31    _ty: PhantomData<fn() -> N>,
32}
33
34impl<N: AstNode> Clone for AstPtr<N> {
35    #[rustversion::before(1.74)]
36    fn clone(&self) -> AstPtr<N> {
37        AstPtr {
38            raw: self.raw,
39            _ty: PhantomData,
40        }
41    }
42    #[rustversion::since(1.74)]
43    fn clone(&self) -> AstPtr<N> {
44        AstPtr {
45            raw: self.raw,
46            _ty: PhantomData,
47        }
48    }
49}
50
51impl<N: AstNode> Eq for AstPtr<N> {}
52
53impl<N: AstNode> PartialEq for AstPtr<N> {
54    fn eq(&self, other: &AstPtr<N>) -> bool {
55        self.raw == other.raw
56    }
57}
58
59impl<N: AstNode> Hash for AstPtr<N> {
60    fn hash<H: Hasher>(&self, state: &mut H) {
61        self.raw.hash(state);
62    }
63}
64
65impl<N: AstNode> AstPtr<N> {
66    pub fn new(node: &N) -> AstPtr<N> {
67        AstPtr {
68            raw: SyntaxNodePtr::new(node.syntax()),
69            _ty: PhantomData,
70        }
71    }
72
73    pub fn to_node(&self, root: &SyntaxNode) -> N {
74        let syntax_node = self.raw.to_node(root);
75        N::cast(syntax_node).unwrap()
76    }
77
78    #[rustversion::since(1.74)]
79    pub fn syntax_node_ptr(&self) -> SyntaxNodePtr {
80        self.raw
81    }
82
83    #[rustversion::before(1.74)]
84    pub fn syntax_node_ptr(&self) -> SyntaxNodePtr {
85        self.raw
86    }
87
88    pub fn text_range(&self) -> TextRange {
89        self.raw.text_range()
90    }
91
92    pub fn cast<U: AstNode>(self) -> Option<AstPtr<U>> {
93        if !U::can_cast(self.raw.kind()) {
94            return None;
95        }
96        Some(AstPtr {
97            raw: self.raw,
98            _ty: PhantomData,
99        })
100    }
101
102    pub fn upcast<M: AstNode>(self) -> AstPtr<M>
103    where
104        N: Into<M>,
105    {
106        AstPtr {
107            raw: self.raw,
108            _ty: PhantomData,
109        }
110    }
111
112    /// Like `SyntaxNodePtr::cast` but the trait bounds work out.
113    pub fn try_from_raw(raw: SyntaxNodePtr) -> Option<AstPtr<N>> {
114        N::can_cast(raw.kind()).then_some(AstPtr {
115            raw,
116            _ty: PhantomData,
117        })
118    }
119}
120
121impl<N: AstNode> From<AstPtr<N>> for SyntaxNodePtr {
122    fn from(ptr: AstPtr<N>) -> SyntaxNodePtr {
123        ptr.raw
124    }
125}
126
127// #[test]
128// fn test_local_syntax_ptr() {
129//     use crate::{ast, AstNode, SourceFile};
130
131//     let file = SourceFile::parse("struct Foo { f: u32, }").ok().unwrap();
132//     let field = file.syntax().descendants().find_map(ast::RecordField::cast).unwrap();
133//     let ptr = SyntaxNodePtr::new(field.syntax());
134//     let field_syntax = ptr.to_node(file.syntax());
135//     assert_eq!(field.syntax(), &field_syntax);
136// }