1 //===- RedundantStringInitCheck.cpp - clang-tidy ----------------*- C++ -*-===//
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 "RedundantStringInitCheck.h"
10 #include "../utils/Matchers.h"
11 #include "../utils/OptionsUtils.h"
12 #include "clang/ASTMatchers/ASTMatchers.h"
13
14 using namespace clang::ast_matchers;
15 using namespace clang::tidy::matchers;
16
17 namespace clang {
18 namespace tidy {
19 namespace readability {
20
21 const char DefaultStringNames[] = "::std::basic_string";
22
RedundantStringInitCheck(StringRef Name,ClangTidyContext * Context)23 RedundantStringInitCheck::RedundantStringInitCheck(StringRef Name,
24 ClangTidyContext *Context)
25 : ClangTidyCheck(Name, Context),
26 StringNames(utils::options::parseStringList(
27 Options.get("StringNames", DefaultStringNames))) {}
28
storeOptions(ClangTidyOptions::OptionMap & Opts)29 void RedundantStringInitCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
30 Options.store(Opts, "StringNames", DefaultStringNames);
31 }
32
registerMatchers(MatchFinder * Finder)33 void RedundantStringInitCheck::registerMatchers(MatchFinder *Finder) {
34 if (!getLangOpts().CPlusPlus)
35 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()));
48
49 // Match string constructor.
50 const auto StringConstructorExpr = expr(
51 anyOf(cxxConstructExpr(argumentCountIs(1),
52 hasDeclaration(cxxMethodDecl(hasStringCtorName))),
53 // If present, the second argument is the alloc object which must
54 // not be present explicitly.
55 cxxConstructExpr(argumentCountIs(2),
56 hasDeclaration(cxxMethodDecl(hasStringCtorName)),
57 hasArgument(1, cxxDefaultArgExpr()))));
58
59 // Match a string constructor expression with an empty string literal.
60 const auto EmptyStringCtorExpr = cxxConstructExpr(
61 StringConstructorExpr,
62 hasArgument(0, ignoringParenImpCasts(stringLiteral(hasSize(0)))));
63
64 const auto EmptyStringCtorExprWithTemporaries =
65 cxxConstructExpr(StringConstructorExpr,
66 hasArgument(0, ignoringImplicit(EmptyStringCtorExpr)));
67
68 // Match a variable declaration with an empty string literal as initializer.
69 // Examples:
70 // string foo = "";
71 // string bar("");
72 Finder->addMatcher(
73 namedDecl(
74 varDecl(
75 hasType(hasUnqualifiedDesugaredType(recordType(
76 hasDeclaration(cxxRecordDecl(hasStringTypeName))))),
77 hasInitializer(expr(ignoringImplicit(anyOf(
78 EmptyStringCtorExpr, EmptyStringCtorExprWithTemporaries)))))
79 .bind("vardecl"),
80 unless(parmVarDecl())),
81 this);
82 }
83
check(const MatchFinder::MatchResult & Result)84 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());
91 }
92
93 } // namespace readability
94 } // namespace tidy
95 } // namespace clang
96