Example 5: Traversing the AST tree
In this topic: |
Having looked at semantic information, let's look now at how our custom function might traverse the AST node tree. While a KAST expression makes this traversal very simple, there are times when you need to perform traversal within a custom function, which you can do with the ktc_proceed() API.
Simple KAST expression
Let's start with a simple KAST expression, such as:
// FuncDef / FuncBody / Stmt::CompoundStmt / Stmts[*]::ExprStmt
This expression navigates from a function definition into its body through the compound statement that bounds its content, and picks the first top-level expression statement in that compound.
void foo(int someParams) { int someVariables; for( ... ) { } someVariables = 32; // This ExprStmt will be our result }
In this exercise, our custom function, invoked on the usual FuncDef node, needs to traverse the function's various statements and do something with each expression statement. You can use the ktc_proceed() function for this purpose.
ktc_proceed() function
The ktc_proceed() function takes a starting node and a designator for an edge through which the checker should progress. If you examine a FuncDef node in Checker Studio, you'll see that it generally has child edges:
FuncDef DeclSpecs[] :: DeclSpec Declarator :: MaybeDeclarator KRParams[] :: DeclOrStmt FuncBody :: AnyFuncBody
Checker Studio's representation of the AST shows that from a FuncDef node, you can proceed through four different child edges (DeclSpecs, Declarator, KRParams and FuncBody), each of which has a particular specialization (DeclSpec, MaybeDeclarator, DeclOrStmt, and AnyFuncBody). To see the specialization for an edge, simply expand the node of interest and you'll see something like:
FuncDef DeclSpecs[] :: DeclSpec BuiltinType Declarator :: MaybeDeclarator Declarator KRParams[] :: DeclOrStmt FuncBody :: AnyFuncBody FuncBody
For example, if we want our checker to find all void functions in KAST, we could specialize our FuncDef like this:
// FuncDef / DeclSpecs[*]::BuiltinType [ @Spec = KTC_BUILTINTYPE_VOID ]
To achieve this objective using the ktc API, you can use a function sequence something like:
// Assume we start with 'node' referencing the FuncDef node, as usual int isVoid(ktc_tree_t node) { return ( (node = ktc_proceed(node, cid_DeclSpecs)) != 0 ) && ( ktc_isTreeType(node, tid_BuiltinType) ) && ( ktc_getBuiltinType(node) == KTC_BUILTINTYPE_VOID ) ? 1 : 0; } HOOKS_SET_START ... XPath_register_int_hook("isVoid", isVoid); HOOKS_SET_END
Now that same KAST statement can be simplified to:
// FuncDef [ isVoid() ]
Note that in a real-world situation, you would be more likely to check a function's type through its semantic information with ktc_sema_getFunctionType(), but the ktc_proceed() usage shown suits our example.