Index: clang-tidy/modernize/CMakeLists.txt =================================================================== --- clang-tidy/modernize/CMakeLists.txt +++ clang-tidy/modernize/CMakeLists.txt @@ -13,6 +13,7 @@ RawStringLiteralCheck.cpp RedundantVoidArgCheck.cpp ReplaceAutoPtrCheck.cpp + ReturnBracedInitListCheck.cpp ShrinkToFitCheck.cpp UseAutoCheck.cpp UseBoolLiteralsCheck.cpp Index: clang-tidy/modernize/ModernizeTidyModule.cpp =================================================================== --- clang-tidy/modernize/ModernizeTidyModule.cpp +++ clang-tidy/modernize/ModernizeTidyModule.cpp @@ -19,6 +19,7 @@ #include "RawStringLiteralCheck.h" #include "RedundantVoidArgCheck.h" #include "ReplaceAutoPtrCheck.h" +#include "ReturnBracedInitListCheck.h" #include "ShrinkToFitCheck.h" #include "UseAutoCheck.h" #include "UseBoolLiteralsCheck.h" @@ -53,6 +54,8 @@ "modernize-redundant-void-arg"); CheckFactories.registerCheck( "modernize-replace-auto-ptr"); + CheckFactories.registerCheck( + "modernize-return-braced-init-list"); CheckFactories.registerCheck("modernize-shrink-to-fit"); CheckFactories.registerCheck("modernize-use-auto"); CheckFactories.registerCheck( Index: clang-tidy/modernize/ReturnBracedInitListCheck.h =================================================================== --- /dev/null +++ clang-tidy/modernize/ReturnBracedInitListCheck.h @@ -0,0 +1,36 @@ +//===--- ReturnBracedInitListCheck.h - clang-tidy----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_RETURN_BRACED_INIT_LIST_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_RETURN_BRACED_INIT_LIST_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace modernize { + +/// Use a braced init list for return statements rather than unnecessary +/// repeating the return type name. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-return-braced-init-list.html +class ReturnBracedInitListCheck : public ClangTidyCheck { +public: + ReturnBracedInitListCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace modernize +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_RETURN_BRACED_INIT_LIST_H Index: clang-tidy/modernize/ReturnBracedInitListCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/modernize/ReturnBracedInitListCheck.cpp @@ -0,0 +1,97 @@ +//===--- ReturnBracedInitListCheck.cpp - clang-tidy------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ReturnBracedInitListCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Lexer.h" +#include "clang/Tooling/FixIt.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace modernize { + +void ReturnBracedInitListCheck::registerMatchers(MatchFinder *Finder) { + // Only register the matchers for C++. + if (!getLangOpts().CPlusPlus11) + return; + + // Skip list initialization and constructors with an initializer list. + auto ConstructExpr = + cxxConstructExpr( + unless(anyOf(hasDeclaration(cxxConstructorDecl(isExplicit())), + isListInitialization(), hasDescendant(initListExpr()), + isInTemplateInstantiation()))) + .bind("ctor"); + + auto CtorAsArgument = materializeTemporaryExpr(anyOf( + has(ConstructExpr), has(cxxFunctionalCastExpr(has(ConstructExpr))))); + + Finder->addMatcher( + functionDecl(isDefinition(), // Declarations don't have return statements. + returns(unless(anyOf(builtinType(), autoType()))), + hasDescendant(returnStmt(hasReturnValue( + has(cxxConstructExpr(has(CtorAsArgument))))))) + .bind("fn"), + this); +} + +void ReturnBracedInitListCheck::check(const MatchFinder::MatchResult &Result) { + const auto *MatchedFunctionDecl = Result.Nodes.getNodeAs("fn"); + const auto *MatchedConstructExpr = + Result.Nodes.getNodeAs("ctor"); + + // Don't make replacements in macro. + SourceLocation Loc = MatchedConstructExpr->getExprLoc(); + if (Loc.isMacroID()) + return; + + // Make sure that the return type matches the constructed type. + const QualType ReturnType = + MatchedFunctionDecl->getReturnType().getCanonicalType(); + const QualType ConstructType = + MatchedConstructExpr->getType().getCanonicalType(); + if (ReturnType != ConstructType) + return; + + auto Diag = diag(Loc, "avoid repeating the return type from the " + "declaration; use a braced initializer list instead"); + + const SourceRange CallParensRange = + MatchedConstructExpr->getParenOrBraceRange(); + + // Make sure there is an explicit constructor call. + if (CallParensRange.isInvalid()) + return; + + // Make sure that the ctor arguments match the declaration. + for (unsigned I = 0, NumParams = MatchedConstructExpr->getNumArgs(); + I < NumParams; ++I) { + if (const auto *VD = dyn_cast( + MatchedConstructExpr->getConstructor()->getParamDecl(I))) { + if (MatchedConstructExpr->getArg(I)->getType().getCanonicalType() != + VD->getType().getCanonicalType()) + return; + } + } + + // Range for constructor name and opening brace. + CharSourceRange CtorCallSourceRange = CharSourceRange::getTokenRange( + Loc, CallParensRange.getBegin().getLocWithOffset(-1)); + + Diag << FixItHint::CreateRemoval(CtorCallSourceRange) + << FixItHint::CreateReplacement(CallParensRange.getBegin(), "{") + << FixItHint::CreateReplacement(CallParensRange.getEnd(), "}"); +} + +} // namespace modernize +} // namespace tidy +} // namespace clang Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -67,6 +67,12 @@ Finds uses of inline assembler. +- New `modernize-return-braced-init-list + `_ check + + Finds and replaces explicit calls to the constructor in a return statement by + a braced initializer list so that the return type is not needlessly repeated. + Improvements to include-fixer ----------------------------- Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -109,6 +109,7 @@ modernize-raw-string-literal modernize-redundant-void-arg modernize-replace-auto-ptr + modernize-return-braced-init-list modernize-shrink-to-fit modernize-use-auto modernize-use-bool-literals Index: docs/clang-tidy/checks/modernize-return-braced-init-list.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/modernize-return-braced-init-list.rst @@ -0,0 +1,22 @@ +.. title:: clang-tidy - modernize-return-braced-init-list + +modernize-return-braced-init-list +================================= + +Replaces explicit calls to the constructor in a return with a braced +initializer list. This way the return type is not needlessly duplicated in the +function definition and the return statement. + +.. code:: c++ + + Foo bar() { + Baz baz; + return Foo(baz); + } + + // transforms to: + + Foo bar() { + Baz baz; + return {baz}; + } Index: test/clang-tidy/modernize-return-braced-init-list.cpp =================================================================== --- /dev/null +++ test/clang-tidy/modernize-return-braced-init-list.cpp @@ -0,0 +1,199 @@ +// RUN: %check_clang_tidy %s modernize-return-braced-init-list %t -- -- +// -std=c++14 + +namespace std { +typedef decltype(sizeof(int)) size_t; + +// libc++'s implementation +template +class initializer_list { + const _E *__begin_; + size_t __size_; + + initializer_list(const _E *__b, size_t __s) + : __begin_(__b), + __size_(__s) {} + +public: + typedef _E value_type; + typedef const _E &reference; + typedef const _E &const_reference; + typedef size_t size_type; + + typedef const _E *iterator; + typedef const _E *const_iterator; + + initializer_list() : __begin_(nullptr), __size_(0) {} + + size_t size() const { return __size_; } + const _E *begin() const { return __begin_; } + const _E *end() const { return __begin_ + __size_; } +}; + +template +class vector { +public: + vector(T) {} + vector(std::initializer_list) {} +}; +} + +class Bar {}; + +Bar b0; + +class Foo { +public: + Foo(Bar) {} + explicit Foo(Bar, unsigned int) {} + Foo(unsigned int) {} +}; + +class Baz { +public: + Foo m() { + Bar bm; + return Foo(bm); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: avoid repeating the return type from the declaration; use a braced initializer list instead [modernize-return-braced-init-list] + // CHECK-FIXES: return {bm}; + } +}; + +class Quux : public Foo { +public: + Quux(Bar bar) : Foo(bar) {} + Quux(unsigned, unsigned, unsigned k = 0) : Foo(k) {} +}; + +Foo f() { + Bar b1; + return Foo(b1); + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid repeating the return type + // CHECK-FIXES: return {b1}; +} + +Foo f2() { + Bar b2; + return {b2}; +} + +auto f3() { + Bar b3; + return Foo(b3); +} + +#define A(b) Foo(b) + +Foo f4() { + Bar b4; + return A(b4); +} + +Foo f5() { + Bar b5; + return Quux(b5); +} + +Foo f6() { + Bar b6; + return Foo(b6, 1); +} + +std::vector f7() { + int i7 = 1; + return std::vector(i7); + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid repeating the return type +} + +Bar f8() { + return {}; +} + +Bar f9() { + return Bar(); + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid repeating the return type +} + +Bar f10() { + return Bar{}; +} + +Foo f11(Bar b11) { + return Foo(b11); + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid repeating the return type + // CHECK-FIXES: return {b11}; +} + +Foo f12() { + return f11(Bar()); +} + +Foo f13() { + return Foo(Bar()); // 13 + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid repeating the return type + // CHECK-FIXES: return {Bar()}; // 13 +} + +Foo f14() { + // FIXME: Type narrowing should not occur! + return Foo(-1); + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid repeating the return type + // CHECK-FIXES: return {-1}; +} + +Foo f15() { + return Foo(f10()); + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid repeating the return type + // CHECK-FIXES: return {f10()}; +} + +Quux f16() { + return Quux(1, 2); + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid repeating the return type + // CHECK-FIXES: return {1, 2}; +} + +Quux f17() { + return Quux(1, 2, 3); + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid repeating the return type + // CHECK-FIXES: return {1, 2, 3}; +} + +template +T f19() { + return T(); +} + +Bar i1 = f19(); +Baz i2 = f19(); + +template +Foo f20(T t) { + return Foo(t); +} + +Foo i3 = f20(b0); + +template +class BazT { +public: + T m() { + Bar b; + return T(b); + } + + Foo m2(T t) { + return Foo(t); + } +}; + +BazT bazFoo; +Foo i4 = bazFoo.m(); +Foo i5 = bazFoo.m2(b0); + +BazT bazQuux; +Foo i6 = bazQuux.m(); +Foo i7 = bazQuux.m2(b0); + +auto v1 = []() { return std::vector({1, 2}); }(); +auto v2 = []() -> std::vector { return std::vector({1, 2}); }();