@@ -20,6 +20,46 @@ namespace readability {
20
20
21
21
const char DefaultStringNames[] = " ::std::basic_string" ;
22
22
23
+ static ast_matchers::internal::Matcher<NamedDecl>
24
+ hasAnyNameStdString (std::vector<std::string> Names) {
25
+ return ast_matchers::internal::Matcher<NamedDecl>(
26
+ new ast_matchers::internal::HasNameMatcher (std::move (Names)));
27
+ }
28
+
29
+ static std::vector<std::string>
30
+ removeNamespaces (const std::vector<std::string> &Names) {
31
+ std::vector<std::string> Result;
32
+ Result.reserve (Names.size ());
33
+ for (const std::string &Name : Names) {
34
+ std::string::size_type ColonPos = Name.rfind (' :' );
35
+ Result.push_back (
36
+ Name.substr (ColonPos == std::string::npos ? 0 : ColonPos + 1 ));
37
+ }
38
+ return Result;
39
+ }
40
+
41
+ static const CXXConstructExpr *
42
+ getConstructExpr (const CXXCtorInitializer &CtorInit) {
43
+ const Expr *InitExpr = CtorInit.getInit ();
44
+ if (const auto *CleanUpExpr = dyn_cast<ExprWithCleanups>(InitExpr))
45
+ InitExpr = CleanUpExpr->getSubExpr ();
46
+ return dyn_cast<CXXConstructExpr>(InitExpr);
47
+ }
48
+
49
+ static llvm::Optional<SourceRange>
50
+ getConstructExprArgRange (const CXXConstructExpr &Construct) {
51
+ SourceLocation B, E;
52
+ for (const Expr *Arg : Construct.arguments ()) {
53
+ if (B.isInvalid ())
54
+ B = Arg->getBeginLoc ();
55
+ if (Arg->getEndLoc ().isValid ())
56
+ E = Arg->getEndLoc ();
57
+ }
58
+ if (B.isInvalid () || E.isInvalid ())
59
+ return llvm::None;
60
+ return SourceRange (B, E);
61
+ }
62
+
23
63
RedundantStringInitCheck::RedundantStringInitCheck (StringRef Name,
24
64
ClangTidyContext *Context)
25
65
: ClangTidyCheck(Name, Context),
@@ -33,18 +73,9 @@ void RedundantStringInitCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
33
73
void RedundantStringInitCheck::registerMatchers (MatchFinder *Finder) {
34
74
if (!getLangOpts ().CPlusPlus )
35
75
return ;
36
- const auto hasStringTypeName = hasAnyName (
37
- SmallVector<StringRef, 3 >(StringNames.begin (), StringNames.end ()));
38
-
39
- // Version of StringNames with namespaces removed
40
- std::vector<std::string> stringNamesNoNamespace;
41
- for (const std::string &name : StringNames) {
42
- std::string::size_type colonPos = name.rfind (' :' );
43
- stringNamesNoNamespace.push_back (
44
- name.substr (colonPos == std::string::npos ? 0 : colonPos + 1 ));
45
- }
46
- const auto hasStringCtorName = hasAnyName (SmallVector<StringRef, 3 >(
47
- stringNamesNoNamespace.begin (), stringNamesNoNamespace.end ()));
76
+ const auto hasStringTypeName = hasAnyNameStdString (StringNames);
77
+ const auto hasStringCtorName =
78
+ hasAnyNameStdString (removeNamespaces (StringNames));
48
79
49
80
// Match string constructor.
50
81
const auto StringConstructorExpr = expr (
@@ -65,29 +96,76 @@ void RedundantStringInitCheck::registerMatchers(MatchFinder *Finder) {
65
96
cxxConstructExpr (StringConstructorExpr,
66
97
hasArgument (0 , ignoringImplicit (EmptyStringCtorExpr)));
67
98
99
+ const auto StringType = hasType (hasUnqualifiedDesugaredType (
100
+ recordType (hasDeclaration (cxxRecordDecl (hasStringTypeName)))));
101
+ const auto EmptyStringInit = expr (ignoringImplicit (
102
+ anyOf (EmptyStringCtorExpr, EmptyStringCtorExprWithTemporaries)));
103
+
68
104
// Match a variable declaration with an empty string literal as initializer.
69
105
// Examples:
70
106
// string foo = "";
71
107
// string bar("");
72
108
Finder->addMatcher (
73
109
namedDecl (
74
- varDecl (
75
- hasType (hasUnqualifiedDesugaredType (recordType (
76
- hasDeclaration (cxxRecordDecl (hasStringTypeName))))),
77
- hasInitializer (expr (ignoringImplicit (anyOf (
78
- EmptyStringCtorExpr, EmptyStringCtorExprWithTemporaries)))))
79
- .bind (" vardecl" ),
110
+ varDecl (StringType, hasInitializer (EmptyStringInit)).bind (" vardecl" ),
80
111
unless (parmVarDecl ())),
81
112
this );
113
+ // Match a field declaration with an empty string literal as initializer.
114
+ Finder->addMatcher (
115
+ namedDecl (fieldDecl (StringType, hasInClassInitializer (EmptyStringInit))
116
+ .bind (" fieldDecl" )),
117
+ this );
118
+ // Matches Constructor Initializers with an empty string literal as
119
+ // initializer.
120
+ // Examples:
121
+ // Foo() : SomeString("") {}
122
+ Finder->addMatcher (
123
+ cxxCtorInitializer (
124
+ isWritten (),
125
+ forField (allOf (StringType, optionally (hasInClassInitializer (
126
+ EmptyStringInit.bind (" empty_init" ))))),
127
+ withInitializer (EmptyStringInit))
128
+ .bind (" ctorInit" ),
129
+ this );
82
130
}
83
131
84
132
void RedundantStringInitCheck::check (const MatchFinder::MatchResult &Result) {
85
- const auto *VDecl = Result.Nodes .getNodeAs <VarDecl>(" vardecl" );
86
- // VarDecl's getSourceRange() spans 'string foo = ""' or 'string bar("")'.
87
- // So start at getLocation() to span just 'foo = ""' or 'bar("")'.
88
- SourceRange ReplaceRange (VDecl->getLocation (), VDecl->getEndLoc ());
89
- diag (VDecl->getLocation (), " redundant string initialization" )
90
- << FixItHint::CreateReplacement (ReplaceRange, VDecl->getName ());
133
+ if (const auto *VDecl = Result.Nodes .getNodeAs <VarDecl>(" vardecl" )) {
134
+ // VarDecl's getSourceRange() spans 'string foo = ""' or 'string bar("")'.
135
+ // So start at getLocation() to span just 'foo = ""' or 'bar("")'.
136
+ SourceRange ReplaceRange (VDecl->getLocation (), VDecl->getEndLoc ());
137
+ diag (VDecl->getLocation (), " redundant string initialization" )
138
+ << FixItHint::CreateReplacement (ReplaceRange, VDecl->getName ());
139
+ }
140
+ if (const auto *FDecl = Result.Nodes .getNodeAs <FieldDecl>(" fieldDecl" )) {
141
+ // FieldDecl's getSourceRange() spans 'string foo = ""'.
142
+ // So start at getLocation() to span just 'foo = ""'.
143
+ SourceRange ReplaceRange (FDecl->getLocation (), FDecl->getEndLoc ());
144
+ diag (FDecl->getLocation (), " redundant string initialization" )
145
+ << FixItHint::CreateReplacement (ReplaceRange, FDecl->getName ());
146
+ }
147
+ if (const auto *CtorInit =
148
+ Result.Nodes .getNodeAs <CXXCtorInitializer>(" ctorInit" )) {
149
+ if (const FieldDecl *Member = CtorInit->getMember ()) {
150
+ if (!Member->hasInClassInitializer () ||
151
+ Result.Nodes .getNodeAs <Expr>(" empty_init" )) {
152
+ // The String isn't declared in the class with an initializer or its
153
+ // declared with a redundant initializer, which will be removed. Either
154
+ // way the string will be default initialized, therefore we can remove
155
+ // the constructor initializer entirely.
156
+ diag (CtorInit->getMemberLocation (), " redundant string initialization" )
157
+ << FixItHint::CreateRemoval (CtorInit->getSourceRange ());
158
+ return ;
159
+ }
160
+ }
161
+ const CXXConstructExpr *Construct = getConstructExpr (*CtorInit);
162
+ if (!Construct)
163
+ return ;
164
+ if (llvm::Optional<SourceRange> RemovalRange =
165
+ getConstructExprArgRange (*Construct))
166
+ diag (CtorInit->getMemberLocation (), " redundant string initialization" )
167
+ << FixItHint::CreateRemoval (*RemovalRange);
168
+ }
91
169
}
92
170
93
171
} // namespace readability
0 commit comments