Search code examples
typesbooleanclangabstract-syntax-treematcher

How to set LangOptions for AST Matcher (bool vs _Bool)


Is there any way how to setup LangOptions for AST matcher? We have following matcher:

using namespace clang;
using namespace clang::ast_matchers;

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

class CMatcherCallback : public MatchFinder::MatchCallback
{
public:
    virtual void run(const MatchFinder::MatchResult& result) override
    {
        if (const CXXMethodDecl* method = result.Nodes.getNodeAs<CXXMethodDecl>("id"))
        {
            method->dumpColor();
        }
    }
};

int main(int argc, const char** argv)
{
    tooling::CommonOptionsParser optionsParser(argc, argv, OptionCategory);
    tooling::ClangTool tool(optionsParser.getCompilations(),
                            optionsParser.getSourcePathList());

    CMatcherCallback callback;
    MatchFinder finder;
    finder.addMatcher(cxxMethodDecl(
                          hasName("Method"),
                          hasParameter(0, parmVarDecl(hasType(asString("bool")))))
                          .bind("id"),
                      &callback);
    int ret = tool.run(tooling::newFrontendActionFactory(&finder).get());

    return ret;
}

It doesn't match this method:

class CTest
{
  void Method(bool);
};

It does match when we change type from hasParameter(0, parmVarDecl(hasType(asString("bool"))))) to _Bool. So it seems matcher is in C instead of C++ mode?


Solution

  • Answering the actual question

    This answer won't solve the original problem (explanation below), but solves the problem stated in the question. It might be useful if someone will search for a way to set the options.

    using namespace clang;
    using namespace clang::ast_matchers;
    
    static llvm::cl::OptionCategory OptionCategory("options");
    
    class CMatcherCallback : public MatchFinder::MatchCallback {
    public:
      virtual void run(const MatchFinder::MatchResult &result) override {
        if (const CXXMethodDecl *method =
                result.Nodes.getNodeAs<CXXMethodDecl>("id")) {
          method->dumpColor();
        }
      }
    };
    
    class FinderFrontendAction : public clang::ASTFrontendAction {
    public:
      FinderFrontendAction() {
        Finder.addMatcher(
            cxxMethodDecl(hasName("Method"),
                          hasParameter(0, parmVarDecl(hasType(asString("bool")))))
                .bind("id"),
            &Callback);
      }
    
      std::unique_ptr<clang::ASTConsumer>
      CreateASTConsumer(clang::CompilerInstance &CI, llvm::StringRef) override {
        auto &LO = CI.getLangOpts();
        LO.CPlusPlus = true;
        LO.Bool = true; // this is the actual option you are interested in
        return Finder.newASTConsumer();
      }
    
    private:
      MatchFinder Finder;
      CMatcherCallback Callback;
    };
    
    int main(int argc, const char **argv) {
      tooling::CommonOptionsParser optionsParser(argc, argv, OptionCategory);
      tooling::ClangTool tool(optionsParser.getCompilations(),
                              optionsParser.getSourcePathList());
    
      int ret =
          tool.run(tooling::newFrontendActionFactory<FinderFrontendAction>().get());
    
      return ret;
    }
    

    Why it won't change the outcome

    Clang understands that this is a C++ code and that it has bool type on its own. So this code literally changes nothing. You can see that when you print the AST, it actually prints bool.

    So where does _Bool come from

    This is a problem of how asString matcher is implemented. It uses QualType::getAsString() method (without parameters). Parametrized version of this function accepts PrintingPolicy, which defines how different types should be printed out.

    LangOpts define the way certain types are printed because PrintingPolicy is constructed from it, but not when you call QualType::getAsString without a printing policy. It uses default LangOpts constructor and bool becomes "_Bool".

    Suggested solution

    Just deal with it and use "_Bool". Otherwise you can implement your own matcher that combats this shortcoming.

    I hope this answers your question!