#include <assert.h>
#include <stdlib.h>
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
#include "clang/AST/ASTContext.h"
#include "clang/AST/ParentMap.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchersMacros.h"
#include "clang/Analysis/CFG.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Lex/Lexer.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/Refactoring/AtomicChange.h"
#include "clang/Tooling/Tooling.h"
#include "clang/Tooling/Transformer/RewriteRule.h"
#include "clang/Tooling/Transformer/SourceCodeBuilders.h"
#include "clang/Tooling/Transformer/Stencil.h"
#include "clang/Tooling/Transformer/Transformer.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/TargetSelect.h"
using clang::tooling::AtomicChange;
using clang::tooling::AtomicChanges;
using clang::tooling::Transformer;
using namespace ::clang::ast_matchers;
using namespace ::clang::transformer;
namespace {
AST_MATCHER_P(clang::VarDecl,
hasInitStyle,
clang::VarDecl::InitializationStyle,
InitStyle) {
return Node.getInitStyle() == InitStyle;
}
Stencil maybeDerefSmart(std::string ID) {
return run([ID = std::move(ID)](const MatchFinder::MatchResult& result)
-> llvm::Expected<std::string> {
if (const auto* op_call =
result.Nodes.getNodeAs<clang::CXXOperatorCallExpr>(ID)) {
if (op_call->getOperator() == clang::OO_Arrow &&
op_call->getNumArgs() == 1) {
llvm::Optional<std::string> text = clang::tooling::buildDereference(
*op_call->getArg(0), *result.Context);
if (!text) {
return llvm::make_error<llvm::StringError>(
llvm::errc::invalid_argument,
"ID has no corresponding source: " + ID);
}
return *text;
}
}
return maybeDeref(std::move(ID))->eval(result);
});
}
auto GetAsStringMatcher() {
return materializeTemporaryExpr(
has(ignoringParenImpCasts(
cxxBindTemporaryExpr(has(cxxMemberCallExpr(
on(expr().bind("piece")),
callee(cxxMethodDecl(
ofClass(hasName("::base::BasicStringPiece")),
hasName("as_string")))))))))
.bind("as_string");
}
RewriteRule ReplaceAsStringWithExplicitStringConversionRule() {
return makeRule(GetAsStringMatcher(),
changeTo(cat("std::string(", maybeDerefSmart("piece"), ")")));
}
RewriteRule RewriteImplicitToExplicitStringConstructionRule() {
auto matcher = materializeTemporaryExpr(
GetAsStringMatcher(), hasParent(cxxConstructExpr(
hasDeclaration(cxxConstructorDecl(
ofClass(hasName("::std::basic_string")))),
hasParent(exprWithCleanups(hasParent(
varDecl(hasInitStyle(clang::VarDecl::CInit))
.bind("varDecl")))))));
return makeRule(
matcher,
editList({
remove(between(name("varDecl"), after(node("as_string")))),
insertAfter(name("varDecl"), cat("(", maybeDerefSmart("piece"), ")")),
}));
}
RewriteRule RemoveAsStringRule() {
static constexpr llvm::StringRef kMatchingStringMembers[] = {
"begin",
"cbegin",
"end",
"cend",
"rbegin",
"crbegin",
"rend",
"crend",
"at",
"front",
"back",
"size",
"length",
"max_size",
"empty",
"copy",
"compare",
"find",
"rfind",
"find_first_of",
"find_last_of",
"find_first_not_of",
"find_last_not_of",
"npos",
};
static constexpr llvm::StringRef kMatchingStringOperators[] = {
"==", "!=", "<", ">", "<=", ">=", "<<",
};
auto string_piece_construct_expr = cxxConstructExpr(hasDeclaration(
cxxConstructorDecl(ofClass(hasName("::base::BasicStringPiece")))));
auto matching_string_member_expr =
memberExpr(member(hasAnyName(kMatchingStringMembers))).bind("member");
auto matching_string_operator_call_expr = cxxOperatorCallExpr(
hasAnyOverloadedOperatorName(kMatchingStringOperators));
auto string_construct_expr = cxxConstructExpr(hasDeclaration(
cxxConstructorDecl(ofClass(hasName("::std::basic_string")))));
auto explicit_string_var_construct_expr = cxxConstructExpr(
string_construct_expr,
hasParent(exprWithCleanups(
hasParent(varDecl(unless(hasInitStyle(clang::VarDecl::CInit)))))));
auto string_class_member_construct_expr = cxxConstructExpr(
string_construct_expr,
hasParent(exprWithCleanups(hasParent(cxxConstructorDecl()))));
auto matcher = materializeTemporaryExpr(
GetAsStringMatcher(),
anyOf(
hasParent(string_piece_construct_expr),
hasParent(matching_string_member_expr),
hasParent(implicitCastExpr(hasParent(matching_string_member_expr))),
hasParent(matching_string_operator_call_expr),
hasParent(explicit_string_var_construct_expr),
hasParent(string_class_member_construct_expr)));
return makeRule(
matcher,
ifBound("member",
changeTo(node("member"), access("piece", cat(member("member")))),
changeTo(maybeDerefSmart("piece"))));
}
Transformer::ChangeConsumer GetConsumer(AtomicChanges& changes) {
return [&changes](llvm::Expected<AtomicChange> change) {
if (change)
changes.push_back(*change);
};
}
}
int main(int argc, const char* argv[]) {
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmParser();
static llvm::cl::OptionCategory tool_options("Tool options");
clang::tooling::CommonOptionsParser options(argc, argv, tool_options);
clang::tooling::ClangTool tool(options.getCompilations(),
options.getSourcePathList());
RewriteRule as_string_rule = applyFirst({
RemoveAsStringRule(),
RewriteImplicitToExplicitStringConstructionRule(),
ReplaceAsStringWithExplicitStringConversionRule(),
});
AtomicChanges changes;
Transformer transformer(as_string_rule, GetConsumer(changes));
MatchFinder match_finder;
transformer.registerMatchers(&match_finder);
auto factory = clang::tooling::newFrontendActionFactory(&match_finder);
int result = tool.run(factory.get());
if (result != 0)
return result;
if (changes.empty())
return 0;
llvm::outs() << "==== BEGIN EDITS ====\n";
for (const auto& change : changes) {
for (const auto& r : change.getReplacements()) {
std::string replacement(r.getReplacementText());
std::replace(replacement.begin(), replacement.end(), '\n', '\0');
llvm::outs() << "r:::" << r.getFilePath() << ":::" << r.getOffset()
<< ":::" << r.getLength() << ":::" << replacement << "\n";
}
for (const auto& header : change.getInsertedHeaders()) {
llvm::outs() << "include-user-header:::" << change.getFilePath()
<< ":::-1:::-1:::" << header << "\n";
}
}
llvm::outs() << "==== END EDITS ====\n";
return 0;
}