Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1869)

Unified Diff: tools/clang/empty_string/EmptyStringConverter.cpp

Issue 12746010: Implement clang tool that converts std::string("") to std::string(). (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Update tool and rewriter to handle initializers. Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | tools/clang/empty_string/Makefile » ('j') | tools/clang/scripts/run_tool.py » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/clang/empty_string/EmptyStringConverter.cpp
diff --git a/tools/clang/empty_string/EmptyStringConverter.cpp b/tools/clang/empty_string/EmptyStringConverter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..526b20af3eda14efef7780669fe1d9bd28bbd0cd
--- /dev/null
+++ b/tools/clang/empty_string/EmptyStringConverter.cpp
@@ -0,0 +1,162 @@
+// 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.
+
+#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 {
+
+enum ReplacementMode {
+ kConstructorMode,
+ kInitializerMode,
+ kTemporaryMode,
+};
+
+class EmptyStringConverterCallback : public MatchFinder::MatchCallback {
+ public:
+ EmptyStringConverterCallback(ReplacementMode mode, Replacements* replacements)
+ : mode_(mode), replacements_(replacements) {}
+
+ virtual void run(const MatchFinder::MatchResult& result);
+
+ private:
+ const ReplacementMode mode_;
+ Replacements* const replacements_;
+};
+
+class EmptyStringConverter {
+ public:
+ explicit EmptyStringConverter(Replacements* replacements)
+ : constructor_callback_(kConstructorMode, replacements),
+ initializer_callback_(kInitializerMode, replacements),
+ temporary_callback_(kTemporaryMode, replacements) {}
+
+ void SetupMatchers(MatchFinder* match_finder);
+
+ private:
+ EmptyStringConverterCallback constructor_callback_;
+ EmptyStringConverterCallback initializer_callback_;
+ EmptyStringConverterCallback 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 EmptyStringConverterCallback::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");
+
+ switch (mode_) {
+ case kConstructorMode: {
+ clang::CharSourceRange range =
+ clang::CharSourceRange::getTokenRange(call->getParenRange());
+ replacements_->insert(Replacement(*result.SourceManager, range, ""));
+ break;
+ }
+ case kInitializerMode: {
+ replacements_->insert(Replacement(*result.SourceManager, call, ""));
+ break;
+ }
+ case kTemporaryMode: {
+ // 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()"));
+ }
+ break;
+ }
+ }
+}
+
+} // 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;
+}
« no previous file with comments | « no previous file | tools/clang/empty_string/Makefile » ('j') | tools/clang/scripts/run_tool.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698