|
145 | 145 | //! (the entirety of the function definition) and for the _name_ of the definition (the content of |
146 | 146 | //! the function's `name`). |
147 | 147 | //! |
| 148 | +//! Adding the `empty_source_span` attribute will use an empty source span located at the start of the |
| 149 | +//! span of the `source_node`. This can be useful when a proper reference or definition is desired, |
| 150 | +//! and thus `source_node` is required, but the span of the available source node is too large. For |
| 151 | +//! example, a module definition which is located at the start of the program, but does span the |
| 152 | +//! whole program: |
| 153 | +//! |
| 154 | +//! ``` skip |
| 155 | +//! (program)@prog { |
| 156 | +//! ; ... |
| 157 | +//! node mod_def |
| 158 | +//! attr mod_def type = "pop_symbol", symbol = mod_name, is_definition, source_node = @prog, empty_source_span |
| 159 | +//! ; ... |
| 160 | +//! } |
| 161 | +//! ``` |
| 162 | +//! |
148 | 163 | //! ### Connecting stack graph nodes with edges |
149 | 164 | //! |
150 | 165 | //! To connect two stack graph nodes, use the `edge` statement to add an edge between them: |
@@ -334,9 +349,10 @@ static SCOPE_TYPE: &'static str = "scope"; |
334 | 349 |
|
335 | 350 | // Node attribute names |
336 | 351 | static DEBUG_ATTR_PREFIX: &'static str = "debug_"; |
| 352 | +static EMPTY_SOURCE_SPAN_ATTR: &'static str = "empty_source_span"; |
337 | 353 | static IS_DEFINITION_ATTR: &'static str = "is_definition"; |
338 | | -static IS_EXPORTED_ATTR: &'static str = "is_exported"; |
339 | 354 | static IS_ENDPOINT_ATTR: &'static str = "is_endpoint"; |
| 355 | +static IS_EXPORTED_ATTR: &'static str = "is_exported"; |
340 | 356 | static IS_REFERENCE_ATTR: &'static str = "is_reference"; |
341 | 357 | static SCOPE_ATTR: &'static str = "scope"; |
342 | 358 | static SOURCE_NODE_ATTR: &'static str = "source_node"; |
@@ -870,7 +886,13 @@ impl<'a> Builder<'a> { |
870 | 886 | Some(source_node) => &self.graph[source_node.as_syntax_node_ref()?], |
871 | 887 | None => return Ok(()), |
872 | 888 | }; |
873 | | - let span = self.span_calculator.for_node(source_node); |
| 889 | + let mut span = self.span_calculator.for_node(source_node); |
| 890 | + if match node.attributes.get(EMPTY_SOURCE_SPAN_ATTR) { |
| 891 | + Some(empty_source_span) => empty_source_span.as_boolean()?, |
| 892 | + None => false, |
| 893 | + } { |
| 894 | + span.end = span.start.clone(); |
| 895 | + } |
874 | 896 | let containing_line = &self.source[span.start.containing_line.clone()]; |
875 | 897 | let containing_line = self.stack_graph.add_string(containing_line); |
876 | 898 | let source_info = self.stack_graph.source_info_mut(node_handle); |
@@ -912,6 +934,7 @@ impl<'a> Builder<'a> { |
912 | 934 | let id = id.as_str(); |
913 | 935 | if !allowed_attributes.contains(id) |
914 | 936 | && id != SOURCE_NODE_ATTR |
| 937 | + && id != EMPTY_SOURCE_SPAN_ATTR |
915 | 938 | && !id.starts_with(DEBUG_ATTR_PREFIX) |
916 | 939 | { |
917 | 940 | eprintln!("Unexpected attribute {} on node of type {}", id, node_type); |
|
0 commit comments