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

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: Whee 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/empty_string/Makefile » ('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..907ab5af67320b6e32f3666d1221fc6d3811ae15
--- /dev/null
+++ b/tools/clang/empty_string/EmptyStringConverter.cpp
@@ -0,0 +1,183 @@
+//===---- tools/extra/ToolTemplate.cpp - Template for refactoring tool ----===//
+//
+// 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 simple clang tool for automatically converting
+// instantiations of std::string with an empty string literal to simply call
+// the default constructor. The goal is to provide a simple example for writing
+// a clang tool for rewriting Chromium code.
+//
+// Usage:
+// tool-template -p <path-to-build-dir> <file1> <file2> ...
+//
+// See <wiki page that is yet to be finished>.
+// TODO(dcheng): Figure out copyright blobs...
Nico 2013/03/27 16:35:29 Is there any reason this can't just have the regul
dcheng 2013/03/28 00:10:17 Done.
+//
+//===----------------------------------------------------------------------===//
+
+#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::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::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) {
+ // TODO(dcheng): This doesn't match std::wstring... but hopefully you're not
+ // using that, right?
+ const char kStringConstructorCall[] =
+ "::std::basic_string<char, std::char_traits<char>, std::allocator<char> >"
+ "::basic_string";
+ const auto& constructor_call = id(
+ "call",
+ constructExpr(hasDeclaration(methodDecl(hasName(kStringConstructorCall))),
+ argumentCountIs(2),
+ hasArgument(0, id("literal", stringLiteral())),
+ hasArgument(1, defaultArgExpr())));
+
+ match_finder->addMatcher(varDecl(forEach(constructor_call)),
+ &constructor_callback_);
+ match_finder->addMatcher(newExpr(has(constructor_call)),
+ &constructor_callback_);
+ match_finder->addMatcher(constructorDecl(forEach(constructor_call)),
+ &initializer_callback_);
+ match_finder->addMatcher(bindTemporaryExpr(has(constructor_call)),
+ &temporary_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: {
+ // TODO(dcheng): Implement.
+ break;
+ }
+ case kTemporaryMode: {
+ // A replacement for an explicit call to std::string("") won't end up
+ // replacing the closing parenthesis. Instead, detect explicit calls and
+ // handle them differently.
+ 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::RefactoringTool tool(options.getCompilations(),
+ options.getSourcePathList());
+
+ EmptyStringConverter converter(&tool.getReplacements());
+ MatchFinder match_finder;
+ converter.SetupMatchers(&match_finder);
+
+ // Intentionally avoid using runAndSave(). Instead, please run this tool using
+ // tools/clang/scripts/run_tool.py. See notes in run_tool.py for more
+ // information about why.
+ 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 : tool.getReplacements()) {
+ 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/empty_string/Makefile » ('J')

Powered by Google App Engine
This is Rietveld 408576698