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