Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
b6db1f6
kotlin: Add loc metrics
Luni-4 Mar 27, 2023
28b6ec3
kotlin: add npa metric
Luni-4 Mar 27, 2023
17cc0eb
kotlin: Add cyclomatic metric
Luni-4 Mar 27, 2023
6c13b6c
kotlin: Add exit metric
Luni-4 Mar 27, 2023
d28585e
kotlin: add wmc metric
Luni-4 Mar 27, 2023
bd8605e
kotlin: implement getter
Luni-4 Mar 27, 2023
73e5b49
kotlin: implement checker
Luni-4 Mar 27, 2023
5735eb4
kotlin: add npm metric
Luni-4 Mar 28, 2023
0c6ede2
kotlin: fix npm
Luni-4 Mar 28, 2023
0093646
kotlin: simplify npa
Luni-4 Mar 28, 2023
196053f
kotlin: implement halstead
Luni-4 Mar 28, 2023
0f64bec
kotlin: implement abc
Luni-4 Mar 28, 2023
c6f497c
kotlin: Do not add a new variable to stats for npm
Luni-4 May 5, 2023
774f3d2
kotlin: Do not add a new variable to stats for npa
Luni-4 May 5, 2023
29b419a
kotlin: Fix clippy warnings
Luni-4 May 5, 2023
905d3c0
Implement traverse children
Luni-4 May 26, 2023
7c637c1
Fix checker and getter
Luni-4 May 26, 2023
803532d
Fix wmc
Luni-4 May 26, 2023
eb4afcd
Fix nargs
Luni-4 May 26, 2023
51465b6
Fix halstead
Luni-4 May 26, 2023
19194d4
kotlin: add cognitive metric
Luni-4 Jun 15, 2023
48349c1
build(deps): update tree-sitter-javascript requirement
dependabot[bot] Sep 2, 2025
b8ce5dd
build(deps): update tree-sitter-javascript requirement in /enums
dependabot[bot] Sep 2, 2025
8e98c27
build(deps): update tree-sitter-javascript requirement
dependabot[bot] Sep 2, 2025
e4f456a
build(deps): update tree-sitter-python requirement in /enums
dependabot[bot] Sep 15, 2025
6f5ea23
build(deps): update tree-sitter-python requirement
dependabot[bot] Sep 15, 2025
395277e
build(deps): update tree-sitter requirement in /tree-sitter-mozjs
dependabot[bot] Dec 15, 2025
189452b
build(deps): update tree-sitter requirement in /tree-sitter-mozcpp
dependabot[bot] Dec 15, 2025
3d41672
build(deps): update tree-sitter requirement in /tree-sitter-preproc
dependabot[bot] Dec 15, 2025
a4ee887
build(deps): update tree-sitter requirement in /enums
dependabot[bot] Dec 15, 2025
fc5e910
build(deps): update tree-sitter requirement in /tree-sitter-ccomment
dependabot[bot] Dec 15, 2025
1c3cb86
build(deps): update askama requirement from ^0.14 to ^0.15 in /enums
dependabot[bot] Dec 22, 2025
909b6d5
Merge remote-tracking branch 'origin/dependabot/cargo/tree-sitter-cco…
Jan 19, 2026
d8ac144
Merge remote-tracking branch 'origin/dependabot/cargo/enums/tree-sitt…
Jan 19, 2026
9800d83
Merge remote-tracking branch 'origin/dependabot/cargo/tree-sitter-pre…
Jan 19, 2026
3d63bbc
Merge remote-tracking branch 'origin/dependabot/cargo/tree-sitter-moz…
Jan 19, 2026
85ffea7
Merge remote-tracking branch 'origin/dependabot/cargo/tree-sitter-moz…
Jan 19, 2026
d683273
Switch to tree-sitter-kotlin-codanna for tree-sitter 0.26.3 compatibi…
Jan 19, 2026
6449cf9
Merge askama 0.15 update
Jan 19, 2026
3bd09d5
Merge tree-sitter-javascript 0.25.0 update
Jan 19, 2026
4fbaca8
Merge tree-sitter-javascript 0.25.0 update (enums)
Jan 19, 2026
f43fe88
Merge tree-sitter-javascript 0.25.0 update (mozjs)
Jan 19, 2026
d2872f4
Merge tree-sitter-python 0.25.0 update (resolved conflict)
Jan 19, 2026
b66657a
Merge tree-sitter-python 0.25.0 update (enums, resolved conflict)
Jan 19, 2026
1cb08be
Update dependencies to latest versions
Jan 19, 2026
9f4dae3
ci: Add GitHub Actions workflows for CI and Release
Jan 19, 2026
43fbd6e
feat: Expose inner tree-sitter::Node for advanced use cases
Jan 19, 2026
1a36820
feat: Add hazard analyzer API improvements - all_occurrences, has_anc…
Jan 19, 2026
4f9c276
fix: Make asttools module functional with correct Node API
Jan 19, 2026
a44288d
Fix PR review comments from Copilot
Jan 19, 2026
30d2228
Fix clippy collapsible_if warnings
Jan 19, 2026
56774b4
style: Fix rustfmt formatting in checker.rs
Jan 19, 2026
ecafc19
Regenerate language enums for tree-sitter 0.26.3
Jan 19, 2026
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
63 changes: 63 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: CI

