Looking for help with an ast matcher

Hi,

Hope this is the right channel for this question. I am trying to make an ast matcher for clang-tidy to find str1.compare(str2), where both str1 and str2 are std::string. I have this so far: http://i.imgur.com/sUma9WC.png . But I am having a hard time finding a way to bind an id to the str1 part of the expression.

Can anyone help me out with an idea?

Best regards,
Mads Ravn

Hi Mads,
Can you provide the code that you run clang-query on, or at least AST for the fragment you want to match?

Piotr

Adding cfe-dev, because it is related to clang, not LLVM.

Sorry for writing 3 emails,
I looked into http://clang.llvm.org/docs/LibASTMatchersReference.html? and it seems that matcher
on :

Matcher<CXXMemberCallExpr> | → | Matcher<Expr> InnerMatcher |

  • | - | - |

would be my candidate to check. If it won’t gonna work then send the test and AST dump and I will try to help.

Piotr

Hi Piotr,

I think I found a working matcher: match ifStmt(hasCondition(implicitCastExpr(hasImplicitDestinationType(isInteger()), has(cxxMemberCallExpr(callee(cxxMethodDecl(hasName(“compare”))), hasArgument(0, declRefExpr().bind(“str2”)), callee(memberExpr(has(declRefExpr().bind(“str1”))))))))).bind(“case1”)

This one bind to both str1 and str2 in str1.compare(str2). I have included a code segment below. I have attached a screenshot of this matcher working from clang-query.
HOWEVER - when I try to use the matcher in clang-tidy it will not work. It is because of the callee(memberExpr(has(declRefExpr().bind(“str1”)))) part of the matcher. If I remove that, I can match on the str1.compare(str2) from the code. But I need to bind str1 as well to test if it is a string.

Do you have any idea why a matcher would work in clang-query, but not in clang-tidy? It’s the same piece of code it is working on. Is there something wrong with my matcher (undefined behavior or something)?

Code:

#include
#include

int main() {
std::string str1{“aa”};
std::string str2{“bb”};

if(str1.compare(str2)) {
std::cout << “Strings not equal” << std::endl;
}

return 0;

}

Hi Mads,
I formatted your matcher a little bit just to understand it:

ifStmt(hasCondition(implicitCastExpr(hasImplicitDestinationType(isInteger()),
has(cxxMemberCallExpr(callee(cxxMethodDecl(hasName(“compare”))),
hasArgument(0, declRefExpr().bind(“str2”)),
callee(memberExpr(has(declRefExpr().bind(“str1”))))))))).bind(“case1”)

And this is the AST part that we care about

-IfStmt 0x7fba9625f5e8 <line:8:5, line:10:5>

-<<>>
-<<>>
-ImplicitCastExpr 0x7fba96258a80 <line:8:8, col:25> ‘_Bool’
`-CXXMemberCallExpr 0x7fba96258a20 <col:8, col:25> ‘int’

-MemberExpr 0x7fba962589e8 <col:8, col:13> ‘’ .compare 0x7fba956ba078
-ImplicitCastExpr 0x7fba96258a50 <col:8> 'const class std::__1::basic_string<char>' lvalue <NoOp> -DeclRefExpr 0x7fba962588e0 col:8 ‘std::string’:‘class std::__1::basic_string’ lvalue Var 0x7fba962585b8 ‘str1’ ‘std::string’:‘class std::__1::basic_string’
-ImplicitCastExpr 0x7fba96258a68 <col:21> 'const class std::__1::basic_string<char>' lvalue <NoOp> -DeclRefExpr 0x7fba962589b0 col:21 ‘std::string’:‘class std::__1::basic_string’ lvalue Var 0x7fba96258770 ‘str2’ ‘std::string’:‘class std::__1::basic_string’

The callee called twice seems weird. Try to replace second one with “onImplicitObjectArgument”, or “on”, or in the worst case - “has” (which would not be totally valid).

Piotr.

Hi Piotr,

Thanks. Yeah, it seemed a little weird, but it was what got me closest. I found out that the matcher I supplied here was working for clang-query 3.8.1. I’m working on a clang-tidy module for 4.0.0 - it’s not working there.

Could you elaborate on the “onImplicitObjectArgument”? There is no document on it on the clang page. So I wouldn’t know how it works or what it does.

Best regards,
Mads Ravn

Well, I don’t know if it works or not, but the name suggest that it matches to the “this” object.
Check if it works, check implementation and unit tests to find out :slight_smile:

BTW you should check matches on the clang-query with the same version as the one you develop on, because the implementation and names could change since 3.8.1.
Piotr

Hi Piotr,

It was the versions which was the problem… I should have checked.

I still haven’t been able to find a matcher to bind str1 to anything. I found another way to ensure it is a string, though.
My actual problem is solved, but I will still be trying to find out how I am going to bind str1. Hopefully, I can figure it out.

Thank you for your assistance!

Best regards,
Mads Ravn