Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/source/components/analyse.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Limitations

**Current Limitations:**

- **Language Support**: C/C++ (``//``, ``/* */``), C# (``//``, ``/* */``, ``///``), Python (``#``) and YAML (``#``) comment styles are supported
- **Language Support**: C/C++ (``//``, ``/* */``), C# (``//``, ``/* */``, ``///``), Python (``#``), YAML (``#``) and Rust (``//``, ``/* */``, ``///``) comment styles are supported
- **Single Comment Style**: Each analysis run processes only one comment style at a time

Extraction Examples
Expand Down
11 changes: 9 additions & 2 deletions docs/source/components/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ Specifies the comment syntax style used in the source code files. This determine

**Type:** ``str``
**Default:** ``"cpp"``
**Supported values:** ``"cpp"``, ``"python"``, ``"cs"``, ``"yaml"``
**Supported values:** ``"cpp"``, ``"python"``, ``"cs"``, ``"yaml"``, ``"rust"``

.. code-block:: toml

Expand Down Expand Up @@ -304,8 +304,15 @@ Specifies the comment syntax style used in the source code files. This determine
- ``"yaml"``
- ``#`` (single-line)
- ``.yaml``, ``.yml``
* - Rust
- ``"rust"``
- ``//`` (single-line),
``/* */`` (multi-line),
``///`` (doc comments),
``//!`` (inner doc comments)
- ``.rs``

.. note:: Future versions may support additional programming languages. Currently, only C/C++ and Python comment styles are supported.
.. note:: Future versions may support additional programming languages.

gitignore
^^^^^^^^^
Expand Down
24 changes: 24 additions & 0 deletions docs/source/components/features.rst
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,30 @@ Features
.. fault:: Sphinx-codelinks halucinates traceability objects in YAML
:id: FAULT_YAML_2

.. feature:: Rust Language Support
:id: FE_RUST

Support for defining traceability objects in Rust source code.

The Rust language parser leverages tree-sitter to accurately identify and extract
comments from Rust source files, including single-line (``//``, ``///``) and multi-line
(``/* */``) comment styles. This ensures that traceability markers are correctly
associated with the appropriate code structures such as functions, structs, enums,
and traits.

Key capabilities:

* Detection of inline and block comments
* Association of comments with function and method definitions
* Support for standard Rust comment conventions
* Accurate scope detection for nested structures

.. fault:: Traceability objects are not detected in Rust language
:id: FAULT_RUST_1

.. fault:: Sphinx-codelinks halucinates traceability objects in Rust
:id: FAULT_RUST_2

.. feature:: Customized comment styles
:id: FE_CMT

Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ dependencies = [
"tree-sitter~=0.25.1",
"tree-sitter-c-sharp>=0.23.1",
"tree-sitter-yaml>=0.7.1",
"tree-sitter-rust>=0.23.0",
]

[build-system]
Expand Down
20 changes: 19 additions & 1 deletion src/sphinx_codelinks/analyse/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@
CommentType.cpp: {"function_definition", "class_definition"},
CommentType.cs: {"method_declaration", "class_declaration", "property_declaration"},
CommentType.yaml: {"block_mapping_pair", "block_sequence_item", "document"},
# @Rust Scope Node Types, IMPL_RUST_2, impl, [FE_RUST];
CommentType.rust: {
"function_item",
"struct_item",
"enum_item",
"impl_item",
"trait_item",
"mod_item",
},
}

# initialize logger
Expand Down Expand Up @@ -47,6 +56,10 @@
CPP_QUERY = """(comment) @comment"""
C_SHARP_QUERY = """(comment) @comment"""
YAML_QUERY = """(comment) @comment"""
RUST_QUERY = """
(line_comment) @comment
(block_comment) @comment
"""


def is_text_file(filepath: Path, sample_size: int = 2048) -> bool:
Expand All @@ -64,7 +77,7 @@ def is_text_file(filepath: Path, sample_size: int = 2048) -> bool:
return False


# @Tree-sitter parser initialization for multiple languages, IMPL_LANG_1, impl, [FE_C_SUPPORT, FE_CPP, FE_PY, FE_YAML]
# @Tree-sitter parser initialization for multiple languages, IMPL_LANG_1, impl, [FE_C_SUPPORT, FE_CPP, FE_PY, FE_YAML, FE_RUST]
def init_tree_sitter(comment_type: CommentType) -> tuple[Parser, Query]:
if comment_type == CommentType.cpp:
import tree_sitter_cpp # noqa: PLC0415
Expand All @@ -86,6 +99,11 @@ def init_tree_sitter(comment_type: CommentType) -> tuple[Parser, Query]:

parsed_language = Language(tree_sitter_yaml.language())
query = Query(parsed_language, YAML_QUERY)
elif comment_type == CommentType.rust:
import tree_sitter_rust # noqa: PLC0415

parsed_language = Language(tree_sitter_rust.language())
query = Query(parsed_language, RUST_QUERY)
else:
raise ValueError(f"Unsupported comment style: {comment_type}")
parser = Parser(parsed_language)
Expand Down
3 changes: 3 additions & 0 deletions src/sphinx_codelinks/source_discover/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"python": ["py"],
"cs": ["cs"],
"yaml": ["yml", "yaml"],
"rust": ["rs"],
}


Expand All @@ -18,6 +19,8 @@ class CommentType(str, Enum):
cpp = "cpp"
cs = "cs"
yaml = "yaml"
# @Support Rust style comments, IMPL_RUST_1, impl, [FE_RUST];
rust = "rust"


class SourceDiscoverSectionConfigType(TypedDict, total=False):
Expand Down
31 changes: 31 additions & 0 deletions tests/data/rust/demo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// demo.rs

/// This is a doc comment for the main function
/// @Main function implementation, main_demo, impl, [REQ_001]
fn main() {
println!("Hello from Rust!");
process_data();
}

// @Data processing function, process_func, impl, [REQ_002]
fn process_data() {
let data = vec![1, 2, 3];
for item in data {
println!("Processing: {}", item);
}
}

/* Block comment with marker
@User data structure, struct_def, impl, [REQ_003]
*/
struct User {
name: String,
age: u32,
}

impl User {
// @User constructor method, new_user, impl, [REQ_004]
fn new(name: String, age: u32) -> Self {
User { name, age }
}
}
14 changes: 14 additions & 0 deletions tests/test_analyse.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,20 @@ def test_analyse(src_dir, src_paths, tmp_path, snapshot_marks):
"warnings_path_exists": True,
},
),
(
TEST_DIR / "data" / "rust",
[
TEST_DIR / "data" / "rust" / "demo.rs",
],
ONELINE_COMMENT_STYLE_DEFAULT,
{
"num_src_files": 1,
"num_uncached_files": 1,
"num_cached_files": 0,
"num_comments": 6,
"num_oneline_warnings": 0,
},
),
],
)
def test_analyse_oneline_needs(
Expand Down
81 changes: 81 additions & 0 deletions tests/test_analyse_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import tree_sitter_c_sharp
import tree_sitter_cpp
import tree_sitter_python
import tree_sitter_rust
import tree_sitter_yaml

from sphinx_codelinks.analyse import utils
Expand Down Expand Up @@ -48,6 +49,14 @@ def init_yaml_tree_sitter() -> tuple[Parser, Query]:
return parser, query


@pytest.fixture(scope="session")
def init_rust_tree_sitter() -> tuple[Parser, Query]:
parsed_language = Language(tree_sitter_rust.language())
query = Query(parsed_language, utils.RUST_QUERY)
parser = Parser(parsed_language)
return parser, query


@pytest.mark.parametrize(
("code", "result"),
[
Expand Down Expand Up @@ -284,6 +293,78 @@ def test_find_associated_scope_yaml(code, result, init_yaml_tree_sitter):
assert result in yaml_structure


@pytest.mark.parametrize(
("code", "result"),
[
(
b"""
// @req-id: need_001
fn dummy_func1() {
}
""",
"fn dummy_func1()",
),
(
b"""
fn dummy_func2() {
}
// @req-id: need_001
fn dummy_func1() {
}
""",
"fn dummy_func1()",
),
(
b"""
fn dummy_func1() {
let a = 1;
/* @req-id: need_001 */
}
""",
"fn dummy_func1()",
),
(
b"""
fn dummy_func1() {
// @req-id: need_001
let a = 1;
}
fn dummy_func2() {
}
""",
"fn dummy_func1()",
),
(
b"""
/// @req-id: need_001
fn dummy_func1() {
}
""",
"fn dummy_func1()",
),
(
b"""
struct MyStruct {
// @req-id: need_001
field: i32,
}
""",
"struct MyStruct",
),
],
)
def test_find_associated_scope_rust(code, result, init_rust_tree_sitter):
parser, query = init_rust_tree_sitter
comments = utils.extract_comments(code, parser, query)
node: TreeSitterNode | None = utils.find_associated_scope(
comments[0], CommentType.rust
)
assert node
assert node.text
rust_def = node.text.decode("utf-8")
assert result in rust_def


@pytest.mark.parametrize(
("code", "result"),
[
Expand Down
2 changes: 1 addition & 1 deletion tests/test_source_discover.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
"comment_type": "java",
},
[
"Schema validation error in field 'comment_type': 'java' is not one of ['cpp', 'cs', 'python', 'yaml']"
"Schema validation error in field 'comment_type': 'java' is not one of ['cpp', 'cs', 'python', 'rust', 'yaml']"
],
),
(
Expand Down
2 changes: 1 addition & 1 deletion tests/test_src_trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
[
"Project 'dcdc' has the following errors:",
"Schema validation error in field 'exclude': 123 is not of type 'string'",
"Schema validation error in field 'comment_type': 'java' is not one of ['cpp', 'cs', 'python', 'yaml']",
"Schema validation error in field 'comment_type': 'java' is not one of ['cpp', 'cs', 'python', 'rust', 'yaml']",
"Schema validation error in field 'gitignore': '_true' is not of type 'boolean'",
"Schema validation error in field 'include': 345 is not of type 'string'",
"Schema validation error in field 'src_dir': ['../dcdc'] is not of type 'string'",
Expand Down