Example 5: Traversing the AST tree
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 expressionLet'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() functionThe 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. |