Using LLVM-15's clang-tidy linter, I'd like to enforce the use of a typedef. Instances of
std::unordered_set<my::namespace1::Class>
used as a variable declaration, function return, or in a using/typedef, should instead be written as
MyClassSet
where
using MyClassSet = std::unordered_set<my::namespace1::Class>
I've tried, eg,
const auto matcher =
varDecl(allOf(
cxxRecordDecl(matchesName("std::unordered_set")),
classTemplateSpecializationDecl(hasTemplateArgument(
0,
templateArgument(refersToType(hasDeclaration(
cxxRecordDecl(matchesName("my::namespace1::Class")))))))))
.bind("uset");
but this doesn't yield any matches and it's unclear to me whether it would actually avoid matching correct uses of the typedef.
Any help figuring out the right matcher would be appreciated!
Quick fix
The attempt in the question almost works for a subset of cases. It cannot match anything because
varDecl(allOf(cxxRecordDecl(...)))requires a matching entity to be both aVarDeclandCXXRecordDecl, which is not possible (they are distinct subclasses ofNamedDecl).The fix is to insert
hasTypebeforeallOfto constrain the type of the declared thing, rather than the declared thing itself. With that one change, the query now reports variable and parameter declarations, but not return types ortypedef/usingaliases, and does not whitelist theMyClassSetalias.Handling those other cases requires slightly adjusting the details of how the type is recognized, as shown below.
Matcher satisfying the specification
Here is a
clang-querycommand that reports any variable, parameter, field, ortypedef/usingalias where the type isstd::unordered_set<my::namespace1::Class>, so long as that type is expressed as a template specialization, as opposed to using a particular whitelistedusingalias. It is written as a shell script with two preliminary variable assignments for improved readability:Test case:
I have tested the above with LLVM+Clang 14.0 on Linux and it correctly reports all nine things in the first section and none in the second.
declaratorDeclmatchesDeclaratorDecl, which is the declaration of anything that uses declarator syntax. Most importantly here, it matches bothVarDeclandFieldDecl, whereasvarDeclonly matchesVarDecl. (I'm just assuming you want to match fields too.)In the AST matcher language,
allOfis usually implicit, so I have omitted it from my query, but it also works to have it explicitly.I chose to use
hasNamerather thanmatchesNamefor greater specificity, but either will work.For ease of experimentation and reproducibility, my matcher is a
clang-querycommand. Although I have not done so, it should be straightforward to translate it to a C++ LibASTMatchers matcher. I believe the effect ofIgnoreUnlessSpelledInSourcecan be achieved by callingclang::ParentMapContext::setTraversalKind(); see this answer for a little more detail about that.