diff --git a/tools/clang/rewrite_scoped_refptr/Makefile b/tools/clang/rewrite_scoped_refptr/Makefile new file mode 100644 index 0000000000000..84a204b13e237 --- /dev/null +++ b/tools/clang/rewrite_scoped_refptr/Makefile @@ -0,0 +1,25 @@ +# 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 = rewrite_scoped_refptr + +NO_INSTALL = 1 + +include $(CLANG_LEVEL)/../../Makefile.config + +LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option +USEDLIBS = clangFrontend.a clangSerialization.a clangDriver.a \ + clangTooling.a clangParse.a clangSema.a \ + clangStaticAnalyzerFrontend.a clangStaticAnalyzerCheckers.a \ + clangStaticAnalyzerCore.a clangAnalysis.a clangRewriteFrontend.a \ + clangRewrite.a clangEdit.a clangAST.a clangLex.a clangBasic.a \ + clangASTMatchers.a + +include $(CLANG_LEVEL)/Makefile + diff --git a/tools/clang/rewrite_scoped_refptr/RewriteScopedRefptr.cpp b/tools/clang/rewrite_scoped_refptr/RewriteScopedRefptr.cpp new file mode 100644 index 0000000000000..3f83cec2fc556 --- /dev/null +++ b/tools/clang/rewrite_scoped_refptr/RewriteScopedRefptr.cpp @@ -0,0 +1,237 @@ +// 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 rewrite all instances of +// scoped_refptr<T>'s implicit cast to T (operator T*) to an explicit call to +// the .get() method. + +#include <algorithm> +#include <memory> +#include <string> + +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/ASTMatchers/ASTMatchersMacros.h" +#include "clang/ASTMatchers/ASTMatchFinder.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/Tooling.h" +#include "llvm/Support/CommandLine.h" + +using namespace clang::ast_matchers; +using clang::tooling::CommonOptionsParser; +using clang::tooling::Replacement; +using clang::tooling::Replacements; +using llvm::StringRef; + +namespace clang { +namespace ast_matchers { + +const internal::VariadicDynCastAllOfMatcher<Decl, CXXConversionDecl> + conversionDecl; + +AST_MATCHER(QualType, isBoolean) { + return Node->isBooleanType(); +} + +} // namespace ast_matchers +} // namespace clang + +namespace { + +// Returns true if expr needs to be put in parens (eg: when it is an operator +// syntactically). +bool NeedsParens(const clang::Expr* expr) { + if (llvm::dyn_cast<clang::UnaryOperator>(expr) || + llvm::dyn_cast<clang::BinaryOperator>(expr) || + llvm::dyn_cast<clang::ConditionalOperator>(expr)) { + return true; + } + // Calls to an overloaded operator also need parens, except for foo(...) and + // foo[...] expressions. + if (const clang::CXXOperatorCallExpr* op = + llvm::dyn_cast<clang::CXXOperatorCallExpr>(expr)) { + return op->getOperator() != clang::OO_Call && + op->getOperator() != clang::OO_Subscript; + } + return false; +} + +class GetRewriterCallback : public MatchFinder::MatchCallback { + public: + explicit GetRewriterCallback(Replacements* replacements) + : replacements_(replacements) {} + virtual void run(const MatchFinder::MatchResult& result) override; + + private: + Replacements* const replacements_; +}; + +void GetRewriterCallback::run(const MatchFinder::MatchResult& result) { + const clang::CXXMemberCallExpr* const implicit_call = + result.Nodes.getNodeAs<clang::CXXMemberCallExpr>("call"); + const clang::Expr* arg = result.Nodes.getNodeAs<clang::Expr>("arg"); + + if (!implicit_call || !arg) + return; + + clang::CharSourceRange range = clang::CharSourceRange::getTokenRange( + result.SourceManager->getSpellingLoc(arg->getLocStart()), + result.SourceManager->getSpellingLoc(arg->getLocEnd())); + if (!range.isValid()) + return; // TODO(rsleevi): Log an error? + + // Handle cases where an implicit cast is being done by dereferencing a + // pointer to a scoped_refptr<> (sadly, it happens...) + // + // This rewrites both "*foo" and "*(foo)" as "foo->get()". + if (const clang::UnaryOperator* op = + llvm::dyn_cast<clang::UnaryOperator>(arg)) { + if (op->getOpcode() == clang::UO_Deref) { + const clang::Expr* const sub_expr = + op->getSubExpr()->IgnoreParenImpCasts(); + clang::CharSourceRange sub_expr_range = + clang::CharSourceRange::getTokenRange( + result.SourceManager->getSpellingLoc(sub_expr->getLocStart()), + result.SourceManager->getSpellingLoc(sub_expr->getLocEnd())); + if (!sub_expr_range.isValid()) + return; // TODO(rsleevi): Log an error? + std::string inner_text = clang::Lexer::getSourceText( + sub_expr_range, *result.SourceManager, result.Context->getLangOpts()); + if (inner_text.empty()) + return; // TODO(rsleevi): Log an error? + + if (NeedsParens(sub_expr)) { + inner_text.insert(0, "("); + inner_text.append(")"); + } + inner_text.append("->get()"); + replacements_->insert( + Replacement(*result.SourceManager, range, inner_text)); + return; + } + } + + std::string text = clang::Lexer::getSourceText( + range, *result.SourceManager, result.Context->getLangOpts()); + if (text.empty()) + return; // TODO(rsleevi): Log an error? + + // Unwrap any temporaries - for example, custom iterators that return + // scoped_refptr<T> as part of operator*. Any such iterators should also + // be declaring a scoped_refptr<T>* operator->, per C++03 24.4.1.1 (Table 72) + if (const clang::CXXBindTemporaryExpr* op = + llvm::dyn_cast<clang::CXXBindTemporaryExpr>(arg)) { + arg = op->getSubExpr(); + } + + // Handle iterators (which are operator* calls, followed by implicit + // conversions) by rewriting *it as it->get() + if (const clang::CXXOperatorCallExpr* op = + llvm::dyn_cast<clang::CXXOperatorCallExpr>(arg)) { + if (op->getOperator() == clang::OO_Star) { + // Note that this doesn't rewrite **it correctly, since it should be + // rewritten using parens, e.g. (*it)->get(). However, this shouldn't + // happen frequently, if at all, since it would likely indicate code is + // storing pointers to a scoped_refptr in a container. + text.erase(0, 1); + text.append("->get()"); + replacements_->insert(Replacement(*result.SourceManager, range, text)); + return; + } + } + + // The only remaining calls should be non-dereferencing calls (eg: member + // calls), so a simple ".get()" appending should suffice. + if (NeedsParens(arg)) { + text.insert(0, "("); + text.append(")"); + } + text.append(".get()"); + replacements_->insert(Replacement(*result.SourceManager, range, text)); +} + +} // namespace + +static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage); + +int main(int argc, const char* argv[]) { + llvm::cl::OptionCategory category("Remove scoped_refptr conversions"); + CommonOptionsParser options(argc, argv, category); + clang::tooling::ClangTool tool(options.getCompilations(), + options.getSourcePathList()); + + MatchFinder match_finder; + + // Finds all calls to conversion operator member function. This catches calls + // to "operator T*", "operator Testable", and "operator bool" equally. + StatementMatcher overloaded_call_matcher = memberCallExpr( + thisPointerType(recordDecl(isSameOrDerivedFrom("::scoped_refptr"), + isTemplateInstantiation())), + callee(conversionDecl()), + on(id("arg", expr()))); + + // This catches both user-defined conversions (eg: "operator bool") and + // standard conversion sequence (C++03 13.3.3.1.1), such as converting a + // pointer to a bool. + StatementMatcher implicit_to_bool = + implicitCastExpr(hasImplicitDestinationType(isBoolean())); + + // Avoid converting calls to of "operator Testable" -> "bool" and calls of + // "operator T*" -> "bool". + StatementMatcher bool_conversion_matcher = hasParent(expr( + anyOf(expr(implicit_to_bool), expr(hasParent(expr(implicit_to_bool)))))); + + // Find all calls to an operator overload that do NOT (ultimately) result in + // being cast to a bool - eg: where it's being converted to T* and rewrite + // them to add a call to get(). + // + // All bool conversions will be handled with the Testable trick, but that + // can only be used once "operator T*" is removed, since otherwise it leaves + // the call ambiguous. + Replacements get_replacements; + GetRewriterCallback get_callback(&get_replacements); + match_finder.addMatcher(id("call", expr(overloaded_call_matcher)), + &get_callback); + +#if 0 + // Finds all temporary scoped_refptr<T>'s being assigned to a T*. Note that + // this will result in two callbacks--both the above callback to append get() + // and this callback will match. + match_finder.addMatcher( + id("var", + varDecl(hasInitializer(ignoringImpCasts( + id("call", expr(overloaded_call_matcher)))), + hasType(pointerType()))), + &callback); + match_finder.addMatcher( + binaryOperator( + hasOperatorName("="), + hasLHS(declRefExpr(to(id("var", varDecl(hasType(pointerType())))))), + hasRHS(ignoringParenImpCasts( + id("call", expr(overloaded_call_matcher))))), + &callback); +#endif + + std::unique_ptr<clang::tooling::FrontendActionFactory> factory = + clang::tooling::newFrontendActionFactory(&match_finder); + int result = tool.run(factory.get()); + if (result != 0) + return result; + + // Serialization format is documented in tools/clang/scripts/run_tool.py + llvm::outs() << "==== BEGIN EDITS ====\n"; + for (const auto& r : get_replacements) { + std::string replacement_text = r.getReplacementText().str(); + std::replace(replacement_text.begin(), replacement_text.end(), '\n', '\0'); + llvm::outs() << "r:" << r.getFilePath() << ":" << r.getOffset() << ":" + << r.getLength() << ":" << replacement_text << "\n"; + } + llvm::outs() << "==== END EDITS ====\n"; + + return 0; +} diff --git a/tools/clang/rewrite_scoped_refptr/tests/scoped_refptr.h b/tools/clang/rewrite_scoped_refptr/tests/scoped_refptr.h new file mode 100644 index 0000000000000..1ebd323358b65 --- /dev/null +++ b/tools/clang/rewrite_scoped_refptr/tests/scoped_refptr.h @@ -0,0 +1,43 @@ +// 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. + +#ifndef SCOPED_REFPTR_H_ +#define SCOPED_REFPTR_H_ + +// Stub scoped_refptr<T> class that supports an implicit cast to T*. +template <class T> +class scoped_refptr { + public: + typedef T element_type; + scoped_refptr() : ptr_(0) {} + scoped_refptr(T* p) : ptr_(p) {} + scoped_refptr(const scoped_refptr<T>& r) : ptr_(r.ptr_) {} + + template <typename U> + scoped_refptr(const scoped_refptr<U>& r) + : ptr_(r.get()) {} + + ~scoped_refptr() {} + + T* get() const { return ptr_; } + operator T*() const { return ptr_; } + T* operator->() const { return ptr_; } + + scoped_refptr<T>& operator=(T* p) { + ptr_ = p; + return *this; + } + scoped_refptr<T>& operator=(const scoped_refptr<T>& r) { + return *this = r.ptr_; + } + template <typename U> + scoped_refptr<T>& operator=(const scoped_refptr<U>& r) { + return *this = r.get(); + } + + protected: + T* ptr_; +}; + +#endif // SCOPED_REFPTR_H_ diff --git a/tools/clang/rewrite_scoped_refptr/tests/test1-expected.cc b/tools/clang/rewrite_scoped_refptr/tests/test1-expected.cc new file mode 100644 index 0000000000000..a704d393324bc --- /dev/null +++ b/tools/clang/rewrite_scoped_refptr/tests/test1-expected.cc @@ -0,0 +1,16 @@ +// 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 "scoped_refptr.h" + +struct Foo { + int dummy; +}; + +// Case 1: An example of an unsafe conversion, where the object is freed by +// the time the function returns. +Foo* GetBuggyFoo() { + scoped_refptr<Foo> unsafe(new Foo); + return unsafe.get(); +} diff --git a/tools/clang/rewrite_scoped_refptr/tests/test1-original.cc b/tools/clang/rewrite_scoped_refptr/tests/test1-original.cc new file mode 100644 index 0000000000000..cafcfcc03550c --- /dev/null +++ b/tools/clang/rewrite_scoped_refptr/tests/test1-original.cc @@ -0,0 +1,16 @@ +// 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 "scoped_refptr.h" + +struct Foo { + int dummy; +}; + +// Case 1: An example of an unsafe conversion, where the object is freed by +// the time the function returns. +Foo* GetBuggyFoo() { + scoped_refptr<Foo> unsafe(new Foo); + return unsafe; +} diff --git a/tools/clang/rewrite_scoped_refptr/tests/test10-expected.cc b/tools/clang/rewrite_scoped_refptr/tests/test10-expected.cc new file mode 100644 index 0000000000000..a74cd7df9336d --- /dev/null +++ b/tools/clang/rewrite_scoped_refptr/tests/test10-expected.cc @@ -0,0 +1,16 @@ +// 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 "scoped_refptr.h" + +struct Foo { + int dummy; +}; + +int TestsAScopedRefptr() { + scoped_refptr<Foo> foo(new Foo); + if (foo.get()) + return 1; + return 0; +} diff --git a/tools/clang/rewrite_scoped_refptr/tests/test10-original.cc b/tools/clang/rewrite_scoped_refptr/tests/test10-original.cc new file mode 100644 index 0000000000000..24ecd744be401 --- /dev/null +++ b/tools/clang/rewrite_scoped_refptr/tests/test10-original.cc @@ -0,0 +1,16 @@ +// 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 "scoped_refptr.h" + +struct Foo { + int dummy; +}; + +int TestsAScopedRefptr() { + scoped_refptr<Foo> foo(new Foo); + if (foo) + return 1; + return 0; +} diff --git a/tools/clang/rewrite_scoped_refptr/tests/test11-expected.cc b/tools/clang/rewrite_scoped_refptr/tests/test11-expected.cc new file mode 100644 index 0000000000000..de4c01cacba0b --- /dev/null +++ b/tools/clang/rewrite_scoped_refptr/tests/test11-expected.cc @@ -0,0 +1,22 @@ +// 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 <vector> + +#include "scoped_refptr.h" + +struct Foo { + int dummy; +}; + +typedef std::vector<scoped_refptr<Foo> > FooList; + +void TestsAScopedRefptr() { + FooList list; + list.push_back(new Foo); + list.push_back(new Foo); + for (FooList::const_iterator it = list.begin(); it != list.end(); ++it) { + Foo* item = it->get(); + } +} diff --git a/tools/clang/rewrite_scoped_refptr/tests/test11-original.cc b/tools/clang/rewrite_scoped_refptr/tests/test11-original.cc new file mode 100644 index 0000000000000..48cf41b2799c3 --- /dev/null +++ b/tools/clang/rewrite_scoped_refptr/tests/test11-original.cc @@ -0,0 +1,22 @@ +// 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 <vector> + +#include "scoped_refptr.h" + +struct Foo { + int dummy; +}; + +typedef std::vector<scoped_refptr<Foo> > FooList; + +void TestsAScopedRefptr() { + FooList list; + list.push_back(new Foo); + list.push_back(new Foo); + for (FooList::const_iterator it = list.begin(); it != list.end(); ++it) { + Foo* item = *it; + } +} diff --git a/tools/clang/rewrite_scoped_refptr/tests/test12-expected.cc b/tools/clang/rewrite_scoped_refptr/tests/test12-expected.cc new file mode 100644 index 0000000000000..37f350c947b27 --- /dev/null +++ b/tools/clang/rewrite_scoped_refptr/tests/test12-expected.cc @@ -0,0 +1,45 @@ +// 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 <iterator> +#include <map> +#include <string> + +#include "scoped_refptr.h" + +struct Foo { + int dummy; +}; + +typedef std::map<std::string, scoped_refptr<const Foo> > MyMap; + +class MyIter + : public std::iterator<std::input_iterator_tag, scoped_refptr<const Foo> > { + public: + MyIter() {} + MyIter(const MyIter& other) : it_(other.it_) {} + explicit MyIter(MyMap::const_iterator it) : it_(it) {} + MyIter& operator++() { + ++it_; + return *this; + } + const scoped_refptr<const Foo> operator*() { return it_->second; } + bool operator!=(const MyIter& other) { return it_ != other.it_; } + bool operator==(const MyIter& other) { return it_ == other.it_; } + + private: + MyMap::const_iterator it_; +}; + +void TestsAScopedRefptr() { + MyMap map; + map["foo"] = new Foo; + map["bar"] = new Foo; + MyIter my_begin(map.begin()); + MyIter my_end(map.end()); + for (MyIter it = my_begin; it != my_end; ++it) { + const Foo* item = NULL; + item = it->get(); + } +} diff --git a/tools/clang/rewrite_scoped_refptr/tests/test12-original.cc b/tools/clang/rewrite_scoped_refptr/tests/test12-original.cc new file mode 100644 index 0000000000000..de4dd67258024 --- /dev/null +++ b/tools/clang/rewrite_scoped_refptr/tests/test12-original.cc @@ -0,0 +1,45 @@ +// 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 <iterator> +#include <map> +#include <string> + +#include "scoped_refptr.h" + +struct Foo { + int dummy; +}; + +typedef std::map<std::string, scoped_refptr<const Foo> > MyMap; + +class MyIter + : public std::iterator<std::input_iterator_tag, scoped_refptr<const Foo> > { + public: + MyIter() {} + MyIter(const MyIter& other) : it_(other.it_) {} + explicit MyIter(MyMap::const_iterator it) : it_(it) {} + MyIter& operator++() { + ++it_; + return *this; + } + const scoped_refptr<const Foo> operator*() { return it_->second; } + bool operator!=(const MyIter& other) { return it_ != other.it_; } + bool operator==(const MyIter& other) { return it_ == other.it_; } + + private: + MyMap::const_iterator it_; +}; + +void TestsAScopedRefptr() { + MyMap map; + map["foo"] = new Foo; + map["bar"] = new Foo; + MyIter my_begin(map.begin()); + MyIter my_end(map.end()); + for (MyIter it = my_begin; it != my_end; ++it) { + const Foo* item = NULL; + item = *it; + } +} diff --git a/tools/clang/rewrite_scoped_refptr/tests/test2-expected.cc b/tools/clang/rewrite_scoped_refptr/tests/test2-expected.cc new file mode 100644 index 0000000000000..dd6e0ee9ba67d --- /dev/null +++ b/tools/clang/rewrite_scoped_refptr/tests/test2-expected.cc @@ -0,0 +1,19 @@ +// 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 "scoped_refptr.h" + +struct Foo { + int dummy; +}; + +// Case 2: An example of an unsafe conversion, where the scoped_refptr<> is +// returned as a temporary, and as such both it and its object are only valid +// for the duration of the full expression. +scoped_refptr<Foo> GetBuggyFoo() { + return new Foo; +} +void UseBuggyFoo() { + Foo* unsafe = GetBuggyFoo(); +} diff --git a/tools/clang/rewrite_scoped_refptr/tests/test2-original.cc b/tools/clang/rewrite_scoped_refptr/tests/test2-original.cc new file mode 100644 index 0000000000000..dd6e0ee9ba67d --- /dev/null +++ b/tools/clang/rewrite_scoped_refptr/tests/test2-original.cc @@ -0,0 +1,19 @@ +// 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 "scoped_refptr.h" + +struct Foo { + int dummy; +}; + +// Case 2: An example of an unsafe conversion, where the scoped_refptr<> is +// returned as a temporary, and as such both it and its object are only valid +// for the duration of the full expression. +scoped_refptr<Foo> GetBuggyFoo() { + return new Foo; +} +void UseBuggyFoo() { + Foo* unsafe = GetBuggyFoo(); +} diff --git a/tools/clang/rewrite_scoped_refptr/tests/test3-expected.cc b/tools/clang/rewrite_scoped_refptr/tests/test3-expected.cc new file mode 100644 index 0000000000000..9a0bf605eb775 --- /dev/null +++ b/tools/clang/rewrite_scoped_refptr/tests/test3-expected.cc @@ -0,0 +1,22 @@ +// 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 "scoped_refptr.h" + +struct Foo { + int dummy; +}; + +void ExpectsScopedRefptr(const scoped_refptr<Foo>& param) { + Foo* foo = param.get(); +} + +void CallExpectsScopedRefptr() { + scoped_refptr<Foo> temp(new Foo); + ExpectsScopedRefptr(temp); +} + +void CallExpectsScopedRefptrWithRawPtr() { + ExpectsScopedRefptr(new Foo); +} diff --git a/tools/clang/rewrite_scoped_refptr/tests/test3-original.cc b/tools/clang/rewrite_scoped_refptr/tests/test3-original.cc new file mode 100644 index 0000000000000..eb40952cf890f --- /dev/null +++ b/tools/clang/rewrite_scoped_refptr/tests/test3-original.cc @@ -0,0 +1,22 @@ +// 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 "scoped_refptr.h" + +struct Foo { + int dummy; +}; + +void ExpectsScopedRefptr(const scoped_refptr<Foo>& param) { + Foo* foo = param; +} + +void CallExpectsScopedRefptr() { + scoped_refptr<Foo> temp(new Foo); + ExpectsScopedRefptr(temp); +} + +void CallExpectsScopedRefptrWithRawPtr() { + ExpectsScopedRefptr(new Foo); +} diff --git a/tools/clang/rewrite_scoped_refptr/tests/test4-expected.cc b/tools/clang/rewrite_scoped_refptr/tests/test4-expected.cc new file mode 100644 index 0000000000000..1fc61bc4166c9 --- /dev/null +++ b/tools/clang/rewrite_scoped_refptr/tests/test4-expected.cc @@ -0,0 +1,22 @@ +// 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 "scoped_refptr.h" + +struct Foo { + int dummy; +}; + +void ExpectsRawPtr(Foo* foo) { + Foo* temp = foo; +} + +void CallExpectsRawPtrWithScopedRefptr() { + scoped_refptr<Foo> ok(new Foo); + ExpectsRawPtr(ok.get()); +} + +void CallExpectsRawPtrWithRawPtr() { + ExpectsRawPtr(new Foo); +} diff --git a/tools/clang/rewrite_scoped_refptr/tests/test4-original.cc b/tools/clang/rewrite_scoped_refptr/tests/test4-original.cc new file mode 100644 index 0000000000000..3395b10c2e5ef --- /dev/null +++ b/tools/clang/rewrite_scoped_refptr/tests/test4-original.cc @@ -0,0 +1,22 @@ +// 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 "scoped_refptr.h" + +struct Foo { + int dummy; +}; + +void ExpectsRawPtr(Foo* foo) { + Foo* temp = foo; +} + +void CallExpectsRawPtrWithScopedRefptr() { + scoped_refptr<Foo> ok(new Foo); + ExpectsRawPtr(ok); +} + +void CallExpectsRawPtrWithRawPtr() { + ExpectsRawPtr(new Foo); +} diff --git a/tools/clang/rewrite_scoped_refptr/tests/test5-expected.cc b/tools/clang/rewrite_scoped_refptr/tests/test5-expected.cc new file mode 100644 index 0000000000000..d9d3924dd3cf6 --- /dev/null +++ b/tools/clang/rewrite_scoped_refptr/tests/test5-expected.cc @@ -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. + +#include "scoped_refptr.h" + +struct Foo { + int dummy; +}; + +struct Bar : public Foo { + int another_dummy; +}; + +// Ensure that the correct cast (the user-defined cast) is converted. +void ExpectsRawFooPtr(Foo* foo) { + Foo* temp = foo; +} + +void CallExpectsRawFooPtrWithBar() { + scoped_refptr<Bar> temp(new Bar); + ExpectsRawFooPtr(temp.get()); +} diff --git a/tools/clang/rewrite_scoped_refptr/tests/test5-original.cc b/tools/clang/rewrite_scoped_refptr/tests/test5-original.cc new file mode 100644 index 0000000000000..eb87a0744b8d1 --- /dev/null +++ b/tools/clang/rewrite_scoped_refptr/tests/test5-original.cc @@ -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. + +#include "scoped_refptr.h" + +struct Foo { + int dummy; +}; + +struct Bar : public Foo { + int another_dummy; +}; + +// Ensure that the correct cast (the user-defined cast) is converted. +void ExpectsRawFooPtr(Foo* foo) { + Foo* temp = foo; +} + +void CallExpectsRawFooPtrWithBar() { + scoped_refptr<Bar> temp(new Bar); + ExpectsRawFooPtr(temp); +} diff --git a/tools/clang/rewrite_scoped_refptr/tests/test6-expected.cc b/tools/clang/rewrite_scoped_refptr/tests/test6-expected.cc new file mode 100644 index 0000000000000..00bbcda1e5803 --- /dev/null +++ b/tools/clang/rewrite_scoped_refptr/tests/test6-expected.cc @@ -0,0 +1,24 @@ +// 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 "scoped_refptr.h" + +struct Foo { + int dummy; +}; + +struct Bar : public Foo { + int another_dummy; +}; + +// Ensure that scoped_refptr<A> -> scoped_refptr<B> conversions are not +// converted. +void ExpectsScopedPtr(const scoped_refptr<Foo>& foo) { + scoped_refptr<Foo> temp(foo); +} + +void CallExpectsScopedPtrWithBar() { + scoped_refptr<Bar> temp(new Bar); + ExpectsScopedPtr(temp); +} diff --git a/tools/clang/rewrite_scoped_refptr/tests/test6-original.cc b/tools/clang/rewrite_scoped_refptr/tests/test6-original.cc new file mode 100644 index 0000000000000..00bbcda1e5803 --- /dev/null +++ b/tools/clang/rewrite_scoped_refptr/tests/test6-original.cc @@ -0,0 +1,24 @@ +// 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 "scoped_refptr.h" + +struct Foo { + int dummy; +}; + +struct Bar : public Foo { + int another_dummy; +}; + +// Ensure that scoped_refptr<A> -> scoped_refptr<B> conversions are not +// converted. +void ExpectsScopedPtr(const scoped_refptr<Foo>& foo) { + scoped_refptr<Foo> temp(foo); +} + +void CallExpectsScopedPtrWithBar() { + scoped_refptr<Bar> temp(new Bar); + ExpectsScopedPtr(temp); +} diff --git a/tools/clang/rewrite_scoped_refptr/tests/test7-expected.cc b/tools/clang/rewrite_scoped_refptr/tests/test7-expected.cc new file mode 100644 index 0000000000000..31b15caafa07c --- /dev/null +++ b/tools/clang/rewrite_scoped_refptr/tests/test7-expected.cc @@ -0,0 +1,21 @@ +// 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 "scoped_refptr.h" + +struct Foo { + int dummy; +}; + +void ExpectsRawPtr(Foo* foo) { + Foo* temp = foo; +} + +// Ensure that de-referencing scoped_refptr<>'s are properly rewritten as +// ->get() calls. +Foo* GetHeapFoo() { + scoped_refptr<Foo>* heap_allocated = new scoped_refptr<Foo>(); + *heap_allocated = new Foo; + return heap_allocated->get(); +} diff --git a/tools/clang/rewrite_scoped_refptr/tests/test7-original.cc b/tools/clang/rewrite_scoped_refptr/tests/test7-original.cc new file mode 100644 index 0000000000000..faad6d69b78a4 --- /dev/null +++ b/tools/clang/rewrite_scoped_refptr/tests/test7-original.cc @@ -0,0 +1,21 @@ +// 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 "scoped_refptr.h" + +struct Foo { + int dummy; +}; + +void ExpectsRawPtr(Foo* foo) { + Foo* temp = foo; +} + +// Ensure that de-referencing scoped_refptr<>'s are properly rewritten as +// ->get() calls. +Foo* GetHeapFoo() { + scoped_refptr<Foo>* heap_allocated = new scoped_refptr<Foo>(); + *heap_allocated = new Foo; + return *heap_allocated; +} diff --git a/tools/clang/rewrite_scoped_refptr/tests/test8-expected.cc b/tools/clang/rewrite_scoped_refptr/tests/test8-expected.cc new file mode 100644 index 0000000000000..a7a211939f26e --- /dev/null +++ b/tools/clang/rewrite_scoped_refptr/tests/test8-expected.cc @@ -0,0 +1,26 @@ +// 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 "scoped_refptr.h" + +struct Foo { + int dummy; +}; + +struct Bar : public Foo { + int another_dummy; +}; + +void ExpectsRawPtr(Foo* foo) { + Foo* temp = foo; +} + +// Ensure that de-referencing scoped_refptr<>'s are properly rewritten as +// ->get() calls, and that the correct conversion is rewritten (eg: not the +// Bar* -> Foo* conversion). +Foo* GetHeapFoo() { + scoped_refptr<Bar>* heap_allocated = new scoped_refptr<Bar>(); + *heap_allocated = new Bar; + return heap_allocated->get(); +} diff --git a/tools/clang/rewrite_scoped_refptr/tests/test8-original.cc b/tools/clang/rewrite_scoped_refptr/tests/test8-original.cc new file mode 100644 index 0000000000000..e485ff04ac5cb --- /dev/null +++ b/tools/clang/rewrite_scoped_refptr/tests/test8-original.cc @@ -0,0 +1,26 @@ +// 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 "scoped_refptr.h" + +struct Foo { + int dummy; +}; + +struct Bar : public Foo { + int another_dummy; +}; + +void ExpectsRawPtr(Foo* foo) { + Foo* temp = foo; +} + +// Ensure that de-referencing scoped_refptr<>'s are properly rewritten as +// ->get() calls, and that the correct conversion is rewritten (eg: not the +// Bar* -> Foo* conversion). +Foo* GetHeapFoo() { + scoped_refptr<Bar>* heap_allocated = new scoped_refptr<Bar>(); + *heap_allocated = new Bar; + return *heap_allocated; +} diff --git a/tools/clang/rewrite_scoped_refptr/tests/test9-expected.cc b/tools/clang/rewrite_scoped_refptr/tests/test9-expected.cc new file mode 100644 index 0000000000000..614aff9a77731 --- /dev/null +++ b/tools/clang/rewrite_scoped_refptr/tests/test9-expected.cc @@ -0,0 +1,45 @@ +// 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 "scoped_refptr.h" + +struct Foo { + int dummy; +}; + +struct HasAScopedRefptr { + scoped_refptr<Foo> member; + + const scoped_refptr<Foo>& GetMemberAsScopedRefptr() const { return member; } + + Foo* GetMemberAsRawPtr() const { return member.get(); } +}; + +void ExpectsRawPtr(Foo* param) { + Foo* temp = param; +} + +void ExpectsScopedRefptr(const scoped_refptr<Foo>& param) { + Foo* temp = param.get(); +} + +void CallsRawWithMemberScopedRefptr() { + HasAScopedRefptr object; + ExpectsRawPtr(object.GetMemberAsScopedRefptr().get()); +} + +void CallsRawWithMemberRawPtr() { + HasAScopedRefptr object; + ExpectsRawPtr(object.GetMemberAsRawPtr()); +} + +void CallsScopedWithMemberScopedRefptr() { + HasAScopedRefptr object; + ExpectsScopedRefptr(object.GetMemberAsScopedRefptr()); +} + +void CallsScopedWithMemberRawPtr() { + HasAScopedRefptr object; + ExpectsScopedRefptr(object.GetMemberAsScopedRefptr()); +} diff --git a/tools/clang/rewrite_scoped_refptr/tests/test9-original.cc b/tools/clang/rewrite_scoped_refptr/tests/test9-original.cc new file mode 100644 index 0000000000000..e59ed6e9e10a8 --- /dev/null +++ b/tools/clang/rewrite_scoped_refptr/tests/test9-original.cc @@ -0,0 +1,45 @@ +// 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 "scoped_refptr.h" + +struct Foo { + int dummy; +}; + +struct HasAScopedRefptr { + scoped_refptr<Foo> member; + + const scoped_refptr<Foo>& GetMemberAsScopedRefptr() const { return member; } + + Foo* GetMemberAsRawPtr() const { return member; } +}; + +void ExpectsRawPtr(Foo* param) { + Foo* temp = param; +} + +void ExpectsScopedRefptr(const scoped_refptr<Foo>& param) { + Foo* temp = param.get(); +} + +void CallsRawWithMemberScopedRefptr() { + HasAScopedRefptr object; + ExpectsRawPtr(object.GetMemberAsScopedRefptr()); +} + +void CallsRawWithMemberRawPtr() { + HasAScopedRefptr object; + ExpectsRawPtr(object.GetMemberAsRawPtr()); +} + +void CallsScopedWithMemberScopedRefptr() { + HasAScopedRefptr object; + ExpectsScopedRefptr(object.GetMemberAsScopedRefptr()); +} + +void CallsScopedWithMemberRawPtr() { + HasAScopedRefptr object; + ExpectsScopedRefptr(object.GetMemberAsScopedRefptr()); +} diff --git a/tools/clang/scripts/run_tool.py b/tools/clang/scripts/run_tool.py index 03b4100a2f8da..3c4f44f83e27c 100755 --- a/tools/clang/scripts/run_tool.py +++ b/tools/clang/scripts/run_tool.py @@ -81,6 +81,7 @@ def _ExtractEditsFromStdout(build_directory, stdout): for line in lines[start_index + 1:end_index]: try: edit_type, path, offset, length, replacement = line.split(':', 4) + replacement = replacement.replace("\0", "\n"); # Normalize the file path emitted by the clang tool to be relative to the # current working directory. path = os.path.relpath(os.path.join(build_directory, path)) diff --git a/tools/clang/scripts/test_tool.py b/tools/clang/scripts/test_tool.py index a99e9f48ae3bc..09ff46033d56d 100755 --- a/tools/clang/scripts/test_tool.py +++ b/tools/clang/scripts/test_tool.py @@ -87,12 +87,12 @@ def main(argv): with open(actual, 'r') as f: actual_output = f.readlines() if actual_output != expected_output: - print '[ FAILED ] %s' % os.path.relpath(actual) failed += 1 for line in difflib.unified_diff(expected_output, actual_output, fromfile=os.path.relpath(expected), tofile=os.path.relpath(actual)): sys.stdout.write(line) + print '[ FAILED ] %s' % os.path.relpath(actual) # Don't clean up the file on failure, so the results can be referenced # more easily. continue