on:
push:
branches: [master, main]
pull_request:
branches: [master, main]

env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1

jobs:
check:
name: Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo check --all-features

clippy:
name: Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- uses: Swatinem/rust-cache@v2
- run: cargo clippy --all-targets --all-features -- -D warnings

fmt:
name: Format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- run: cargo fmt --all -- --check

test:
name: Test
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo test --all-features

build:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo build --release
57 changes: 57 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Release

on:
push:
tags:
- 'v*'
workflow_dispatch:

jobs:
create-release:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: taiki-e/create-gh-release-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}

upload-assets:
needs: create-release
strategy:
matrix:
include:
- target: aarch64-unknown-linux-gnu
os: ubuntu-latest
- target: aarch64-apple-darwin
os: macos-latest
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
- target: x86_64-apple-darwin
os: macos-latest
- target: armv7-unknown-linux-gnueabihf
os: ubuntu-latest
- target: i686-unknown-linux-gnu
os: ubuntu-latest
- target: x86_64-pc-windows-gnu
os: windows-latest
- target: x86_64-pc-windows-msvc
os: windows-latest
- target: aarch64-pc-windows-msvc
os: windows-latest
runs-on: ${{ matrix.os }}
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Install cross-compilation tools
uses: taiki-e/setup-cross-toolchain-action@v1
with:
target: ${{ matrix.target }}
if: startsWith(matrix.os, 'ubuntu')
- uses: taiki-e/upload-rust-binary-action@v1
with:
bin: rust-code-analysis-cli
target: ${{ matrix.target }}
token: ${{ secrets.GITHUB_TOKEN }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ target
enums/target
*~
Cargo.lock
.claude/
12 changes: 6 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,20 @@ serde = { version = "^1.0", features = ["derive"] }
termcolor = "^1.2"
walkdir = "^2.3"

tree-sitter = "=0.25.3"
tree-sitter = "=0.26.3"
tree-sitter-java = "=0.23.5"
tree-sitter-kotlin-ng = "1.1.0"
tree-sitter-kotlin-codanna = "0.3.9"
tree-sitter-typescript = "=0.23.2"
tree-sitter-javascript = "=0.23.1"
tree-sitter-python = "=0.23.6"
tree-sitter-rust = "=0.23.2"
tree-sitter-javascript = "=0.25.0"
tree-sitter-python = "=0.25.0"
tree-sitter-rust = "=0.24.0"
tree-sitter-preproc = { path = "./tree-sitter-preproc", version = "=0.20.3" }
tree-sitter-ccomment = { path = "./tree-sitter-ccomment", version = "=0.20.3" }
tree-sitter-mozcpp = { path = "./tree-sitter-mozcpp", version = "=0.20.4" }
tree-sitter-mozjs = { path = "./tree-sitter-mozjs", version = "=0.20.3" }

[dev-dependencies]
insta = { version = "1.29.0", features = ["yaml", "json", "redactions"] }
insta = { version = "1.46.1", features = ["yaml", "json", "redactions"] }
pretty_assertions = "^1.3"

[profile.dev.package.insta]
Expand Down
19 changes: 19 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.PHONY: build test check clean fmt

build:
cargo build

build-release:
cargo build --release

test:
cargo test

check:
cargo check

clean:
cargo clean

fmt:
cargo fmt
12 changes: 6 additions & 6 deletions enums/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ edition = "2024"

[dependencies]
clap = { version = "^4.0", features = ["derive"] }
askama = "^0.14"
askama = "^0.15"

tree-sitter = "=0.25.3"
tree-sitter = "=0.26.3"
tree-sitter-java = "=0.23.5"
tree-sitter-kotlin-ng = "1.1.0"
tree-sitter-kotlin-codanna = "0.3.9"
tree-sitter-typescript = "=0.23.2"
tree-sitter-javascript = "=0.23.1"
tree-sitter-python = "=0.23.6"
tree-sitter-rust = "=0.23.2"
tree-sitter-javascript = "=0.25.0"
tree-sitter-python = "=0.25.0"
tree-sitter-rust = "=0.24.0"
tree-sitter-preproc = { path = "../tree-sitter-preproc", version = "=0.20.3" }
tree-sitter-ccomment = { path = "../tree-sitter-ccomment", version = "=0.20.3" }
tree-sitter-mozcpp = { path = "../tree-sitter-mozcpp", version = "=0.20.4" }
Expand Down
2 changes: 1 addition & 1 deletion enums/src/languages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use tree_sitter::Language;
mk_langs!(
// 1) Name for enum
// 2) tree-sitter function to call to get a Language
(Kotlin, tree_sitter_kotlin_ng),
(Kotlin, tree_sitter_kotlin_codanna),
(Java, tree_sitter_java),
(Rust, tree_sitter_rust),
(Cpp, tree_sitter_cpp),
Expand Down
2 changes: 1 addition & 1 deletion enums/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ macro_rules! mk_get_language {
( $( ($camel:ident, $name:ident) ),* ) => {
pub fn get_language(lang: &Lang) -> Language {
match lang {
Lang::Kotlin => tree_sitter_kotlin_ng::LANGUAGE.into(),
Lang::Kotlin => tree_sitter_kotlin_codanna::language().into(),
Lang::Java => tree_sitter_java::LANGUAGE.into(),
Lang::Typescript => tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into(),
Lang::Tsx => tree_sitter_typescript::LANGUAGE_TSX.into(),
Expand Down
8 changes: 4 additions & 4 deletions src/alterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ impl Alterator for CppCode {
AstNode::new(node.kind(), text, span, Vec::new())
}
Cpp::PreprocDef | Cpp::PreprocFunctionDef | Cpp::PreprocCall => {
if let Some(last) = children.last() {
if last.r#type == "\n" {
children.pop();
}
if let Some(last) = children.last()
&& last.r#type == "\n"
{
children.pop();
}
Self::get_default(node, code, span, children)
}
Expand Down
153 changes: 153 additions & 0 deletions src/asttools.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
//! AST traversal and analysis utilities.
//!
//! This module provides helper functions and macros for traversing
//! and analyzing the AST tree structure.

use crate::node::Node;

/// Gets an ancestor at a specific level above the current node.
///
/// # Arguments
/// * `node` - The starting node
/// * `level` - How many levels up to traverse (0 returns the node itself)
///
/// # Returns
/// The ancestor node at the specified level, or None if the tree isn't deep enough.
///
/// # Example
/// ```ignore
/// // Get the grandparent (2 levels up)
/// if let Some(grandparent) = get_parent(&node, 2) {
/// println!("Grandparent kind: {}", grandparent.kind());
/// }
/// ```
pub fn get_parent<'a>(node: &Node<'a>, level: usize) -> Option<Node<'a>> {
let mut level = level;
let mut current = *node;
while level != 0 {
current = current.parent()?;
level -= 1;
}
Some(current)
}

/// Traverses a tree passing from children to children in search of a specific
/// token or series of tokens.
///
/// # Arguments
/// * `node` - The starting node
/// * `token_list` - A slice of predicates, each matching a level of descent
///
/// # Returns
/// The final node after following the token path, or None if any token wasn't found.
///
/// # Example
/// ```ignore
/// // Find: node -> child matching pred1 -> grandchild matching pred2
/// let result = traverse_children(&node, &[
/// |id| id == SomeToken::Foo as u16,
/// |id| id == SomeToken::Bar as u16,
/// ]);
/// ```
pub fn traverse_children<'a, F>(node: &Node<'a>, token_list: &[F]) -> Option<Node<'a>>
where
F: Fn(u16) -> bool,
{
let mut current = *node;
'outer: for token in token_list {
for child in current.children() {
if token(child.kind_id()) {
current = child;
continue 'outer;
}
}
// Token not found at this level
return None;
}
Some(current)
}

/// Checks if a node has specific ancestors in sequence.
///
/// This macro checks if the node's ancestors match a specific pattern,
/// where the first pattern(s) are immediate ancestors and the last pattern
/// is the final ancestor to match.
///
/// # Example
/// ```ignore
/// // Check if node is inside a function inside a class
/// let is_method = has_ancestors!(node, Class | Struct, Function);
/// ```
#[macro_export]
macro_rules! has_ancestors {
($node:expr, $( $typs:pat_param )|*, $( $typ:pat_param ),+) => {{
let mut res = false;
loop {
let mut node = *$node;
$(
if let Some(parent) = node.parent() {
match parent.kind_id().into() {
$typ => {
node = parent;
},
_ => {
break;
}
}
} else {
break;
}
)*
if let Some(parent) = node.parent() {
match parent.kind_id().into() {
$( $typs )|+ => {
res = true;
},
_ => {}
}
}
break;
}
res
}};
}

/// Counts specific ancestors matching a pattern until a stop condition.
///
/// This macro traverses up the tree counting ancestors that match the given
/// patterns, stopping when it encounters an ancestor matching the stop pattern.
///
/// # Example
/// ```ignore
/// // Count nested if statements until we hit a function boundary
/// let nesting = count_specific_ancestors!(node, If | ElseIf, Function | Method);
/// ```
#[macro_export]
macro_rules! count_specific_ancestors {
($node:expr, $checker:ty, $( $typs:pat_param )|*, $( $stops:pat_param )|*) => {{
let mut count = 0;
let mut node = *$node;
while let Some(parent) = node.parent() {
match parent.kind_id().into() {
$( $typs )|* => {
if !<$checker>::is_else_if(&parent) {
count += 1;
}
},
$( $stops )|* => break,
_ => {}
}
node = parent;
}
count
}};
}

#[cfg(test)]
mod tests {
#[test]
fn test_get_parent_level_zero() {
// Level 0 should return the same node
// (actual test would need a real node)
}
}
Loading