1 | //===--- ReturnBracedInitListCheck.cpp - clang-tidy------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "ReturnBracedInitListCheck.h" |
10 | #include "clang/AST/ASTContext.h" |
11 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
12 | #include "clang/ASTMatchers/ASTMatchers.h" |
13 | #include "clang/Lex/Lexer.h" |
14 | #include "clang/Tooling/FixIt.h" |
15 | |
16 | using namespace clang::ast_matchers; |
17 | |
18 | namespace clang::tidy::modernize { |
19 | |
20 | void ReturnBracedInitListCheck::registerMatchers(MatchFinder *Finder) { |
21 | auto SemanticallyDifferentContainer = allOf( |
22 | hasDeclaration( |
23 | // Container(size_type count, const T &value, |
24 | // const Allocator &alloc = Allocator()); |
25 | InnerMatcher: cxxConstructorDecl(parameterCountIs(N: 3), |
26 | hasParameter(N: 0, InnerMatcher: hasType(InnerMatcher: qualType(hasCanonicalType( |
27 | InnerMatcher: isInteger())))))), |
28 | hasType(InnerMatcher: cxxRecordDecl(hasAnyName("::std::basic_string" , "::std::vector" , |
29 | "::std::deque" , "::std::forward_list" , |
30 | "::std::list" )))); |
31 | |
32 | auto ConstructExpr = |
33 | cxxConstructExpr( |
34 | unless(anyOf( |
35 | // Skip explicit constructor. |
36 | hasDeclaration(InnerMatcher: cxxConstructorDecl(isExplicit())), |
37 | // Skip list initialization and constructors with an initializer |
38 | // list. |
39 | isListInitialization(), hasDescendant(initListExpr()), |
40 | // Skip container `vector(size_type, const T&, ...)`. |
41 | SemanticallyDifferentContainer))) |
42 | .bind(ID: "ctor" ); |
43 | |
44 | Finder->addMatcher( |
45 | NodeMatch: returnStmt(hasReturnValue(InnerMatcher: ConstructExpr), |
46 | forFunction(InnerMatcher: functionDecl(returns(InnerMatcher: unless(anyOf(builtinType(), |
47 | autoType())))) |
48 | .bind(ID: "fn" ))), |
49 | Action: this); |
50 | } |
51 | |
52 | void ReturnBracedInitListCheck::check(const MatchFinder::MatchResult &Result) { |
53 | const auto *MatchedFunctionDecl = Result.Nodes.getNodeAs<FunctionDecl>(ID: "fn" ); |
54 | const auto *MatchedConstructExpr = |
55 | Result.Nodes.getNodeAs<CXXConstructExpr>(ID: "ctor" ); |
56 | |
57 | // Don't make replacements in macro. |
58 | SourceLocation Loc = MatchedConstructExpr->getExprLoc(); |
59 | if (Loc.isMacroID()) |
60 | return; |
61 | |
62 | // Make sure that the return type matches the constructed type. |
63 | const QualType ReturnType = |
64 | MatchedFunctionDecl->getReturnType().getCanonicalType(); |
65 | const QualType ConstructType = |
66 | MatchedConstructExpr->getType().getCanonicalType(); |
67 | if (ReturnType != ConstructType) |
68 | return; |
69 | |
70 | auto Diag = diag(Loc, Description: "avoid repeating the return type from the " |
71 | "declaration; use a braced initializer list instead" ); |
72 | |
73 | const SourceRange CallParensRange = |
74 | MatchedConstructExpr->getParenOrBraceRange(); |
75 | |
76 | // Make sure there is an explicit constructor call. |
77 | if (CallParensRange.isInvalid()) |
78 | return; |
79 | |
80 | // Make sure that the ctor arguments match the declaration. |
81 | for (unsigned I = 0, NumParams = MatchedConstructExpr->getNumArgs(); |
82 | I < NumParams; ++I) { |
83 | if (const auto *VD = dyn_cast<VarDecl>( |
84 | MatchedConstructExpr->getConstructor()->getParamDecl(I))) { |
85 | if (MatchedConstructExpr->getArg(Arg: I)->getType().getCanonicalType() != |
86 | VD->getType().getCanonicalType()) |
87 | return; |
88 | } |
89 | } |
90 | |
91 | // Range for constructor name and opening brace. |
92 | CharSourceRange CtorCallSourceRange = CharSourceRange::getTokenRange( |
93 | B: Loc, E: CallParensRange.getBegin().getLocWithOffset(Offset: -1)); |
94 | |
95 | Diag << FixItHint::CreateRemoval(RemoveRange: CtorCallSourceRange) |
96 | << FixItHint::CreateReplacement(RemoveRange: CallParensRange.getBegin(), Code: "{" ) |
97 | << FixItHint::CreateReplacement(RemoveRange: CallParensRange.getEnd(), Code: "}" ); |
98 | } |
99 | |
100 | } // namespace clang::tidy::modernize |
101 | |