#include <string>
#include <vector>
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FormatVariadic.h"
using namespace clang::ast_matchers;
using clang::tooling::CommonOptionsParser;
using clang::tooling::Replacement;
using clang::tooling::Replacements;
namespace {
class FunctionDefCallback : public MatchFinder::MatchCallback {
public:
explicit FunctionDefCallback(std::vector<Replacement>* replacements)
: replacements_(replacements) {}
void run(const MatchFinder::MatchResult& result) override;
private:
std::vector<Replacement>* const replacements_;
};
class TraceAnnotator {
public:
explicit TraceAnnotator(std::vector<Replacement>* replacements)
: function_def_callback_(replacements) {}
void SetupMatchers(MatchFinder* match_finder);
private:
FunctionDefCallback function_def_callback_;
};
AST_MATCHER(clang::FunctionDecl, isImplicitFunctionTemplateSpecialization) {
switch (Node.getTemplateSpecializationKind()) {
case clang::TSK_ImplicitInstantiation:
return true;
case clang::TSK_Undeclared:
case clang::TSK_ExplicitSpecialization:
case clang::TSK_ExplicitInstantiationDeclaration:
case clang::TSK_ExplicitInstantiationDefinition:
return false;
}
}
AST_POLYMORPHIC_MATCHER(isInMacroLocation,
AST_POLYMORPHIC_SUPPORTED_TYPES(clang::Decl,
clang::Stmt,
clang::TypeLoc)) {
return Node.getBeginLoc().isMacroID();
}
void TraceAnnotator::SetupMatchers(MatchFinder* match_finder) {
const clang::ast_matchers::DeclarationMatcher function_call =
functionDecl(
has(compoundStmt().bind("function body")),
unless(anyOf(
avoid conflicting edits. */
isImplicitFunctionTemplateSpecialization(),
isConstexpr(), isDefaulted(),
cxxConstructorDecl(), cxxDestructorDecl(),
operators). */
isInMacroLocation(), has(compoundStmt(isInMacroLocation())),
than intended because of [&]). */
hasParent(cxxRecordDecl(isLambda())))))
.bind("function");
match_finder->addMatcher(function_call, &function_def_callback_);
}
std::string getFunctionName(const clang::FunctionDecl* function) {
std::string qualified_name;
if (auto* name_space = llvm::dyn_cast<clang::NamespaceDecl>(
function->getEnclosingNamespaceContext())) {
qualified_name += name_space->getQualifiedNameAsString();
qualified_name += "::";
}
if (auto* method = llvm::dyn_cast<clang::CXXMethodDecl>(function)) {
qualified_name += method->getParent()->getNameAsString();
qualified_name += "::";
}
qualified_name += function->getNameAsString();
return qualified_name;
}
void FunctionDefCallback::run(const MatchFinder::MatchResult& result) {
const clang::FunctionDecl* function =
result.Nodes.getNodeAs<clang::FunctionDecl>("function");
const clang::CompoundStmt* function_body =
result.Nodes.getNodeAs<clang::CompoundStmt>("function body");
clang::CharSourceRange range =
clang::CharSourceRange::getTokenRange(function_body->getBeginLoc());
const char kReplacementTextTemplate[] = R"( TRACE_EVENT0("test", "{0}"); )";
std::string function_name = getFunctionName(function);
std::string replacement_text =
llvm::formatv(kReplacementTextTemplate, function_name).str();
const char kAnnotationTemplate[] = " { {0}";
std::string annotation =
llvm::formatv(kAnnotationTemplate, replacement_text).str();
replacements_->push_back(
Replacement(*result.SourceManager, range, annotation));
}
}
static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage);
int main(int argc, const char* argv[]) {
llvm::cl::OptionCategory category("TraceAnnotator Tool");
llvm::Expected<CommonOptionsParser> options =
CommonOptionsParser::create(argc, argv, category);
if (!options) {
llvm::outs() << llvm::toString(options.takeError());
return 1;
}
clang::tooling::ClangTool tool(options->getCompilations(),
options->getSourcePathList());
std::vector<Replacement> replacements;
TraceAnnotator converter(&replacements);
MatchFinder match_finder;
converter.SetupMatchers(&match_finder);
std::unique_ptr<clang::tooling::FrontendActionFactory> frontend_factory =
clang::tooling::newFrontendActionFactory(&match_finder);
int result = tool.run(frontend_factory.get());
if (result != 0)
return result;
if (replacements.empty())
return 0;
std::set<std::string> include_added_to;
llvm::outs() << "==== BEGIN EDITS ====\n";
for (const auto& r : replacements) {
if (include_added_to.find(r.getFilePath().str()) ==
include_added_to.end()) {
include_added_to.insert(r.getFilePath().str());
llvm::outs() << "include-user-header:::" << r.getFilePath()
<< ":::-1:::-1:::base/trace_event/trace_event.h"
<< "\n";
}
llvm::outs() << "r:::" << r.getFilePath() << ":::" << r.getOffset()
<< ":::" << r.getLength() << ":::" << r.getReplacementText()
<< "\n";
}
llvm::outs() << "==== END EDITS ====\n";
return 0;
}