[cfe-dev] Analysing a file with AST matcher tool takes significant more time than just compile the same file

Han Wang wanghan02 at gmail.com
Thu Jul 16 02:26:29 PDT 2015


#include <stdio.h>
#include <string>
#include <vector>
#include <system_error>
#include <type_traits>
#include "clang/AST/AST.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Lex/Lexer.h"
#include "clang/Tooling/CompilationDatabase.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/TargetSelect.h"

using namespace llvm;
using clang::tooling::newFrontendActionFactory;
using clang::tooling::Replacement;
using clang::tooling::CompilationDatabase;
using namespace clang;
using namespace clang::ast_matchers;
using namespace clang::driver;
using namespace clang::tooling;

static llvm::cl::OptionCategory ToolingSampleCategory("IsEmpty");


// Returns the text that makes up 'node' in the source.
// Returns an empty string if the text cannot be found.
template <typename T>
static std::string getText(const SourceManager &SourceManager, const T
&Node) {
  SourceLocation StartSpellingLocation =
      SourceManager.getSpellingLoc(Node.getLocStart());
  SourceLocation EndSpellingLocation =
      SourceManager.getSpellingLoc(Node.getLocEnd());
  if (!StartSpellingLocation.isValid() || !EndSpellingLocation.isValid()) {
    return std::string();
  }
  bool Invalid = true;
  const char *Text =
      SourceManager.getCharacterData(StartSpellingLocation, &Invalid);
  if (Invalid) {
    return std::string();
  }
  std::pair<FileID, unsigned> Start =
      SourceManager.getDecomposedLoc(StartSpellingLocation);
  std::pair<FileID, unsigned> End =
      SourceManager.getDecomposedLoc(Lexer::getLocForEndOfToken(
          EndSpellingLocation, 0, SourceManager, LangOptions()));
  if (Start.first != End.first) {
    // Start and end are in different files.
    return std::string();
  }
  if (End.second < Start.second) {
    // Shuffling text with macros may cause this.
    return std::string();
  }
  return std::string(Text, End.second - Start.second);
}

class ColIsEmptyHandler: public MatchFinder::MatchCallback {
public:
  ColIsEmptyHandler(Replacements *Replace): Replace(Replace) {};
  virtual void run(const MatchFinder::MatchResult &Result) {
    if (const CXXMemberCallExpr *callExpr =
Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>("Col::IsEmptyExpr")) {
      const std::string text = getText(*(Result.SourceManager), *callExpr);
      Replacement Rep(*(Result.SourceManager),
CharSourceRange::getTokenRange(SourceRange(callExpr->getCallee()->getExprLoc())),
"IsEmpty");
      Replace->insert(Rep);
    }
  }
private:
  Replacements *Replace;
};

class ColIsEmptyInClassHandler: public MatchFinder::MatchCallback {
public:
  ColIsEmptyInClassHandler(Replacements *Replace): Replace(Replace) {};
  virtual void run(const MatchFinder::MatchResult &Result) {
    if (const CXXMethodDecl *decl =
Result.Nodes.getNodeAs<clang::CXXMethodDecl>("Col::IsEmptyInClass")) {
      const std::string text = getText(*(Result.SourceManager), *decl);
      Replacement Rep(*(Result.SourceManager),
CharSourceRange::getTokenRange(SourceRange(decl->getNameInfo().getLoc())),
"IsEmpty");
      Replace->insert(Rep);
    }
  }
private:
  Replacements *Replace;
};

int main(int argc, const char **argv) {
  llvm::InitializeAllTargets();
  llvm::InitializeAllTargetMCs();
  llvm::InitializeAllAsmPrinters();
  llvm::InitializeAllAsmParsers();
  CommonOptionsParser op(argc, argv, ToolingSampleCategory);
  RefactoringTool Tool(op.getCompilations(), op.getSourcePathList());
  // Set up AST matcher callbacks.
  ColIsEmptyHandler HandlerForColIsEmpty(&Tool.getReplacements());
  ColIsEmptyInClassHandler
HandlerForColIsEmptyInClass(&Tool.getReplacements());
  MatchFinder Finder;
  Finder.addMatcher(memberCallExpr(hasDeclaration(methodDecl(
  hasName("Empty"),
ofClass(isSameOrDerivedFrom(hasName("Col")))))).bind("Col::IsEmptyExpr"),
&HandlerForColIsEmpty);
  Finder.addMatcher(methodDecl(hasName("Empty"),
ofClass(isSameOrDerivedFrom(hasName("Col")))).bind("Col::IsEmptyInClass"),
&HandlerForColIsEmptyInClass);
  // Run the tool and collect a list of replacements.
  if (int Result =
Tool.runAndSave(newFrontendActionFactory(&Finder).get())) {
    return Result;
  }
  llvm::outs() << "Replacements collected by the tool:\n";
  for (auto &r : Tool.getReplacements()) {
    llvm::outs() << r.toString() << "\n";
  }
  return 0;
}

On Thu, Jul 16, 2015 at 11:11 AM, Manuel Klimek <klimek at google.com> wrote:

> Can you post the code for the matcher?
>
> On Thu, Jul 16, 2015 at 11:02 AM Han Wang <wanghan02 at gmail.com> wrote:
>
>> Hi,
>>
>> I'm using clang AST matcher tool on MAC to analyse complex files with
>> many many headers included. It turns out that it takes more than 10 times
>> longer than just compile the same file with clang. Is it normal or am I
>> doing something wrong? Thanks!
>>
>> Best regards,
>> Han
>> _______________________________________________
>> cfe-dev mailing list
>> cfe-dev at cs.uiuc.edu
>> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20150716/f1e88a3f/attachment.html>


More information about the cfe-dev mailing list