If this is not the correct mailing list for this question, please let me know.
I am trying to write a static analyzer for code base of my current job. The particular static analyzer I am writing involves a particular class of my company’s code base. Let’s say it is T. I want to write a checker to ensure T.ok() is called before T.value() is called.
Static analyzer is perfect for this type of check as it requires path sensitive checks. When I trying to write the checker, I basically checks for pre-call and post-call. I want to tell if a CallEvent is T.ok() and T.value(). Currently what I am doing is:
- From CallEvent, I cast to CXXMemberCall, and getOriginExpr(), then call getMethodDecl().
- From CXXMethodDecl, I first call getThisType(), then call getAsCXXRecordDecl(), then getQualifiedNameAsString(), and compare with qualified name of T, to make sure it is member of T.
- From CXXMethodDecl, I call getNameAsString() to get the method name, and compare them with “ok” and “value”.
It works, but it looks complicated, and involves string comparison, which I assume is slow. Is there a easier way, blessed by clang static analyzer official teams, that tell if a MethodDecl, or CXXRecordDecl, is the function or class I am interested in?
The ideal way is, there is one-time call to lookup the MethodDecl for T::ok() and T::value(), and CXXRecordDecl for T. Then ever since then, I just need to compare the pointers.
Another way is, the first time I found it is T, T::ok() or T::value(), I save the pointer of CXXRecordDecl or MethodDecl, and use the pointers later. Is this the reliable (assume I use the pointer canonical decl will not change) and blessed way to do it? If it is, is my steps above the correct and simplest way to determine it is T::ok or T::value? Is there some better and more reliable way?
You’re in the right place. Welcome You can also try to do the following chain: dyn_cast_or_null(CallEvent.getDecl()) to obtain CXXMethodDecl. To obtain parent class declaration from CXXMethodDecl, you can use getParent() method. So, the pseudocode will look like “MethodDecl->getParent()->getQualifiedNameAsString()”. This looks OK. But it can be useful to know that getQualifiedNameAsString() for CXXMethodDecl will also include parent name. So, you can just check if MethodDecl->getQualifiedNameAsString() is equal to “T::value” or “T::ok”. This is possible. You can just launch a simple matcher against AST: cxxMethodDecl(hasName(“::T::value”)) and get CXXMethodDecl you need and compare with it later. I think it is reliable enough: pointers to canonical declarations don’t change in AST.
Yeah, the traditional way to see if this is a certain method of a certain class is to compare the name of the method (CXXMethodDecl->getName()) and the class (CXXMethodDecl->getParent()->getName()), as strings, to the desired strings. getQualifiedNameAsString() works fine, but it's considered slow for whatever reason (i've never observed it personally). ASTMatchers would also work, even if a bit of an overkill. Don't forget to check the method's argument number and types as well. We have an effort to make this whole common idiom more simple via CallDescription, but it doesn't work for C++ methods yet.
With C++, you'll need to be aware of the bug we're currently having with object lifetime extension. If you're identifying the object by its memory region (which is correct), you'd occasionally notice that the region for a C++ temporary object may change along the analysis even if no copy-construction occurs. It has been making our new C++ checkers (most noticeably, the iterator checker) more difficult to write than they needed to be. More details in http://lists.llvm.org/pipermail/cfe-dev/2018-February/056898.html - i'm hoping to do something about it soon.