C
This page curates a list of example ast-grep rules to check and to rewrite C code.
Match Function Call in C
Description
One of the common questions of ast-grep is to match function calls in C.
A plain pattern like test($A)
will not work. This is because tree-sitter-c parse the code snippet into macro_type_specifier
, see the pattern output.
To avoid this ambiguity, ast-grep lets us write a contextual pattern, which is a pattern inside a larger code snippet. We can use context
to write a pattern like this: test($A);
. Then, we can use the selector call_expression
to match only function calls.
YAML
id: match-function-call
language: c
rule:
pattern:
context: $M($$$);
selector: call_expression
Example
#define test(x) (2*x)
int a = test(2);
int main(){
int b = test(2);
}
Caveat
Note, tree-sitter-c parses code differently when it receives code fragment. For example,
test(a)
is parsed asmacro_type_specifier
test(a);
is parsed asexpression_statement -> call_expression
int b = test(a)
is parsed asdeclaration -> init_declarator -> call_expression
The behavior is controlled by how the tree-sitter parser is written. And tree-sitter-c behaves differently from tree-sitter-cpp.
Please file issues on tree-sitter-c repo if you want to change the behavior. ast-grep will respect changes and decision from upstream authors.
Rewrite Method to Function Call Has Fix
Description
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 deallocation 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.
YAML
id: method_receiver
language: c
rule:
pattern: $R.$METHOD($$$ARGS)
transform:
MAYBE_COMMA:
replace:
source: $$$ARGS
replace: '^.+'
by: ', '
fix:
$METHOD(&$R$MAYBE_COMMA$$$ARGS)
Example
void test_func() {
some_struct->field.method();
some_struct->field.other_method(1, 2, 3);
}
Diff
void test_func() {
some_struct->field.method();
method(&some_struct->field);
some_struct->field.other_method(1, 2, 3);
other_method(&some_struct->field, 1, 2, 3);
}
Contributed by
Surma, adapted from the original tweet
Rewrite Check to Yoda Condition Has Fix
Description
In programming jargon, a Yoda condition is a style that places the constant portion of the expression on the left side of the conditional statement. It is used to prevent assignment errors that may occur in languages like C.
YAML
id: may-the-force-be-with-you
language: c
rule:
pattern: $A == $B # Find equality comparison
inside: # inside an if_statement
kind: parenthesized_expression
inside: {kind: if_statement}
constraints: # with the constraint that
B: { kind: number_literal } # right side is a number
fix: $B == $A
The rule targets an equality comparison, denoted by the pattern $A == $B
. This comparison must occur inside an if_statement
. Additionally, there’s a constraint that the right side of the comparison, $B
, must be a number_literal like 42
.
Example
if (myNumber == 42) { /* ... */}
if (notMatch == another) { /* ... */}
if (notMatch) { /* ... */}
Diff
if (myNumber == 42) { /* ... */}
if (42 == myNumber) { /* ... */}
if (notMatch == another) { /* ... */}
if (notMatch) { /* ... */}
Contributed by
Inspired by this thread