1 //===--- MiscStringCompare.cpp - clang-tidy--------------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "StringCompareCheck.h" 11 #include "../utils/FixItHintUtils.h" 12 #include "clang/AST/ASTContext.h" 13 #include "clang/ASTMatchers/ASTMatchFinder.h" 14 #include "clang/Tooling/FixIt.h" 15 16 using namespace clang::ast_matchers; 17 18 namespace clang { 19 namespace tidy { 20 namespace readability { 21 22 static const StringRef CompareMessage = "do not use 'compare' to test equality " 23 "of strings; use the string equality " 24 "operator instead"; 25 26 void StringCompareCheck::registerMatchers(MatchFinder *Finder) { 27 if (!getLangOpts().CPlusPlus) 28 return; 29 30 const auto StrCompare = cxxMemberCallExpr( 31 callee(cxxMethodDecl(hasName("compare"), 32 ofClass(classTemplateSpecializationDecl( 33 hasName("::std::basic_string"))))), 34 hasArgument(0, expr().bind("str2")), argumentCountIs(1), 35 callee(memberExpr().bind("str1"))); 36 37 // First and second case: cast str.compare(str) to boolean. 38 Finder->addMatcher(implicitCastExpr(hasImplicitDestinationType(booleanType()), 39 has(StrCompare)) 40 .bind("match1"), 41 this); 42 43 // Third and fourth case: str.compare(str) == 0 and str.compare(str) != 0. 44 Finder->addMatcher( 45 binaryOperator(anyOf(hasOperatorName("=="), hasOperatorName("!=")), 46 hasEitherOperand(StrCompare.bind("compare")), 47 hasEitherOperand(integerLiteral(equals(0)).bind("zero"))) 48 .bind("match2"), 49 this); 50 } 51 52 void StringCompareCheck::check(const MatchFinder::MatchResult &Result) { 53 if (const auto *Matched = Result.Nodes.getNodeAs<Stmt>("match1")) { 54 diag(Matched->getLocStart(), CompareMessage); 55 return; 56 } 57 58 if (const auto *Matched = Result.Nodes.getNodeAs<Stmt>("match2")) { 59 const ASTContext &Ctx = *Result.Context; 60 61 if (const auto *Zero = Result.Nodes.getNodeAs<Stmt>("zero")) { 62 const auto *Str1 = Result.Nodes.getNodeAs<MemberExpr>("str1"); 63 const auto *Str2 = Result.Nodes.getNodeAs<Stmt>("str2"); 64 const auto *Compare = Result.Nodes.getNodeAs<Stmt>("compare"); 65 66 auto Diag = diag(Matched->getLocStart(), CompareMessage); 67 68 if (Str1->isArrow()) 69 Diag << FixItHint::CreateInsertion(Str1->getLocStart(), "*"); 70 71 Diag << tooling::fixit::createReplacement(*Zero, *Str2, Ctx) 72 << tooling::fixit::createReplacement(*Compare, *Str1->getBase(), 73 Ctx); 74 } 75 } 76 77 // FIXME: Add fixit to fix the code for case one and two (match1). 78 } 79 80 } // namespace readability 81 } // namespace tidy 82 } // namespace clang 83