Skip to content

feat(KindMatcher): Support super types #1875

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

mohebifar
Copy link
Member

@mohebifar mohebifar commented Mar 13, 2025

As mentioned in #1784, ast-grep currently accepts supertypes like "declaration" and "expression" as valid types but doesn't actually match anything.

Ideally, it should either return an InvalidKindName error or match all subtypes instead of doing nothing.

Tree-sitter recently introduced support for exposing supertype data. While the tree-sitter crate already includes the necessary API changes, the parser dependencies haven't been updated in the last four months. As a result, this PR won't have any effect until the tree-sitter team updates those crates using the new tree-sitter-cli. We can disable the test and merge, or just wait until the tree-sitter-typescript crate is updated.

To make the test pass, you can replace the tree-sitter-typescript dependency in core/Cargo.toml with the following:

tree-sitter-typescript = { package = "tree-sitter-typescript-codemod", version = "0.25.0-codemod.1" }

Summary by CodeRabbit

  • Chores
    • Upgraded the tree-sitter library dependency to version 0.25.3, enhancing underlying performance and stability.
  • Refactor
    • Revamped matching functionality to support multiple subtypes more effectively, with added test coverage for supertype matching.
    • Improved language compatibility checks by switching to ABI version retrieval for better validation.
    • Standardized UTF‑16 parsing to consistently use little‑endian encoding for reliable text processing.

Copy link
Contributor

coderabbitai bot commented Mar 13, 2025

Walkthrough

This pull request updates the tree-sitter dependency in the project by changing its version from 0.24.4 to 0.25.3. It also refactors the KindMatcher in the matcher module by replacing its single kind field with a BitSet of subtypes and revising the corresponding matching logic. Additionally, the language loading function now uses abi_version() instead of version(), and the document parser updates its UTF-16 parsing to little-endian. A new test case is added to validate supertype matching.

Changes

File(s) Summary
Cargo.toml Updated tree-sitter dependency version: 0.24.40.25.3.
crates/core/src/matcher/kind.rs Refactored KindMatcher: replaced kind with subtypes (BitSet), updated instantiation methods (new, from_id), matching logic, and added test_supertype_match.
crates/dynamic/src/lib.rs Modified load_ts_language to retrieve the language version using lang.abi_version() instead of lang.version().
crates/napi/src/doc.rs Changed the parse_tree_sitter method to call parser.parse_utf16_le instead of parser.parse_utf16.

Sequence Diagram(s)

KindMatcher Matching Process

sequenceDiagram
    participant Client
    participant KindMatcher
    participant BitSet
    Client->>KindMatcher: new(node_kind)
    alt node_kind is supertype
        KindMatcher->>BitSet: Populate with all subtypes
    else
        KindMatcher->>BitSet: Insert node_kind id
    end
    Client->>KindMatcher: match_node_with_env(node)
    KindMatcher-->>Client: Return match result (true/false)
Loading

Language Loading Process

sequenceDiagram
    participant Client
    participant LanguageLoader
    participant Language
    Client->>LanguageLoader: load_ts_language()
    LanguageLoader->>Language: abi_version()
    Language-->>LanguageLoader: Return ABI version
    LanguageLoader->>LanguageLoader: Validate version compatibility
    alt Version valid
        LanguageLoader-->>Client: Return language instance
    else
        LanguageLoader-->>Client: Return error
    end
Loading

Possibly related PRs

Suggested reviewers

  • HerringtonDarkholme

Poem

I’m a rabbit with a codey beat,
Hopping through updates oh so neat.
Changing versions and switching logic in play,
Subtypes now lead the matching way.
With every commit, I cheer and repeat—
A carrot-filled future, crisp and sweet!
🥕🐇

Tip

⚡🧪 Multi-step agentic review comment chat (experimental)
  • We're introducing multi-step agentic chat in review comments. This experimental feature enhances review discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments.
    - To enable this feature, set early_access to true under in the settings.
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
crates/core/src/matcher/kind.rs (1)

