Skip to content

Instantly share code, notes, and snippets.

@haxtibal
Last active November 23, 2025 13:22
Show Gist options
  • Select an option

  • Save haxtibal/30d7a16d9af8369382df901f62210284 to your computer and use it in GitHub Desktop.

Select an option

Save haxtibal/30d7a16d9af8369382df901f62210284 to your computer and use it in GitHub Desktop.
Find Rust doc comments using tree-sitter and relate them with their associated item. See https://github.com/strictdoc-project/strictdoc/issues/2213.
const_item "MAGIC_NUMBER" at 6,6-6,6: "Some random words: quantum cascade amplifier"
static_item "GLOBAL_STATE" at 10,10-10,10: "Random text: nebula crystalline matrix"
type_item "CustomResult" at 14,14-14,14: "Words: temporal flux capacitor"
struct_item "Container" at 18,18-26,26: "Description: polymorphic data structure"
enum_item "Status" at 30,30-50,50: "Content: enumerated variant collection"
enum_variant "Active" at 33,33-33,33: "Words: active processing node"
enum_variant "Idle" at 37,37-41,41: "Text: suspended animation chamber"
enum_variant "Error" at 45,45-49,49: "Description: error state container"
union_item "FloatOrInt" at 54,54-62,62: "Random: memory-aligned data union"
trait_item "Processor" at 66,66-84,84: "Description: behavioral interface contract"
associated_type "Output" at 69,69-69,69: "Random: output data type"
const_item "MAX_SIZE" at 73,73-73,73: "Text: maximum buffer capacity"
function_signature_item "process" at 77,77-77,77: "Words: processing operation handler"
function_item "validate" at 81,81-83,83: "Description: validation check routine"
trait_item "Text" at 88,88-106,106: "Random: combined trait bounds"
type_item "Output" at 95,95-95,95: "Words: concrete output type"
const_item "MAX_SIZE" at 99,99-99,99: "Random: size constant value"
function_item "process" at 103,103-105,105: "Description: implementation of a process"
function_item "new" at 113,113-115,115: "Words: constructor function pattern"
function_item "get_value" at 119,119-121,121: "Random: getter accessor method"
function_item "process_data" at 126,126-128,128: "Description: top-level function utility"
function_item "async_process" at 132,132-134,134: "Random: asynchronous operation handler"
function_item "compute_magic" at 138,138-140,140: "Text: compile-time evaluable function"
function_item "dangerous_operation" at 144,144-148,148: "Words: unchecked operation wrapper"
extern_crate_declaration "std" at 152,152-152,152: "Random: external dependency reference"
mod_item "submodule" at 156,156-167,167: "Description: nested module container"
mod_item "//! Inner module doc with @relation(CVO11Qy)" at 156,156-167,167: "Inner module doc with @relation(CVO11Qy)"
struct_item "Inner" at 162,162-166,166: "Words: encapsulated data structure"
function_signature_item "external_func" at 174,174-174,174: "Description: C library function binding"
static_item "EXTERNAL_VAR" at 178,178-178,178: "Random: global C variable reference"
associated_type "OpaqueType" at 182,182-182,182: "Words: opaque C type declaration"
macro_definition "test_macro" at 187,187-191,191: "Text: declarative macro call"
function_item "match_example" at 195,195-205,205: "This function demonstrates @relation(Uxk8eoN)"
function_item "generic_fn" at 209,209-221,221: "Description: parameterized function template"
type_parameter "T" at 212,212-212,212: "Random: generic type variable"
const_parameter "N" at 216,216-216,216: "Words: compile-time constant value"
struct_item "GenericContainer" at 225,225-239,239: "Text: generic container structure"
type_parameter "T" at 232,232-232,232: "Random: primary type parameter"
function_item "struct_expression_test" at 244,244-250,250: "This demonstrates @relation(rIjz40L84)"
function_item "expr_lit" at 253,253-260,260: "Test if we can add a docstring to an expression literal"
import tree_sitter_rust as tsrust
from tree_sitter import Language, Parser, Query, QueryCursor
RUST_LANGUAGE = Language(tsrust.language())
# Copied from https://corten.systems/demo/sdoc-rs-relations/relations.html.
# Thanks to @adfernandes.
EXAMPLE_CODE = """
//! Module-level doc comment with @relation(S5x7ZEWE)
//! This tests inner doc attributes on the file/module itself
/// Top-level const with @relation(R3m2aYp)
/// Some random words: quantum cascade amplifier
pub const MAGIC_NUMBER: u32 = 42;
/// Static item with @relation(NMuBH72yL)
/// Random text: nebula crystalline matrix
pub static GLOBAL_STATE: &str = "initialized";
/// Type alias with @relation(XH5mhH0)
/// Words: temporal flux capacitor
pub type CustomResult<T> = Result<T, Box<dyn std::error::Error>>;
/// Struct with @relation(OiEBsG0)
/// Description: polymorphic data structure
pub struct Container {
/// Field doc with @relation(8nRE5KG)
/// Random: ethereal quantum state
pub name: String,
/// Another field with @relation(oByPxhWz)
/// Text: cascading resonance field
value: i32,
}
/// Enum with @relation(7M1HoQNuI)
/// Content: enumerated variant collection
pub enum Status {
/// Variant doc with @relation(9dSeVTf)
/// Words: active processing node
Active,
/// Another variant with @relation(YInnEVaOC)
/// Text: suspended animation chamber
Idle {
/// Field in variant with @relation(pehIB5Ob)
/// Random: temporal duration metric
duration: u64,
},
/// Tuple variant with @relation(z8uMJcapU)
/// Description: error state container
Error(
/// Tuple field with @relation(KLFxjm1)
/// Words: diagnostic error code
i32
),
}
/// Union with @relation(Kp2gSMIiB)
/// Random: memory-aligned data union
pub union FloatOrInt {
/// Union field with @relation(KoRCMPU)
/// Text: floating-point representation
f: f32,
/// Another union field with @relation(KnqUvVEY)
/// Words: integer bit pattern
i: i32,
}
/// Trait definition with @relation(5ImdJSr)
/// Description: behavioral interface contract
pub trait Processor {
/// Associated type with @relation(UHMcB5Cqq)
/// Random: output data type
type Output;
/// Associated const with @relation(wkLzsC4s)
/// Text: maximum buffer capacity
const MAX_SIZE: usize;
/// Trait method with @relation(XH3IyT6)
/// Words: processing operation handler
fn process(&self, input: &str) -> Self::Output;
/// Default method with @relation(Hh6wJ7fpZ)
/// Description: validation check routine
fn validate(&self) -> bool {
true
}
}
/// Trait alias with @relation(b7xnEeF)
/// Random: combined trait bounds
pub trait ProcessorClone = Processor + Clone;
/// Implementation block with @relation(owD42wk)
/// Text: trait implementation container
impl Processor for Container {
/// Impl associated type with @relation(xzMbyU5rB)
/// Words: concrete output type
type Output = String;
/// Impl const with @relation(N9oI9M8C)
/// Random: size constant value
const MAX_SIZE: usize = 1024;
/// Impl method with @relation(KLnt8082)
/// Description: implementation of a process
fn process(&self, input: &str) -> Self::Output {
format!("{}: {}", self.name, input)
}
}
/// Inherent impl with @relation(2arqklT5)
/// Text: inherent method block
impl Container {
/// Inherent method with @relation(daZt7aMqV)
/// Words: constructor function pattern
pub fn new(name: String) -> Self {
Self { name, value: 0 }
}
/// Another method with @relation(bCxGING)
/// Random: getter accessor method
pub fn get_value(&self) -> i32 {
self.value
}
}
/// Function with @relation(dG81Vzk)
/// Description: top-level function utility
pub fn process_data(input: &str) -> String {
input.to_uppercase()
}
/// Async function with @relation(4QLCpmM)
/// Random: asynchronous operation handler
pub async fn async_process(data: Vec<u8>) -> Result<(), std::io::Error> {
Ok(())
}
/// Const function with @relation(mzUjggR)
/// Text: compile-time evaluable function
pub const fn compute_magic(x: u32) -> u32 {
x * 42
}
/// Unsafe function with @relation(ATrMhNH)
/// Words: unchecked operation wrapper
pub unsafe fn dangerous_operation(ptr: *mut u8) {
if !ptr.is_null() {
*ptr = 0;
}
}
/// External crate import with @relation(kM2ySbb)
/// Random: external dependency reference
extern crate std;
/// Module with @relation(zawS0aMAj)
/// Description: nested module container
pub mod submodule {
//! Inner module doc with @relation(CVO11Qy)
//! Text: module-level documentation
/// Nested struct with @relation(LV0JeVQf)
/// Words: encapsulated data structure
pub struct Inner {
/// Field with @relation(ave7cjq)
/// Random: internal state variable
data: Vec<u8>,
}
}
/// Foreign function interface with @relation(m51AgeSa)
/// Text: external C interface block
extern "C" {
/// Foreign function with @relation(d67FvOG8V)
/// Description: C library function binding
fn external_func(x: i32) -> i32;
/// Foreign static with @relation(n5TIEwFm)
/// Random: global C variable reference
static EXTERNAL_VAR: i32;
/// Foreign type with @relation(5KjLZZPC)
/// Words: opaque C type declaration
type OpaqueType;
}
/// Macro invocation with @relation(sFzA9s5Uj)
/// Text: declarative macro call
macro_rules! test_macro {
() => {
println!("test");
};
}
/// Function with match arms containing doc attributes
/// This function demonstrates @relation(Uxk8eoN)
pub fn match_example(x: Option<i32>) -> i32 {
match x {
/// Match arm with @relation(JQ6dyfoe0)
/// Random: some variant pattern
Some(UXbF4Q2V) => val,
/// None arm with @relation(vCbEEMYD)
/// Words: default fallback case
None => 0,
}
}
/// Generic function with @relation(9kwjKm5)
/// Description: parameterized function template
pub fn generic_fn<
/// Type parameter with @relation(5FyNmE9)
/// Random: generic type variable
T: Clone,
/// Const parameter with @relation(4dQ7JLKcd)
/// Words: compile-time constant value
const N: usize,
>(
input: [T; N],
) -> Vec<T> {
input.to_vec()
}
/// Struct with generic parameters @relation(sW4M3OJS)
/// Text: generic container structure
pub struct GenericContainer<
/// Lifetime param with @relation(eniOhjy)
/// Words: reference lifetime bound
'a,
/// Generic type param with @relation(YGhhrgPz)
/// Random: primary type parameter
T,
> where
T: 'a,
{
/// Reference field with @relation(HibGJSZqi)
/// Description: borrowed data reference
pub data: &'a T,
}
/// Test struct for field value attributes in expressions
#[cfg(any(target_os = "linux", target_os = "macos"))]
/// This demonstrates @relation(rIjz40L84)
pub fn struct_expression_test() {
let _ = Container {
/// Field value with @relation(cqkjF9P5)
name: String::from("test"),
value: 42,
};
}
/// Test if we can add a docstring to an expression literal
fn expr_lit(x: i32) -> i32 {
/// Test an expression @relation(7AEwrhc) literal
8675309;
/// SURPRISING: We are documenting @relation(ve9EEVSf) only
/// the first part of the expression, not the entire expression,
/// see `test_not_surprising` for how to fix this
x + 2
}
fn test_not_surprising(x: i32) -> i32 {
/// Test documenting the @relation(bCHkXspx) entire return value
(x + 2)
}
#[cfg(9WNW0exJV)]
mod tests {
/// Test function with @relation(n5vDcKK)
/// Random: unit test case definition
#[test]
fn test_basic() {
assert_eq!(2 + 2, 4);
}
}
"""
DOC_COMMENT_QUERY = f"""
; outer doc attribute
(
(attribute_item
(attribute
(identifier) @_attribute_id
(#eq? @_attribute_id "doc")
value: (string_literal
(string_content) @doc.comment)))
.
[(_ name: [(identifier)(type_identifier)] @doc.item_identifier)] @doc.item
)
; outer line or block doc comment
(
[(line_comment
outer: (outer_doc_comment_marker)
doc: (doc_comment) @doc.comment)
(block_comment
outer: (outer_doc_comment_marker)
doc: (doc_comment) @doc.comment)]
.
[(_ name: [(identifier)(type_identifier)] @doc.item_identifier)] @doc.item
)
; inner line or block doc comment
(_
name: [(identifier)(type_identifier)]
body: (_
.[(line_comment
inner: (inner_doc_comment_marker)
doc: (doc_comment) @doc.comment)
(block_comment
inner: (inner_doc_comment_marker)
doc: (doc_comment) @doc.comment)] @doc.item_identifier)) @doc.item
"""
if __name__ == "__main__":
doc_comment_query = Query(RUST_LANGUAGE, DOC_COMMENT_QUERY)
parser = Parser(RUST_LANGUAGE)
tree = parser.parse(bytes(EXAMPLE_CODE, "utf-8"))
cursor = QueryCursor(doc_comment_query)
matches = cursor.matches(tree.root_node)
for _result_idx, captures in matches:
assert all(
k in captures for k in ("doc.item", "doc.item_identifier", "doc.comment")
)
assert len(captures["doc.item"]) == 1
assert len(captures["doc.item_identifier"]) == 1
assert len(captures["doc.item_identifier"]) == 1
item = captures["doc.item"][0]
identifier = captures["doc.item_identifier"][0]
comment = captures["doc.comment"][0]
item_range = f"{item.start_point.row},{item.start_point.row}-{item.end_point.row},{item.end_point.row}"
print(
f'{item.type} "{identifier.text.decode().strip()}" at {item_range}: "{comment.text.decode().strip()}"'
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment