clang::FunctionDecl matcher matching twice

37 Views Asked by At

I have a clang matcher on a FunctionDecl node to get its return type and it also matches first argument depending on the situation.

I have a class file Instrumentizer, creating the matcher and calling the callback:

#include <string>
#include <vector>

#include "clang/Tooling/Tooling.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/CompilationDatabase.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"

#include "InstrumentizerMatchers.hpp"

using namespace clang::ast_matchers;

static llvm::cl::OptionCategory InstrumentizerAPICategory("");

class Instrumentizer {
public:
  Instrumentizer(std::string source) {
    this->source_file = source;
    
    const char* argv[] = {
      "instrumentizer",
      source.c_str(),
      "--"
    };
  
    int argc = 3;
    options = new CommonOptionsParser(argc, argv, InstrumentizerAPICategory);
    auto files = options->getSourcePathList();
    tool = new RefactoringTool(options->getCompilations(), files);

    this->Callback = new InstrumentizerCallback();
  }
  
  ~Instrumentizer();

  // Config
  void addTypeToMatch(std::string regex){
    
    // Base matcher
    TypeMatcher NaiveTypeMatcher = instrumentizer_matchers::asUnqualifiedType(asString(regex));
    // Typeloc matcher
    auto NaiveTypeLocMatcher = typeLoc(loc(NaiveTypeMatcher)).bind("type");
    // Checks if current node or its descendants have a float type (to detect arrays and pointers)
    // Also has a typeLoc variant
    TypeMatcher TypeMatcher = anyOf(hasDescendant(NaiveTypeMatcher), NaiveTypeMatcher);
    auto TypeLocMatcher = anyOf(hasDescendant(NaiveTypeLocMatcher), NaiveTypeLocMatcher);

    // Matches a function return type
    auto FunctionReturnMatcher = functionDecl(hasTypeLoc(TypeLocMatcher)).bind("floating_function");

    Matcher.addMatcher(FunctionReturnMatcher, Callback);
  }
   
  // Get infos  
  std::vector<variable_t>& getVariables() {
    // If it has not been done yet, run the tool with
    // callbacks that will obtain and save variable names/types
    std::cout << "in getVariables()" << std::endl;
    if(variables.size() == 0) {
      tool->run(newFrontendActionFactory(&Matcher).get());
    }
    return variables;
  }

private:
  // source file
  std::string source_file;
  // Store all the variables and types
  std::vector<variable_t> variables;

  clang::tooling::CommonOptionsParser *options;
  // Underlying tool used to analyse the code
  // It will also be used to apply the replacements for us
  clang::tooling::RefactoringTool *tool;
  // Matcher to be ran on the AST
  MatchFinder Matcher;
  InstrumentizerCallback *Callback;
};

The callback is defined here, with its run function:

#pragma once

#include <string>
#include <sstream>
#include <cstring>
#include <iostream>

#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"

using namespace clang::tooling;
using namespace clang::ast_matchers;
using namespace clang::ast_matchers::internal;

extern "C" {

typedef struct {
  size_t type_idx; // index into the type list
  char* name; // name of the variable/function
} variable_t;

} // extern C

namespace instrumentizer_matchers {
  // Matches if Name is contained in the string description of the qualType
  AST_MATCHER_P(clang::QualType, inString, std::string, Name) {
    return Node.getAsString().find(Name, Node.getAsString().length() - Name.length()) != -1;
  }
  
  // Matches if the type without Qualifiers matches
  AST_MATCHER_P(clang::QualType, asUnqualifiedType, Matcher<clang::QualType>, InnerMatcher) {
    return InnerMatcher.matches(Node.getLocalUnqualifiedType(), Finder, Builder);
  }
}

class InstrumentizerCallback : public MatchFinder::MatchCallback {
public:
  InstrumentizerCallback(){}

  ~InstrumentizerCallback(){} 


  virtual void run(const MatchFinder::MatchResult &Result) {
    auto SM = Result.SourceManager;
    
    // Handle function return types
    auto func_decl = Result.Nodes.getNodeAs<clang::FunctionDecl>("floating_function");
    if (func_decl) {
      auto beginLoc = func_decl->getBeginLoc();
      std::cout << "beginLoc: " << beginLoc.printToString(*SM) << std::endl;

      std::cout << "name: " << func_decl->getNameAsString() << std::endl; 
      
      auto type = Result.Nodes.getNodeAs<clang::TypeLoc>("type");
      std::cout << "position: " << type->getSourceRange().printToString(*SM) << std::endl;
      
      return;
    }
  }
};

The if (func_decl) and the couts inside are expected to happen when a function declaration is matched.

I use a main file:

#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Tooling.h"
#include "clang/Tooling/Refactoring.h"
#include "FloatingTypeFindingAction.h"

#include "Instrumentizer_class.cpp"

using namespace llvm;
using namespace clang::tooling;


int main(int argc, const char **argv) {
  
  Instrumentizer* inst = new Instrumentizer("test.cc");
  
  inst->addTypeToMatch("double");
  inst->addTypeToMatch("test");
  
  std::vector<variable_t> var = inst->getVariables();
  
}

to apply my Instrumentizer object on the test.cc file:

typedef double test;

double fun(test x){
    return x;
}

int main(int argc, char **argv) {

  return 0;
}

when inside getVariables(), the callback matches both the double and test types, and I just want the double in this case.

beginLoc: /local/ferro/testestestestestestestestest/src/../api/test.cc:3:1
name: fun
position: </local/ferro/testestestestestestestestest/src/../api/test.cc:3:1>
beginLoc: /local/ferro/testestestestestestestestest/src/../api/test.cc:3:1
name: fun
position: </local/ferro/testestestestestestestestest/src/../api/test.cc:3:12>
0

There are 0 best solutions below