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