1 | //===--- StringFindStrContainsCheck.cc - 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 "StringFindStrContainsCheck.h" |
10 | |
11 | #include "../utils/OptionsUtils.h" |
12 | #include "clang/AST/ASTContext.h" |
13 | #include "clang/ASTMatchers/ASTMatchers.h" |
14 | #include "clang/Frontend/CompilerInstance.h" |
15 | #include "clang/Tooling/Transformer/RewriteRule.h" |
16 | #include "clang/Tooling/Transformer/Stencil.h" |
17 | |
18 | // FixItHint - Hint to check documentation script to mark this check as |
19 | // providing a FixIt. |
20 | |
21 | using namespace clang::ast_matchers; |
22 | |
23 | namespace clang::tidy::abseil { |
24 | |
25 | using ::clang::transformer::addInclude; |
26 | using ::clang::transformer::applyFirst; |
27 | using ::clang::transformer::cat; |
28 | using ::clang::transformer::changeTo; |
29 | using ::clang::transformer::makeRule; |
30 | using ::clang::transformer::node; |
31 | using ::clang::transformer::RewriteRuleWith; |
32 | |
33 | AST_MATCHER(Type, isCharType) { return Node.isCharType(); } |
34 | |
35 | static const char DefaultStringLikeClasses[] = "::std::basic_string;" |
36 | "::std::basic_string_view;" |
37 | "::absl::string_view" ; |
38 | static const char [] = "absl/strings/match.h" ; |
39 | |
40 | static transformer::RewriteRuleWith<std::string> |
41 | makeRewriteRule(ArrayRef<StringRef> StringLikeClassNames, |
42 | StringRef ) { |
43 | auto StringLikeClass = cxxRecordDecl(hasAnyName(StringLikeClassNames)); |
44 | auto StringType = |
45 | hasUnqualifiedDesugaredType(InnerMatcher: recordType(hasDeclaration(InnerMatcher: StringLikeClass))); |
46 | auto CharStarType = |
47 | hasUnqualifiedDesugaredType(InnerMatcher: pointerType(pointee(isAnyCharacter()))); |
48 | auto CharType = hasUnqualifiedDesugaredType(InnerMatcher: isCharType()); |
49 | auto StringNpos = declRefExpr( |
50 | to(InnerMatcher: varDecl(hasName(Name: "npos" ), hasDeclContext(InnerMatcher: StringLikeClass)))); |
51 | auto StringFind = cxxMemberCallExpr( |
52 | callee(InnerMatcher: cxxMethodDecl( |
53 | hasName(Name: "find" ), parameterCountIs(N: 2), |
54 | hasParameter( |
55 | N: 0, InnerMatcher: parmVarDecl(anyOf(hasType(InnerMatcher: StringType), hasType(InnerMatcher: CharStarType), |
56 | hasType(InnerMatcher: CharType)))))), |
57 | on(InnerMatcher: hasType(InnerMatcher: StringType)), hasArgument(N: 0, InnerMatcher: expr().bind(ID: "parameter_to_find" )), |
58 | anyOf(hasArgument(N: 1, InnerMatcher: integerLiteral(equals(Value: 0))), |
59 | hasArgument(N: 1, InnerMatcher: cxxDefaultArgExpr())), |
60 | onImplicitObjectArgument(InnerMatcher: expr().bind(ID: "string_being_searched" ))); |
61 | |
62 | RewriteRuleWith<std::string> Rule = applyFirst( |
63 | Rules: {makeRule( |
64 | M: binaryOperator(hasOperatorName(Name: "==" ), |
65 | hasOperands(Matcher1: ignoringParenImpCasts(InnerMatcher: StringNpos), |
66 | Matcher2: ignoringParenImpCasts(InnerMatcher: StringFind))), |
67 | Edits: {changeTo(Replacement: cat(Parts: "!absl::StrContains(" , Parts: node(ID: "string_being_searched" ), |
68 | Parts: ", " , Parts: node(ID: "parameter_to_find" ), Parts: ")" )), |
69 | addInclude(Header: AbseilStringsMatchHeader)}, |
70 | Metadata: cat(Parts: "use !absl::StrContains instead of find() == npos" )), |
71 | makeRule( |
72 | M: binaryOperator(hasOperatorName(Name: "!=" ), |
73 | hasOperands(Matcher1: ignoringParenImpCasts(InnerMatcher: StringNpos), |
74 | Matcher2: ignoringParenImpCasts(InnerMatcher: StringFind))), |
75 | Edits: {changeTo(Replacement: cat(Parts: "absl::StrContains(" , Parts: node(ID: "string_being_searched" ), |
76 | Parts: ", " , Parts: node(ID: "parameter_to_find" ), Parts: ")" )), |
77 | addInclude(Header: AbseilStringsMatchHeader)}, |
78 | Metadata: cat(Parts: "use absl::StrContains instead " |
79 | "of find() != npos" ))}); |
80 | return Rule; |
81 | } |
82 | |
83 | StringFindStrContainsCheck::StringFindStrContainsCheck( |
84 | StringRef Name, ClangTidyContext *Context) |
85 | : TransformerClangTidyCheck(Name, Context), |
86 | StringLikeClassesOption(utils::options::parseStringList( |
87 | Option: Options.get(LocalName: "StringLikeClasses" , Default: DefaultStringLikeClasses))), |
88 | AbseilStringsMatchHeaderOption(Options.get( |
89 | LocalName: "AbseilStringsMatchHeader" , Default: DefaultAbseilStringsMatchHeader)) { |
90 | setRule( |
91 | makeRewriteRule(StringLikeClassNames: StringLikeClassesOption, AbseilStringsMatchHeader: AbseilStringsMatchHeaderOption)); |
92 | } |
93 | |
94 | bool StringFindStrContainsCheck::isLanguageVersionSupported( |
95 | const LangOptions &LangOpts) const { |
96 | return LangOpts.CPlusPlus11; |
97 | } |
98 | |
99 | void StringFindStrContainsCheck::storeOptions( |
100 | ClangTidyOptions::OptionMap &Opts) { |
101 | TransformerClangTidyCheck::storeOptions(Opts); |
102 | Options.store(Options&: Opts, LocalName: "StringLikeClasses" , |
103 | Value: utils::options::serializeStringList(Strings: StringLikeClassesOption)); |
104 | Options.store(Options&: Opts, LocalName: "AbseilStringsMatchHeader" , |
105 | Value: AbseilStringsMatchHeaderOption); |
106 | } |
107 | |
108 | } // namespace clang::tidy::abseil |
109 | |