cabstract-syntax-treeclang-ast-matchers

Replace expression with macro using clang AST


I am looking to change the following code, with the help of the clang ast matcher.

foo(NUM << DEV_SHIFT | DEVICE);

to

foo(ADDR(NUM, DEVICE));

with

#define ADDR(a, b) (((a) << NUM_SHIFT) | (b))

I have the following AST-matcher that seems to identify the code pretty well.

Finder->addMatcher(
       callExpr(hasArgument(                                                                                                                                                             
           0, binaryOperator(hasOperatorName("|"),
                             hasLHS(ignoringParenImpCasts(
                                 binaryOperator(hasOperatorName("<<")))))
                  .bind("replaceWithMacro"))),
       this);

But I have a problem to understand how to write the check and the translation. I am currently stuck with this code:

void FirstCheckCheck::check(const MatchFinder::MatchResult &Result) {
  // FIXME: Add callback implementation.
  if (const auto MatchedDecl =
          Result.Nodes.getNodeAs<CallExpr>("replaceWithMacro")) {
    diag(MatchedDecl->getExprLoc(), "CallExp");
  } else if (const auto MatchedDecl =
                 Result.Nodes.getNodeAs<Expr>("replaceWithMacro")) {
    diag(MatchedDecl->getExprLoc(), "Expr");
    diag(MatchedDecl->getBeginLoc(), "BeginLOC");
    diag(MatchedDecl->getEndLoc(), "EndLOC");
  }

I don't know how to extract the two variables as strings. I was looking at the documentation for the Expr class (http://clang.llvm.org/doxygen/classclang_1_1Expr.html), but I couldn't find something useful.

If somebody could point me into the right direction it would be appreciated.

Add edit's.


Solution

  • This is the solution that I am quite happy with. It is based on boq's suggestion from the comments.

    //===--- FirstCheckCheck.cpp - clang-tidy ---------------------------------===//
    //
    // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
    // See https://llvm.org/LICENSE.txt for license information.
    // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
    //
    //===----------------------------------------------------------------------===//
    
    #include "FirstCheckCheck.h"
    #include "clang/AST/ASTContext.h"
    #include "clang/ASTMatchers/ASTMatchFinder.h"
    
    using namespace clang::ast_matchers;
    
    namespace clang {
    namespace tidy {
    namespace misc {
    
    void FirstCheckCheck::registerMatchers(MatchFinder *Finder) {
      // FIXME: Add matchers.
      //
      Finder->addMatcher(
          callExpr(hasArgument(
              1,
              binaryOperator(
                  hasOperatorName("|"),
                  hasRHS(ignoringImplicit(
                      anyOf(declRefExpr().bind("moduleNum"), integerLiteral().bind(
                          "moduleNum")))),
                  hasLHS(ignoringParens(
                      binaryOperator(hasOperatorName("<<"),
                                     hasLHS(ignoringImplicit(anyOf(
                                         declRefExpr().bind("deviceNum"),
                                         integerLiteral().bind("deviceNum"))))))))
                  .bind("replaceWithMacro"))),
          this);
    }
    
    void FirstCheckCheck::check(const MatchFinder::MatchResult &Result) {
      // FIXME: Add callback implementation.
        std::string deviceNumString;
        std::string moduleNumString;
        std::string ReplacementText;
    
      if (const auto MatchedDecl =
              Result.Nodes.getNodeAs<Expr>("deviceNum")) {
    
        const LangOptions &Opts = getLangOpts();
    
        /* get device string */
        deviceNumString = Lexer::getSourceText(
            CharSourceRange::getTokenRange(MatchedDecl->getSourceRange()),
            *Result.SourceManager, Opts);
      }
      /* ((uint16_t)(deviceNum << 8 | moduleNum)) */
      if (const auto MatchedDecl =
              Result.Nodes.getNodeAs<Expr>("moduleNum")) {
    
        const LangOptions &Opts = getLangOpts();
    
        moduleNumString = Lexer::getSourceText(
            CharSourceRange::getTokenRange(MatchedDecl->getSourceRange()),
            *Result.SourceManager, Opts);
      }
      if (const auto MatchedDecl =
                     Result.Nodes.getNodeAs<Expr>("replaceWithMacro")) {
        const LangOptions &Opts = getLangOpts();
    
        ReplacementText = Lexer::getSourceText(
            CharSourceRange::getTokenRange(MatchedDecl->getSourceRange()),
            *Result.SourceManager, Opts);
    
        std::string replacementString =
            "ADDR(" + deviceNumString + ", " + moduleNumString + ")";
    
        FixItHint Hint = FixItHint::CreateReplacement(
            MatchedDecl->getSourceRange(), replacementString);
    
        diag(MatchedDecl->getBeginLoc(), "Replace with ADDR() macro") << Hint;
      }
    
      /* diag(MatchedDecl->getLocation(), "insert 'awesome'", DiagnosticIDs::Note)
       */
      /*     << FixItHint::CreateInsertion(MatchedDecl->getLocation(), "awesome_");
       */
    }
    
    } // namespace misc
    } // namespace tidy
    } // namespace clang