[cfe-dev] [AST Matcher API] How to match against a subtree?

Gábor Márton via cfe-dev cfe-dev at lists.llvm.org
Thu Jul 26 02:52:19 PDT 2018


Hi Stephen,

Thanks for pointing out to findAll and it's weaknesses.
I went on and created a phabricator review:
https://reviews.llvm.org/D49840

Cheers,
GaborOn Thu, Jul 26, 2018 at 12:00 AM Stephen Kelly via cfe-dev
<cfe-dev at lists.llvm.org> wrote:
>
> Gábor Márton via cfe-dev wrote:
>
> > Hi,
> >
> > I'd like to do a traversal on a subtree rooted on a specific node, but
> > not on the whole tree rooted at the TranslataionUnitDecl. My
> > understanding is this is not possible today, or is it? I wonder why
> > this was not missing for anybody.
>
> Hi Gabor,
>
> I encountered the same issue, and wrote a similar solution. However, then I
> realized the ast_matchers::match documentation says:
>
> /// If you want to find all matches on the sub-tree rooted at \c Node
> (rather
> /// than only the matches on \c Node itself), surround the \c Matcher with a
> /// \c findAll().
>
> So, we should be able to do something like
>
>  Decl* someRootDeclContext = ...;
>
>  clang::ast_matchers::match(
>    decl(findAll(callExpr()))
>    , *someRootDeclContext, *Result.Context);
>
>
> However, that does not work because findAll is implemented with eachOf and
>
>    decl(eachOf(callExpr(), expr()))
>
> doesn't work for the same reason
>
>    decl(callExpr())
>
> doesn't work - a decl is never a callExpr.
>
> I filed a bug for this: https://bugs.llvm.org/show_bug.cgi?id=38318
>
> Meanwhile, findAll might work for your specific case, or you might be able
> to write a replacement or use something more specific.
>
> A case I encountered involved replacing the type of certain varDecl nodes in
> the source code, but porting them if they are passed by reference in a
> function call (a case I handled separately).
>
> Given
>
>     void takeRef(int&)
>     {
>
>     }
>
>     void takeConstRef(int const&)
>     {
>
>     }
>
>     void takeVal(int)
>     {
>
>     }
>
>     struct IntWrapper
>     {
>         IntWrapper(int i) : m_i(i) {}
>
>         operator int() { return m_i; }
>
>     private:
>         int m_i;
>     };
>
>     int main()
>     {
>         int a = 42;
>         int b = 7;
>
>         takeVal(a);
>         takeConstRef(a);
>
>         takeVal(b);
>         takeRef(b);
>     }
>
> the goal is to replace
>
>         int a = 42;
>
> with
>
>         IntWrapper a = 42;
>
> but leave
>
>         int b = 7;
>
> untouched because
>
>         takeRef(b);
>
> would not compile if the varDecl is ported.
>
>
> Here is my solution which uses match(). You should be able to do something
> similar for your case without the additional API.
>
> Thanks,
>
> Stephen.
>
>
>     #include "PortToIntwrapperCheck.h"
>     #include "clang/AST/ASTContext.h"
>     #include "clang/ASTMatchers/ASTMatchFinder.h"
>
>     using namespace clang::ast_matchers;
>
>     namespace clang {
>     namespace tidy {
>     namespace misc {
>
>     void PortToIntwrapperCheck::registerMatchers(MatchFinder *Finder) {
>
>       Finder->addMatcher(
>         varDecl(
>             hasType(asString("int")),
>             unless(parmVarDecl()),
>             hasDeclContext(decl().bind("varDeclContext"))
>             ).bind("portToIntWrapperIfPossible")
>         , this);
>     }
>
>     void PortToIntwrapperCheck::check(const MatchFinder::MatchResult
> &Result) {
>
>       if (const auto *portToIntWrapper =
> Result.Nodes.getNodeAs<VarDecl>("portToIntWrapperIfPossible"))
>       {
>         const auto *varDeclContext =
> Result.Nodes.getNodeAs<Decl>("varDeclContext");
>
>         // Got a match for a particular var.
>         // Check if it is passed by reference in any calls
>         // within the context it is defined in
>
>         auto usesByReference = clang::ast_matchers::match(
>             decl(forEachDescendant(
>                 callExpr(forEachArgumentWithParam(
>                     declRefExpr(
>                         to(varDecl(equalsNode(portToIntWrapper)))
>                         ).bind("arg"),
>                     parmVarDecl(
>                         hasType(lValueReferenceType(
>                             unless(pointee(isConstQualified()))
>                             ))
>                         )
>                     ))
>                 ))
>             , *varDeclContext, *Result.Context);
>
>         if (usesByReference.empty())
>         {
>             // All uses are safe to port to IntWrapper
>
>             auto tInfo = portToIntWrapper->getTypeSourceInfo();
>             auto tLoc = tInfo->getTypeLoc();
>
>             SourceRange typeRange = tLoc.getSourceRange();
>
>             diag(typeRange.getBegin(), "type of %0 should be IntWrapper")
>               << portToIntWrapper
>               << FixItHint::CreateReplacement(typeRange, "IntWrapper");
>         }
>       }
>     }
>
>     } // namespace misc
>     } // namespace tidy
>     } // namespace clang
>
>
> _______________________________________________
> cfe-dev mailing list
> cfe-dev at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev


More information about the cfe-dev mailing list