[clang-tidy] Add check 'modernize-return-braced-init-list'

Summary:
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
return type and the return statement.

```
Foo bar() {
  Baz baz;
  return Foo(baz);
}

// transforms to:

Foo bar() {
  Baz baz;
  return {baz};
}
```

Reviewers: hokein, Prazek, aaron.ballman, alexfh

Reviewed By: Prazek, aaron.ballman, alexfh

Subscribers: malcolm.parsons, mgorny, cfe-commits

Tags: #clang-tools-extra

Differential Revision: https://reviews.llvm.org/D28768

llvm-svn: 295199
This commit is contained in:
Jonas Devlieghere 2017-02-15 17:06:06 +00:00
parent 0ac6d124cf
commit 2789043178
8 changed files with 365 additions and 0 deletions

View File

@ -13,6 +13,7 @@ add_clang_library(clangTidyModernizeModule
RawStringLiteralCheck.cpp
RedundantVoidArgCheck.cpp
ReplaceAutoPtrCheck.cpp
ReturnBracedInitListCheck.cpp
ShrinkToFitCheck.cpp
UseAutoCheck.cpp
UseBoolLiteralsCheck.cpp

View File

@ -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 @@ public:
"modernize-redundant-void-arg");
CheckFactories.registerCheck<ReplaceAutoPtrCheck>(
"modernize-replace-auto-ptr");
CheckFactories.registerCheck<ReturnBracedInitListCheck>(
"modernize-return-braced-init-list");
CheckFactories.registerCheck<ShrinkToFitCheck>("modernize-shrink-to-fit");
CheckFactories.registerCheck<UseAutoCheck>("modernize-use-auto");
CheckFactories.registerCheck<UseBoolLiteralsCheck>(

View File

@ -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<FunctionDecl>("fn");
const auto *MatchedConstructExpr =
Result.Nodes.getNodeAs<CXXConstructExpr>("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<VarDecl>(
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

View File

@ -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

View File

@ -67,6 +67,12 @@ Improvements to clang-tidy
Finds uses of inline assembler.
- New `modernize-return-braced-init-list
<http://clang.llvm.org/extra/clang-tidy/checks/modernize-return-braced-init-list.html>`_ 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
-----------------------------

View File

@ -109,6 +109,7 @@ Clang-Tidy Checks
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

View File

@ -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};
}

View File

@ -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 _E>
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 <typename T>
class vector {
public:
vector(T) {}
vector(std::initializer_list<T>) {}
};
}
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<int> f7() {
int i7 = 1;
return std::vector<int>(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 <typename T>
T f19() {
return T();
}
Bar i1 = f19<Bar>();
Baz i2 = f19<Baz>();
template <typename T>
Foo f20(T t) {
return Foo(t);
}
Foo i3 = f20(b0);
template <typename T>
class BazT {
public:
T m() {
Bar b;
return T(b);
}
Foo m2(T t) {
return Foo(t);
}
};
BazT<Foo> bazFoo;
Foo i4 = bazFoo.m();
Foo i5 = bazFoo.m2(b0);
BazT<Quux> bazQuux;
Foo i6 = bazQuux.m();
Foo i7 = bazQuux.m2(b0);
auto v1 = []() { return std::vector<int>({1, 2}); }();
auto v2 = []() -> std::vector<int> { return std::vector<int>({1, 2}); }();