2
class __attribute__((annotate("some_important_string"))) Foo {
public:
    void do_something();
};

I want to find all the public methods in a class with a specific annotation. I have a matcher set up to get classes with annotations:

Matcher.addMatcher(cxxRecordDecl(anyOf(isStruct(), isClass()),
                                 hasAttr(attr::Annotate)).bind("class")

but there doesn't seem to be any way to match on the exact string of the annotation.

So, in my MatchCallback::run method, I check for the attribute string by getting the attributes, dyn_casting to AnnotateAttr and checking the annotation string ref:

auto &attrs = decl->getAttrs();
...
auto attribute_attr = dyn_cast<AnnotateAttr>(attr);
auto string_ref = attribute_attr->getAnnotation();

but now I'd really rather do more matching, than AST tree traversal. My understanding is the matchers are more stable than the tree traversal functions, and they seem to be more straight forward, too.

So is there any way to go back to using matchers after filtering the class for an annotation string?

I am currently filtering based on the following code and it works ok, but it's not how I'd like to do it:

        if (method->getAccess() != AS_public) {
            if (PRINT_SKIPPED_EXPORT_REASONS) printf("%s**%s is not public, skipping\n", indentation.c_str(), method_name.c_str());
            return;
        }
        if (method->isOverloadedOperator()) {
            if (PRINT_SKIPPED_EXPORT_REASONS) printf("%s**skipping overloaded operator %s\n", indentation.c_str(), method_name.c_str());
            return;
        }
        if (dyn_cast<CXXConstructorDecl>(method)) {
            if (PRINT_SKIPPED_EXPORT_REASONS) printf("%s**skipping constructor %s\n", indentation.c_str(), method_name.c_str());
            return;
        }
        if (dyn_cast<CXXDestructorDecl>(method)) {
            if (PRINT_SKIPPED_EXPORT_REASONS) printf("%s**skipping destructor %s\n", indentation.c_str(), method_name.c_str());
            return;
        }
        if (method->isPure()) {
            assert(method->isVirtual());
            if (PRINT_SKIPPED_EXPORT_REASONS) printf("%s**skipping pure virtual %s\n", indentation.c_str(), method_name.c_str());
            return;
        }
xaxxon
  • 19,189
  • 5
  • 50
  • 80

1 Answers1

1

I'm not sure that the Clang AST Matchers API currently (as of 3.9) supports restarting the matching process. As far as I can tell - MatchFinder includes methods to match a node MatchFinder.match, and to match a whole translation unit MatchFinder.matchAST, but not recursively match from a provided node.

One way around this problem is to simply make your matcher more complex, and embed all of the matching you want to do in one place (below).

#include <string>
#include <iostream>
#include "clang/AST/AST.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/Support/raw_ostream.h"

using namespace clang;
using namespace clang::ast_matchers;
using namespace clang::driver;
using namespace clang::tooling;

static llvm::cl::OptionCategory ToolingSampleCategory("Matcher Sample");

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

  void run(const MatchFinder::MatchResult &Result) override {
    if(const CXXRecordDecl* klass = Result.Nodes.getNodeAs<CXXRecordDecl>("klass"))
    {
      for(auto it : klass->attrs())
      {
        Attr& attr = (*it);
        auto annotatedAttr = dyn_cast<AnnotateAttr>(&attr);
        if((annotatedAttr == nullptr) || (std::string(annotatedAttr->getAnnotation()) != "meh"))
        {
            return;
        }
      }
    }
    if(const CXXMethodDecl* mthd = Result.Nodes.getNodeAs<CXXMethodDecl>("mthd"))
    {
      std::cout << mthd->getNameInfo().getAsString() << std::endl;
    }
  }
};

int main(int argc, const char **argv) {
  CommonOptionsParser op(argc, argv, ToolingSampleCategory);
  RefactoringTool Tool(op.getCompilations(), op.getSourcePathList());
  Handler handler;
  MatchFinder finder;
  finder.addMatcher(
          cxxRecordDecl(
              hasAttr(attr::Annotate),
              forEach(
                  cxxMethodDecl(
                      isPublic(),
                      unless(isPure()),
                      unless(cxxConstructorDecl()),
                      unless(cxxDestructorDecl())).bind("mthd")
              )).bind("klass"), &handler);

  Tool.run(newFrontendActionFactory(&finder).get());
  return 0;
}
Andrew Walker
  • 40,984
  • 8
  • 62
  • 84
  • Thanks. This is super useful not only because of the specific answer, but also because it makes it more clear to me the relationship between some of the different types I had previously just filed under "magic" - stuff I just copied/pasted from example code. – xaxxon Jul 03 '16 at 01:59
  • I've been doing some further testing, and it appears that matchAST will restart the matching process from the start of the translation unit, and that the related match function didn't do what I expected. I'm going to keep looking for a solution and will update if I find something – Andrew Walker Jul 03 '16 at 05:29
  • I've added a solution that appears to work correctly in a broader set of situations than the original code. – Andrew Walker Jul 03 '16 at 05:57