summaryrefslogtreecommitdiffstats
path: root/tools/clang/empty_string
diff options
context:
space:
mode:
authordcheng@chromium.org <dcheng@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-02 23:46:53 +0000
committerdcheng@chromium.org <dcheng@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-02 23:46:53 +0000
commitd1a751f18e6591c5d53086da863f8d3421da08f3 (patch)
tree4ce6861ed1c9958d6ef78bab075a289c54098eb3 /tools/clang/empty_string
parentf78ac522f8fc1463797b4de6831f9f0ee58566f8 (diff)
downloadchromium_src-d1a751f18e6591c5d53086da863f8d3421da08f3.zip
chromium_src-d1a751f18e6591c5d53086da863f8d3421da08f3.tar.gz
chromium_src-d1a751f18e6591c5d53086da863f8d3421da08f3.tar.bz2
Implement clang tool that converts std::string("") to std::string().
This is intended to be a simple demo of the clang tools functionality. BUG= Review URL: https://codereview.chromium.org/12746010 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@191936 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools/clang/empty_string')
-rw-r--r--tools/clang/empty_string/EmptyStringConverter.cpp196
-rw-r--r--tools/clang/empty_string/Makefile23
-rw-r--r--tools/clang/empty_string/tests/test-expected.cc38
-rw-r--r--tools/clang/empty_string/tests/test-original.cc38
4 files changed, 295 insertions, 0 deletions
diff --git a/tools/clang/empty_string/EmptyStringConverter.cpp b/tools/clang/empty_string/EmptyStringConverter.cpp
new file mode 100644
index 0000000..a9cc4b0
--- /dev/null
+++ b/tools/clang/empty_string/EmptyStringConverter.cpp
@@ -0,0 +1,196 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This implements a Clang tool to convert all instances of std::string("") to
+// std::string(). The latter is more efficient (as std::string doesn't have to
+// take a copy of an empty string) and generates fewer instructions as well. It
+// should be run using the tools/clang/scripts/run_tool.py helper.
+
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchFinder.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"
+
+using clang::ast_matchers::MatchFinder;
+using clang::ast_matchers::argumentCountIs;
+using clang::ast_matchers::bindTemporaryExpr;
+using clang::ast_matchers::constructorDecl;
+using clang::ast_matchers::constructExpr;
+using clang::ast_matchers::defaultArgExpr;
+using clang::ast_matchers::expr;
+using clang::ast_matchers::forEach;
+using clang::ast_matchers::has;
+using clang::ast_matchers::hasArgument;
+using clang::ast_matchers::hasDeclaration;
+using clang::ast_matchers::hasName;
+using clang::ast_matchers::id;
+using clang::ast_matchers::methodDecl;
+using clang::ast_matchers::newExpr;
+using clang::ast_matchers::ofClass;
+using clang::ast_matchers::stringLiteral;
+using clang::ast_matchers::varDecl;
+using clang::tooling::CommonOptionsParser;
+using clang::tooling::Replacement;
+using clang::tooling::Replacements;
+
+namespace {
+
+// Handles replacements for stack and heap-allocated instances, e.g.:
+// std::string a("");
+// std::string* b = new std::string("");
+class ConstructorCallback : public MatchFinder::MatchCallback {
+ public:
+ ConstructorCallback(Replacements* replacements)
+ : replacements_(replacements) {}
+
+ virtual void run(const MatchFinder::MatchResult& result) LLVM_OVERRIDE;
+
+ private:
+ Replacements* const replacements_;
+};
+
+// Handles replacements for invocations of std::string("") in an initializer
+// list.
+class InitializerCallback : public MatchFinder::MatchCallback {
+ public:
+ InitializerCallback(Replacements* replacements)
+ : replacements_(replacements) {}
+
+ virtual void run(const MatchFinder::MatchResult& result) LLVM_OVERRIDE;
+
+ private:
+ Replacements* const replacements_;
+};
+
+// Handles replacements for invocations of std::string("") in a temporary
+// context, e.g. FunctionThatTakesString(std::string("")). Note that this
+// handles implicits construction of std::string as well.
+class TemporaryCallback : public MatchFinder::MatchCallback {
+ public:
+ TemporaryCallback(Replacements* replacements) : replacements_(replacements) {}
+
+ virtual void run(const MatchFinder::MatchResult& result) LLVM_OVERRIDE;
+
+ private:
+ Replacements* const replacements_;
+};
+
+class EmptyStringConverter {
+ public:
+ explicit EmptyStringConverter(Replacements* replacements)
+ : constructor_callback_(replacements),
+ initializer_callback_(replacements),
+ temporary_callback_(replacements) {}
+
+ void SetupMatchers(MatchFinder* match_finder);
+
+ private:
+ ConstructorCallback constructor_callback_;
+ InitializerCallback initializer_callback_;
+ TemporaryCallback temporary_callback_;
+};
+
+void EmptyStringConverter::SetupMatchers(MatchFinder* match_finder) {
+ const auto& constructor_call =
+ id("call",
+ constructExpr(
+ hasDeclaration(methodDecl(ofClass(hasName("std::basic_string")))),
+ argumentCountIs(2),
+ hasArgument(0, id("literal", stringLiteral())),
+ hasArgument(1, defaultArgExpr())));
+
+ // Note that expr(has()) in the matcher is significant; the Clang AST wraps
+ // calls to the std::string constructor with exprWithCleanups nodes. Without
+ // the expr(has()) matcher, the first and last rules would not match anything!
+ match_finder->addMatcher(varDecl(forEach(expr(has(constructor_call)))),
+ &constructor_callback_);
+ match_finder->addMatcher(newExpr(has(constructor_call)),
+ &constructor_callback_);
+ match_finder->addMatcher(bindTemporaryExpr(has(constructor_call)),
+ &temporary_callback_);
+ match_finder->addMatcher(
+ constructorDecl(forEach(expr(has(constructor_call)))),
+ &initializer_callback_);
+}
+
+void ConstructorCallback::run(const MatchFinder::MatchResult& result) {
+ const clang::StringLiteral* literal =
+ result.Nodes.getNodeAs<clang::StringLiteral>("literal");
+ if (literal->getLength() > 0)
+ return;
+
+ const clang::CXXConstructExpr* call =
+ result.Nodes.getNodeAs<clang::CXXConstructExpr>("call");
+ clang::CharSourceRange range =
+ clang::CharSourceRange::getTokenRange(call->getParenRange());
+ replacements_->insert(Replacement(*result.SourceManager, range, ""));
+}
+
+void InitializerCallback::run(const MatchFinder::MatchResult& result) {
+ const clang::StringLiteral* literal =
+ result.Nodes.getNodeAs<clang::StringLiteral>("literal");
+ if (literal->getLength() > 0)
+ return;
+
+ const clang::CXXConstructExpr* call =
+ result.Nodes.getNodeAs<clang::CXXConstructExpr>("call");
+ replacements_->insert(Replacement(*result.SourceManager, call, ""));
+}
+
+void TemporaryCallback::run(const MatchFinder::MatchResult& result) {
+ const clang::StringLiteral* literal =
+ result.Nodes.getNodeAs<clang::StringLiteral>("literal");
+ if (literal->getLength() > 0)
+ return;
+
+ const clang::CXXConstructExpr* call =
+ result.Nodes.getNodeAs<clang::CXXConstructExpr>("call");
+ // Differentiate between explicit and implicit calls to std::string's
+ // constructor. An implicitly generated constructor won't have a valid
+ // source range for the parenthesis.
+ clang::SourceRange range = call->getParenRange();
+ if (range.isValid()) {
+ replacements_->insert(Replacement(*result.SourceManager, literal, ""));
+ } else {
+ replacements_->insert(
+ Replacement(*result.SourceManager, call, "std::string()"));
+ }
+}
+
+} // namespace
+
+static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage);
+
+int main(int argc, const char* argv[]) {
+ CommonOptionsParser options(argc, argv);
+ clang::tooling::ClangTool tool(options.getCompilations(),
+ options.getSourcePathList());
+
+ Replacements replacements;
+ EmptyStringConverter converter(&replacements);
+ MatchFinder match_finder;
+ converter.SetupMatchers(&match_finder);
+
+ int result =
+ tool.run(clang::tooling::newFrontendActionFactory(&match_finder));
+ if (result != 0)
+ return result;
+
+ // Each replacement line should have the following format:
+ // r:<file path>:<offset>:<length>:<replacement text>
+ // Only the <replacement text> field can contain embedded ":" characters.
+ // TODO(dcheng): Use a more clever serialization.
+ llvm::outs() << "==== BEGIN EDITS ====\n";
+ for (const Replacement& r : replacements) {
+ llvm::outs() << "r:" << r.getFilePath() << ":" << r.getOffset() << ":"
+ << r.getLength() << ":" << r.getReplacementText() << "\n";
+ }
+ llvm::outs() << "==== END EDITS ====\n";
+
+ return 0;
+}
diff --git a/tools/clang/empty_string/Makefile b/tools/clang/empty_string/Makefile
new file mode 100644
index 0000000..d446412
--- /dev/null
+++ b/tools/clang/empty_string/Makefile
@@ -0,0 +1,23 @@
+# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# This Makefile requires the LLVM build system. In order to build this tool,
+# please run tools/clang/scripts/build_tool.py.
+
+CLANG_LEVEL := ../..
+
+TOOLNAME = empty_string
+
+NO_INSTALL = 1
+
+include $(CLANG_LEVEL)/../../Makefile.config
+
+LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc
+USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \
+ clangRewriteFrontend.a clangRewriteCore.a clangParse.a clangSema.a \
+ clangAnalysis.a clangAST.a clangASTMatchers.a clangEdit.a \
+ clangLex.a clangBasic.a
+
+include $(CLANG_LEVEL)/Makefile
+
diff --git a/tools/clang/empty_string/tests/test-expected.cc b/tools/clang/empty_string/tests/test-expected.cc
new file mode 100644
index 0000000..88877b1
--- /dev/null
+++ b/tools/clang/empty_string/tests/test-expected.cc
@@ -0,0 +1,38 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Test file for the empty string clang tool.
+
+#include <string>
+
+// Tests for std::string declarations.
+void TestDeclarations() { std::string a, b("abc"), c; }
+
+// Tests for std::string allocated with new.
+void TestNew() {
+ std::string* a = new std::string,
+ *b = new std::string("abc"),
+ *c = new std::string,
+ *d = new std::string();
+}
+
+// Tests for std::string construction in initializer lists.
+class TestInitializers {
+ public:
+ TestInitializers() {}
+ TestInitializers(bool) {}
+ TestInitializers(double) : b("cat"), c() {}
+
+ private:
+ std::string a;
+ std::string b;
+ std::string c;
+};
+
+// Tests for temporary std::strings.
+void TestTemporaries(const std::string& reference_argument,
+ const std::string value_argument) {
+ TestTemporaries(std::string(), std::string());
+ TestTemporaries(std::string(), std::string());
+}
diff --git a/tools/clang/empty_string/tests/test-original.cc b/tools/clang/empty_string/tests/test-original.cc
new file mode 100644
index 0000000..f86fd08
--- /dev/null
+++ b/tools/clang/empty_string/tests/test-original.cc
@@ -0,0 +1,38 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Test file for the empty string clang tool.
+
+#include <string>
+
+// Tests for std::string declarations.
+void TestDeclarations() { std::string a(""), b("abc"), c(""); }
+
+// Tests for std::string allocated with new.
+void TestNew() {
+ std::string* a = new std::string(""),
+ *b = new std::string("abc"),
+ *c = new std::string(""),
+ *d = new std::string();
+}
+
+// Tests for std::string construction in initializer lists.
+class TestInitializers {
+ public:
+ TestInitializers() : a("") {}
+ TestInitializers(bool) : a(""), b("") {}
+ TestInitializers(double) : a(""), b("cat"), c() {}
+
+ private:
+ std::string a;
+ std::string b;
+ std::string c;
+};
+
+// Tests for temporary std::strings.
+void TestTemporaries(const std::string& reference_argument,
+ const std::string value_argument) {
+ TestTemporaries("", "");
+ TestTemporaries(std::string(""), std::string(""));
+}