33-49: Consider validating empty subtypes.
The logic correctly populates subtypes for a supertype. However, if subtypes_for_supertype returns an empty set, you might want to handle that case (e.g., treat it as invalid).

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 84910e3 and 0b60e9f.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • Cargo.toml (1 hunks)
  • crates/core/src/matcher/kind.rs (4 hunks)
  • crates/dynamic/src/lib.rs (1 hunks)
  • crates/napi/src/doc.rs (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: coverage
🔇 Additional comments (11)
Cargo.toml (1)

36-36: Confirm upgrade to tree-sitter 0.25.3.
Please verify that this version aligns with upstream changes and does not introduce unexpected behavior.

Would you like me to check for any known compatibility issues or release notes for tree-sitter 0.25.3?

crates/napi/src/doc.rs (1)

59-59: Ensure parse_utf16_le suits all targeted platforms.
Switching from parse_utf16 to parse_utf16_le is correct for little-endian environments, but may break big-endian support. Confirm that big-endian architectures are not required or handle them separately.

Would you like me to run a script to confirm no big-endian references exist in the codebase?

crates/dynamic/src/lib.rs (1)

129-129: Validate usage of abi_version().
Switching from lang.version() to lang.abi_version() is potentially more accurate for ABI compatibility, but please confirm your range check remains valid.

I can help verify that MIN_COMPATIBLE_LANGUAGE_VERSION..=LANGUAGE_VERSION is appropriate for ABI version checking with tree-sitter 0.25.3.

crates/core/src/matcher/kind.rs (8)

27-27: Use of BitSet for subtypes
Replacing a single kind with a BitSet is a solid design choice for handling multiple subtypes.


50-51: Constructor usage is straightforward.
Storing the BitSet in Self is clearly organized.


66-67: Initialize subtypes for from_id.
Creating a new BitSet and inserting the provided kind looks good.


69-69: Returning Self with subtypes is clear.
No issues here; straightforward instantiation.


76-76: is_invalid check.
Using TS_BUILTIN_SYM_END (0) to detect an invalid kind is a suitable solution for unknown node kinds.


107-107: Check membership in subtypes.
This direct membership check is efficient and clear.


115-115: Expose potential kinds as a cloned BitSet.
Returning a clone of subtypes is a clean approach for further usage.


156-169: Supertype matching test.
Tests confirm that a supertype correctly matches relevant subtypes. Nicely done.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
crates/core/src/matcher/kind.rs (1)

155-167: Good test coverage for the new supertype matching functionality.

The test correctly verifies that a matcher created with a supertype ("declaration") successfully matches a node of a subtype (class declaration).

Consider adding a few more test cases covering different supertype-subtype relationships to ensure comprehensive coverage of the feature.

  #[test]
  fn test_supertype_match() {
    let supertype_kind = "declaration";
    let cand = pattern_node("class A { a = 123 }");
    let cand = cand.root();
    let pattern = KindMatcher::new(supertype_kind, Tsx);
    assert!(
      pattern.find_node(cand.clone()).is_some(),
      "goal: {}, candidate: {}",
      supertype_kind,
      cand.to_sexp(),
    );
  }

+ #[test]
+ fn test_expression_supertype_match() {
+   let supertype_kind = "expression";
+   let cand = pattern_node("const a = 1 + 2");
+   let cand = cand.root();
+   let pattern = KindMatcher::new(supertype_kind, Tsx);
+   assert!(
+     pattern.find_node(cand.clone()).is_some(),
+     "goal: {}, candidate: {}",
+     supertype_kind,
+     cand.to_sexp(),
+   );
+ }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0b60e9f and d171492.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • Cargo.toml (1 hunks)
  • crates/core/src/matcher/kind.rs (4 hunks)
  • crates/dynamic/src/lib.rs (1 hunks)
  • crates/napi/src/doc.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • crates/dynamic/src/lib.rs
  • crates/napi/src/doc.rs
  • Cargo.toml
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: coverage
🔇 Additional comments (7)
crates/core/src/matcher/kind.rs (7)

27-27: Excellent change to support supertype matching.

Replacing the single kind: KindId field with a subtypes: BitSet is a great approach to support matching multiple kinds at once, which is essential for implementing supertype support.


32-53: LGTM - The implementation correctly handles both supertype and regular kind matching.

The new implementation elegantly handles both cases:

  1. For supertypes: retrieves all subtypes and populates the BitSet
  2. For regular kinds: simply adds the single kind to the BitSet

This approach maintains backward compatibility while adding the new supertype functionality.


64-71: Good adaptation of from_id to work with the new BitSet approach.

The method correctly initializes a BitSet with a single kind, ensuring compatibility with existing code that uses this factory method.


73-76: Validation logic correctly adapted to work with BitSet.

The check for invalid kinds appropriately verifies if the special TS_BUILTIN_SYM_END value is present in the set.


100-111: Matching logic properly updated to work with multiple subtypes.

The implementation now checks if the node's kind is contained within any of the subtypes in the BitSet, rather than doing a direct comparison to a single kind.


113-115: Proper implementation of potential_kinds.

The method correctly returns a clone of the entire BitSet containing all potential kinds that this matcher could match.


170-178:

❓ Verification inconclusive

Existing test needs to be updated.

The existing test for potential_kinds expects exactly one potential kind, but this assumption may no longer be valid when matching a supertype that has multiple subtypes.


🏁 Script executed:

#!/bin/bash
# Check if the test for potential_kinds passes with the supertype implementation
cargo test -p ast-grep-core --test matcher::kind::test::test_kind_potential_kinds -- --exact

# Check how many kinds are returned for a supertype
cat << 'EOF' > /tmp/check_supertype_kinds.rs
fn main() {
    use tree_sitter::Language;
    // This is just a placeholder for demonstration, the actual check
    // would depend on how to access Tree-sitter language definitions
    let lang = tree_sitter_typescript::language_tsx();
    let supertype_id = lang.id_for_node_kind("declaration", true);
    let subtypes = lang.subtypes_for_supertype(supertype_id);
    println!("Number of subtypes for 'declaration': {}", subtypes.len());
}
EOF

echo "Note: This script is demonstrative. The actual implementation would need to be adapted to your project's structure."

Length of output: 440


Attention: Update the Expected Number of Potential Kinds

The current test in crates/core/src/matcher/kind.rs (lines 170–178) expects exactly one potential kind. However, when matching a supertype that can have multiple subtypes, this assumption is no longer valid. Please update the test so that it verifies the returned potential kinds align with the new matching logic. Consider one of the following approaches:

  • Adjust the assertion: Instead of expecting a fixed length of one, validate that the list of potential kinds accurately reflects the supertype's multiple subtypes.
  • Parameterize the test: Make the expected count configurable or determined based on the actual language definition for the supertype.
  • Double-check language definitions: Ensure that the underlying Tree-sitter definitions (or any language-specific mappings) are correctly incorporated into the matching logic.

@HerringtonDarkholme
Copy link
Member

the coverage test is failing, would you like to fix it?

@mohebifar
Copy link
Member Author

the coverage test is failing, would you like to fix it?

The test is using tree-sitter-typescript and tree-sitter-typescript crate needs to be updated with the latest CLI, until then, this PR's test won't pass.

@HerringtonDarkholme
Copy link
Member

tree-sitter-typescript and tree-sitter-typescript crate needs to be updated with the latest CLI

sure, should we change this PR to draft now?

@mohebifar
Copy link
Member Author

mohebifar commented Mar 14, 2025 via email

Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Comments suppressed due to low confidence (3)

crates/napi/src/doc.rs:59

  • [nitpick] Ensure that switching from 'parse_utf16' to 'parse_utf16_le' is intentional and consistent with the encoding expectations throughout the application.
parser.parse_utf16_le(self.inner.as_slice(), tree)

crates/dynamic/src/lib.rs:129

  • [nitpick] Consider renaming the variable from 'version' to 'abi_version' for greater clarity on its meaning.
let version = lang.abi_version();

crates/core/src/matcher/kind.rs:37

  • If 'subtypes_for_supertype' returns an empty iterator for a supertype, the matcher may end up with an empty BitSet leading to unexpected matching behavior; consider handling or documenting this case explicitly.
if lang.get_ts_language().node_kind_is_supertype(kind_id) {

@mohebifar mohebifar marked this pull request as draft April 15, 2025 03:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants