Skip to main content

Prompt Customization

Path-based instructions

This section explains how to add custom code review instructions for the entire project or specific file paths in your project using glob patterns. Developers can provide tailored review guidelines based on the file paths. These instructions are needed only if you want the reviewer to follow specific instructions besides the standard review.

Adding file path prompts allows developers to specify custom instructions for different parts of the codebase. For example, you may want to enforce a style guide by file types or directories.

Sample Usage

  • path: **/*.js

    instructions: Review the JavaScript code against the Google JavaScript style guide and point out any mismatches

  • path: tests/**.*

    instructions: Review the following unit test code written using the Mocha test library. Ensure that: The code adheres to best practices associated with Mocha. Descriptive test names are used to clearly convey the intent of each test.

note
  • Paths accept glob patterns.
  • Instructions generally work well for specific additional instructions. However, they are not that effective if you are instructing AI not to do something.
  • Test the review feedback on pull requests and tailor as necessary.

Abstract Syntax Tree (AST) instructions

note

Deep dive into AST patterns and ast-grep rules

This section explains how to add custom code review instructions using ast-grep rules. ast-grep is a tool used for searching code using abstract syntax trees (AST) patterns.

By default, you can add ast-grep rules by following these steps:

  1. Create a directory that keeps all the ast-grep rules in your project directory.
  2. Add individual .yaml files for each ast-grep rule within the newly created directory.
  3. Ensure that each .yaml file contains the necessary ast-grep rule configurations.
  4. Ensure that all rules contains a message property, that will be used in the review process.
  5. Add the rules' directory to the .coderabbit.yml file under tools.ast-grep configuration.
#...
reviews:
#...
tools:
ast-grep:
rules_directory: "custom-name"
#...

The rule object

Rule object is the core concept of ast-grep rule system and every other feature is built on top of it.

Below is the full list of fields in a rule object. Every rule field is optional and can be omitted, but at least one field should be present in a rule. A node will match a rule if and only if it satisfies all fields in the rule object.

rule:
# atomic rule
pattern: "search.pattern"
kind: "tree_sitter_node_kind"
regex: "rust|regex"
# relational rule
inside: { pattern: "sub.rule" }
has: { kind: "sub_rule" }
follows: { regex: "can|use|any" }
precedes: { kind: "multi_keys", pattern: "in.sub" }
# composite rule
all: [{ pattern: "match.all" }, { kind: "match_all" }]
any: [{ pattern: "match.any" }, { kind: "match_any" }]
not: { pattern: "not.this" }
matches: "utility-rule"

Three Rule Categories

To summarize the rule object fields above, we have three categories of rules:

  • Atomic Rule: the most basic rule that checks if AST nodes matches.
  • Relational Rule: rules that check if a node is surrounded by another node.
  • Composite Rule: rules that combine sub-rules together using logical operators.

These three categories of rules can be composed together to create more complex rules.

The rule object is inspired by the CSS selectors but with more composability and expressiveness. Thinking about how selectors in CSS works can help you understand the rule object!

Read ast-grep > documentation for detailed guides.

Atomic rule

Atomic rule defines the most basic matching rule that determines whether one syntax node matches the rule or not. There are three kinds of atomic rule: pattern, kind and regex.

Official documentation guide on Atomic Rule

Relational rule

A relational rule defines the relationship between two syntax nodes. There are four kinds of relational rule: inside, has, follows and precedes.

All four relational rules accept a sub-rule object as their value. The sub-rule will match the surrounding node, while the relational rule itself will match the target node.

Official documentation guide on Relational Rule

rule:
pattern: await $PROMISE
inside:
kind: for_in_statement
stopBy: end

Composite rule

A composite rule defines the logical relationship between multiple sub-rules. There are three kinds of composite rule: all, any and not.

all

The all rule matches if all sub-rules match.

rule:
all:
- pattern: console.log('Hello World');
- kind: expression_statement

any

any rule matches if any sub-rule matches.

rule:
any:
- pattern: var a = $A
- pattern: const a = $A
- pattern: let a = $A

not

not applies negation to a sub-rule. It matches if the sub-rule does not match.

rule:
pattern: console.log($GREETING)
not:
pattern: console.log('Hello World')

Official documentation guide on Composite Rule

Reusing rule as utility

ast-grep chooses to use YAML for rule representation. While this decision makes writing rules easier, it does impose some limitations on the rule authoring. One of the limitations is that rule objects cannot be reused.

Local utility rule

Local utility rules are defined in the utils field of the config file. Utils is a string-keyed dictionary.

For example, the following config file defines a local utility rule is-literal:

utils:
is-literal:
any:
- kind: string
- kind: number
- kind: boolean
rule:
matches: is-literal

Global utility rule

Global utility rules are defined in a separate file. But they are available across all rule configurations in the project.

To create global utility rules, you need to have the rules directory created on the root of your project and another utils directory inside the root of your project.

my-awesome-project   # project root
|- rules # rule directory
| |- my-rule.yml
|- utils # utils directory
| |- is-literal.yml

Also, you need to add the rules and utils directories to the .coderabbit.yml file under tools.ast-grep configuration.

#...
reviews:
#...
tools:
ast-grep:
rules_directory: "rules"
utils_directory: "utils"
#...
# is-literal.yml
id: is-literal
language: TypeScript
rule:
any:
- kind: "false"
- kind: undefined
- kind: "null"
- kind: "true"
- kind: regex
- kind: number
- kind: string

Official documentation guide on Utility Rule

Multiple Languages Support

CodeRabbit supports multiple programming languages for defining ast-grep rules.

  • JavaScript
  • Typescript
  • C#
  • Golang
  • Java
  • Kotlin
  • Rust
  • Python
  • C

Below are examples of ast-grep rules in different languages:

JavaScript

Importing files without an extension is not allowed

id: find-import-file
language: js
message: "Importing files without an extension is not allowed"
rule:
regex: "/[^.]+[^/]$"
kind: string_fragment
any:
- inside:
stopBy: end
kind: import_statement
- inside:
stopBy: end
kind: call_expression
has:
field: function
regex: "^import$"

No console.log allowed except console.error on the catch block

id: no-console-except-error
language: typescript
message: "No console.log allowed except console.error on the catch block"
rule:
any:
- pattern: console.error($$$)
not:
inside:
kind: catch_clause
stopBy: end
- pattern: console.$METHOD($$$)
constraints:
METHOD:
regex: "log|debug|warn"

C

In C, there is no built-in support for object-oriented programming, but some programmers use structs and function pointers to simulate classes and methods.

However, this style can have some drawbacks, such as:

  • Extra memory allocation and reallocation for the struct and the function pointer.
  • Indirection overhead when calling the function pointer.

A possible alternative is to use a plain function call with the struct pointer as the first argument.

id: method_receiver
language: c
rule:
pattern: $R.$METHOD($$$ARGS)
transform:
MAYBE_COMMA:
replace:
source: $$$ARGS
replace: "^.+"
by: ", "
fix: $METHOD(&$R$MAYBE_COMMA$$$ARGS)