forked from OSchip/llvm-project
Put it in the correct place within the repository.
llvm-svn: 161398
This commit is contained in:
parent
cb36b8c2e6
commit
1d0717443f
|
@ -0,0 +1,6 @@
|
|||
add_clang_executable(remove-cstr-calls
|
||||
RemoveCStrCalls.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(remove-cstr-calls
|
||||
clangEdit clangTooling clangBasic clangAST clangASTMatchers)
|
|
@ -0,0 +1,23 @@
|
|||
##===- tools/remove-cstr-calls/Makefile --------------------*- Makefile -*-===##
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
##===----------------------------------------------------------------------===##
|
||||
|
||||
CLANG_LEVEL := ../..
|
||||
|
||||
TOOLNAME = remove-cstr-calls
|
||||
NO_INSTALL = 1
|
||||
|
||||
# No plugins, optimize startup time.
|
||||
TOOL_NO_EXPORTS = 1
|
||||
|
||||
LINK_COMPONENTS := support mc
|
||||
USEDLIBS = clangEdit.a clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \
|
||||
clangRewrite.a clangParse.a clangSema.a clangAnalysis.a \
|
||||
clangAST.a clangASTMatchers.a clangLex.a clangBasic.a
|
||||
|
||||
include $(CLANG_LEVEL)/Makefile
|
|
@ -0,0 +1,233 @@
|
|||
//===- examples/Tooling/RemoveCStrCalls.cpp - Redundant c_str call removal ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements a tool that prints replacements that remove redundant
|
||||
// calls of c_str() on strings.
|
||||
//
|
||||
// Usage:
|
||||
// remove-cstr-calls <cmake-output-dir> <file1> <file2> ...
|
||||
//
|
||||
// Where <cmake-output-dir> is a CMake build directory in which a file named
|
||||
// compile_commands.json exists (enable -DCMAKE_EXPORT_COMPILE_COMMANDS in
|
||||
// CMake to get this output).
|
||||
//
|
||||
// <file1> ... specify the paths of files in the CMake source tree. This path
|
||||
// is looked up in the compile command database. If the path of a file is
|
||||
// absolute, it needs to point into CMake's source tree. If the path is
|
||||
// relative, the current working directory needs to be in the CMake source
|
||||
// tree and the file must be in a subdirectory of the current working
|
||||
// directory. "./" prefixes in the relative files will be automatically
|
||||
// removed, but the rest of a relative path must be a suffix of a path in
|
||||
// the compile command line database.
|
||||
//
|
||||
// For example, to use remove-cstr-calls on all files in a subtree of the
|
||||
// source tree, use:
|
||||
//
|
||||
// /path/in/subtree $ find . -name '*.cpp'|
|
||||
// xargs remove-cstr-calls /path/to/build
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/ASTMatchers/ASTMatchers.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Frontend/FrontendActions.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
#include "clang/Tooling/CompilationDatabase.h"
|
||||
#include "clang/Tooling/Refactoring.h"
|
||||
#include "clang/Tooling/Tooling.h"
|
||||
#include "llvm/ADT/OwningPtr.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/Support/system_error.h"
|
||||
|
||||
using namespace clang;
|
||||
using namespace clang::ast_matchers;
|
||||
using namespace llvm;
|
||||
using clang::tooling::newFrontendActionFactory;
|
||||
using clang::tooling::Replacement;
|
||||
using clang::tooling::CompilationDatabase;
|
||||
|
||||
// FIXME: Pull out helper methods in here into more fitting places.
|
||||
|
||||
// Returns the text that makes up 'node' in the source.
|
||||
// Returns an empty string if the text cannot be found.
|
||||
template <typename T>
|
||||
static std::string getText(const SourceManager &SourceManager, const T &Node) {
|
||||
SourceLocation StartSpellingLocatino =
|
||||
SourceManager.getSpellingLoc(Node.getLocStart());
|
||||
SourceLocation EndSpellingLocation =
|
||||
SourceManager.getSpellingLoc(Node.getLocEnd());
|
||||
if (!StartSpellingLocatino.isValid() || !EndSpellingLocation.isValid()) {
|
||||
return std::string();
|
||||
}
|
||||
bool Invalid = true;
|
||||
const char *Text =
|
||||
SourceManager.getCharacterData(StartSpellingLocatino, &Invalid);
|
||||
if (Invalid) {
|
||||
return std::string();
|
||||
}
|
||||
std::pair<FileID, unsigned> Start =
|
||||
SourceManager.getDecomposedLoc(StartSpellingLocatino);
|
||||
std::pair<FileID, unsigned> End =
|
||||
SourceManager.getDecomposedLoc(Lexer::getLocForEndOfToken(
|
||||
EndSpellingLocation, 0, SourceManager, LangOptions()));
|
||||
if (Start.first != End.first) {
|
||||
// Start and end are in different files.
|
||||
return std::string();
|
||||
}
|
||||
if (End.second < Start.second) {
|
||||
// Shuffling text with macros may cause this.
|
||||
return std::string();
|
||||
}
|
||||
return std::string(Text, End.second - Start.second);
|
||||
}
|
||||
|
||||
// Return true if expr needs to be put in parens when it is an
|
||||
// argument of a prefix unary operator, e.g. when it is a binary or
|
||||
// ternary operator syntactically.
|
||||
static bool needParensAfterUnaryOperator(const Expr &ExprNode) {
|
||||
if (dyn_cast<clang::BinaryOperator>(&ExprNode) ||
|
||||
dyn_cast<clang::ConditionalOperator>(&ExprNode)) {
|
||||
return true;
|
||||
}
|
||||
if (const CXXOperatorCallExpr *op =
|
||||
dyn_cast<CXXOperatorCallExpr>(&ExprNode)) {
|
||||
return op->getNumArgs() == 2 &&
|
||||
op->getOperator() != OO_PlusPlus &&
|
||||
op->getOperator() != OO_MinusMinus &&
|
||||
op->getOperator() != OO_Call &&
|
||||
op->getOperator() != OO_Subscript;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Format a pointer to an expression: prefix with '*' but simplify
|
||||
// when it already begins with '&'. Return empty string on failure.
|
||||
static std::string formatDereference(const SourceManager &SourceManager,
|
||||
const Expr &ExprNode) {
|
||||
if (const clang::UnaryOperator *Op =
|
||||
dyn_cast<clang::UnaryOperator>(&ExprNode)) {
|
||||
if (Op->getOpcode() == UO_AddrOf) {
|
||||
// Strip leading '&'.
|
||||
return getText(SourceManager, *Op->getSubExpr()->IgnoreParens());
|
||||
}
|
||||
}
|
||||
const std::string Text = getText(SourceManager, ExprNode);
|
||||
if (Text.empty()) return std::string();
|
||||
// Add leading '*'.
|
||||
if (needParensAfterUnaryOperator(ExprNode)) {
|
||||
return std::string("*(") + Text + ")";
|
||||
}
|
||||
return std::string("*") + Text;
|
||||
}
|
||||
|
||||
namespace {
|
||||
class FixCStrCall : public ast_matchers::MatchFinder::MatchCallback {
|
||||
public:
|
||||
FixCStrCall(tooling::Replacements *Replace)
|
||||
: Replace(Replace) {}
|
||||
|
||||
virtual void run(const ast_matchers::MatchFinder::MatchResult &Result) {
|
||||
const CallExpr *Call =
|
||||
Result.Nodes.getStmtAs<CallExpr>("call");
|
||||
const Expr *Arg =
|
||||
Result.Nodes.getStmtAs<Expr>("arg");
|
||||
const bool Arrow =
|
||||
Result.Nodes.getStmtAs<MemberExpr>("member")->isArrow();
|
||||
// Replace the "call" node with the "arg" node, prefixed with '*'
|
||||
// if the call was using '->' rather than '.'.
|
||||
const std::string ArgText = Arrow ?
|
||||
formatDereference(*Result.SourceManager, *Arg) :
|
||||
getText(*Result.SourceManager, *Arg);
|
||||
if (ArgText.empty()) return;
|
||||
|
||||
Replace->insert(Replacement(*Result.SourceManager, Call, ArgText));
|
||||
}
|
||||
|
||||
private:
|
||||
tooling::Replacements *Replace;
|
||||
};
|
||||
} // end namespace
|
||||
|
||||
const char *StringConstructor =
|
||||
"::std::basic_string<char, std::char_traits<char>, std::allocator<char> >"
|
||||
"::basic_string";
|
||||
|
||||
const char *StringCStrMethod =
|
||||
"::std::basic_string<char, std::char_traits<char>, std::allocator<char> >"
|
||||
"::c_str";
|
||||
|
||||
cl::opt<std::string> BuildPath(
|
||||
cl::Positional,
|
||||
cl::desc("<build-path>"));
|
||||
|
||||
cl::list<std::string> SourcePaths(
|
||||
cl::Positional,
|
||||
cl::desc("<source0> [... <sourceN>]"),
|
||||
cl::OneOrMore);
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
cl::ParseCommandLineOptions(argc, argv);
|
||||
std::string ErrorMessage;
|
||||
llvm::OwningPtr<CompilationDatabase> Compilations(
|
||||
CompilationDatabase::loadFromDirectory(BuildPath, ErrorMessage));
|
||||
if (!Compilations)
|
||||
llvm::report_fatal_error(ErrorMessage);
|
||||
tooling::RefactoringTool Tool(*Compilations, SourcePaths);
|
||||
ast_matchers::MatchFinder Finder;
|
||||
FixCStrCall Callback(&Tool.getReplacements());
|
||||
Finder.addMatcher(
|
||||
constructorCall(
|
||||
hasDeclaration(method(hasName(StringConstructor))),
|
||||
argumentCountIs(2),
|
||||
// The first argument must have the form x.c_str() or p->c_str()
|
||||
// where the method is string::c_str(). We can use the copy
|
||||
// constructor of string instead (or the compiler might share
|
||||
// the string object).
|
||||
hasArgument(
|
||||
0,
|
||||
id("call", call(
|
||||
callee(id("member", memberExpression())),
|
||||
callee(method(hasName(StringCStrMethod))),
|
||||
on(id("arg", expression()))))),
|
||||
// The second argument is the alloc object which must not be
|
||||
// present explicitly.
|
||||
hasArgument(
|
||||
1,
|
||||
defaultArgument())),
|
||||
&Callback);
|
||||
Finder.addMatcher(
|
||||
constructorCall(
|
||||
// Implicit constructors of these classes are overloaded
|
||||
// wrt. string types and they internally make a StringRef
|
||||
// referring to the argument. Passing a string directly to
|
||||
// them is preferred to passing a char pointer.
|
||||
hasDeclaration(method(anyOf(
|
||||
hasName("::llvm::StringRef::StringRef"),
|
||||
hasName("::llvm::Twine::Twine")))),
|
||||
argumentCountIs(1),
|
||||
// The only argument must have the form x.c_str() or p->c_str()
|
||||
// where the method is string::c_str(). StringRef also has
|
||||
// a constructor from string which is more efficient (avoids
|
||||
// strlen), so we can construct StringRef from the string
|
||||
// directly.
|
||||
hasArgument(
|
||||
0,
|
||||
id("call", call(
|
||||
callee(id("member", memberExpression())),
|
||||
callee(method(hasName(StringCStrMethod))),
|
||||
on(id("arg", expression())))))),
|
||||
&Callback);
|
||||
return Tool.run(newFrontendActionFactory(&Finder));
|
||||
}
|
||||
|
Loading…
Reference in New Issue