1 //===--- UnconventionalAssignOperatorCheck.cpp - clang-tidy -----*- C++ -*-===// 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 "UnconventionalAssignOperatorCheck.h" 11 #include "clang/ASTMatchers/ASTMatchFinder.h" 12 #include "clang/ASTMatchers/ASTMatchers.h" 13 14 using namespace clang::ast_matchers; 15 16 namespace clang { 17 namespace tidy { 18 namespace misc { 19 20 void UnconventionalAssignOperatorCheck::registerMatchers( 21 ast_matchers::MatchFinder *Finder) { 22 // Only register the matchers for C++; the functionality currently does not 23 // provide any benefit to other languages, despite being benign. 24 if (!getLangOpts().CPlusPlus) 25 return; 26 27 const auto HasGoodReturnType = cxxMethodDecl(returns( 28 lValueReferenceType(pointee(unless(isConstQualified()), 29 hasDeclaration(equalsBoundNode("class")))))); 30 31 const auto IsSelf = qualType( 32 anyOf(hasDeclaration(equalsBoundNode("class")), 33 referenceType(pointee(hasDeclaration(equalsBoundNode("class")))))); 34 const auto IsAssign = 35 cxxMethodDecl(unless(anyOf(isDeleted(), isPrivate(), isImplicit())), 36 hasName("operator="), ofClass(recordDecl().bind("class"))) 37 .bind("method"); 38 const auto IsSelfAssign = 39 cxxMethodDecl(IsAssign, hasParameter(0, parmVarDecl(hasType(IsSelf)))) 40 .bind("method"); 41 42 Finder->addMatcher( 43 cxxMethodDecl(IsAssign, unless(HasGoodReturnType)).bind("ReturnType"), 44 this); 45 46 const auto BadSelf = referenceType( 47 anyOf(lValueReferenceType(pointee(unless(isConstQualified()))), 48 rValueReferenceType(pointee(isConstQualified())))); 49 50 Finder->addMatcher( 51 cxxMethodDecl(IsSelfAssign, 52 hasParameter(0, parmVarDecl(hasType(BadSelf)))) 53 .bind("ArgumentType"), 54 this); 55 56 Finder->addMatcher( 57 cxxMethodDecl(IsSelfAssign, anyOf(isConst(), isVirtual())).bind("cv"), 58 this); 59 60 const auto IsBadReturnStatement = returnStmt(unless(has(ignoringParenImpCasts( 61 anyOf(unaryOperator(hasOperatorName("*"), hasUnaryOperand(cxxThisExpr())), 62 cxxOperatorCallExpr(argumentCountIs(1), 63 callee(unresolvedLookupExpr()), 64 hasArgument(0, cxxThisExpr()))))))); 65 const auto IsGoodAssign = cxxMethodDecl(IsAssign, HasGoodReturnType); 66 67 Finder->addMatcher(returnStmt(IsBadReturnStatement, forFunction(IsGoodAssign)) 68 .bind("returnStmt"), 69 this); 70 } 71 72 void UnconventionalAssignOperatorCheck::check( 73 const MatchFinder::MatchResult &Result) { 74 if (const auto *RetStmt = Result.Nodes.getNodeAs<ReturnStmt>("returnStmt")) { 75 diag(RetStmt->getLocStart(), "operator=() should always return '*this'"); 76 } else { 77 static const char *const Messages[][2] = { 78 {"ReturnType", "operator=() should return '%0&'"}, 79 {"ArgumentType", "operator=() should take '%0 const&', '%0&&' or '%0'"}, 80 {"cv", "operator=() should not be marked '%1'"}}; 81 82 const auto *Method = Result.Nodes.getNodeAs<CXXMethodDecl>("method"); 83 for (const auto &Message : Messages) { 84 if (Result.Nodes.getNodeAs<Decl>(Message[0])) 85 diag(Method->getLocStart(), Message[1]) 86 << Method->getParent()->getName() 87 << (Method->isConst() ? "const" : "virtual"); 88 } 89 } 90 } 91 92 } // namespace misc 93 } // namespace tidy 94 } // namespace clang 95