105 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "UnconventionalAssignOperatorCheck.h"10#include "clang/ASTMatchers/ASTMatchFinder.h"11#include "clang/ASTMatchers/ASTMatchers.h"12 13using namespace clang::ast_matchers;14 15namespace clang::tidy::misc {16 17namespace {18 19AST_MATCHER_P(CXXMethodDecl, firstParameter,20 ast_matchers::internal::Matcher<ParmVarDecl>, InnerMatcher) {21 const unsigned N = Node.isExplicitObjectMemberFunction() ? 1 : 0;22 return (N < Node.parameters().size() &&23 InnerMatcher.matches(*Node.parameters()[N], Finder, Builder));24}25} // namespace26 27void UnconventionalAssignOperatorCheck::registerMatchers(28 ast_matchers::MatchFinder *Finder) {29 const auto HasGoodReturnType =30 cxxMethodDecl(returns(hasCanonicalType(lValueReferenceType(pointee(31 unless(isConstQualified()),32 anyOf(autoType(),33 hasDeclaration(declaresSameEntityAsBoundNode("class"))))))));34 35 const auto IsSelf = qualType(hasCanonicalType(36 anyOf(hasDeclaration(declaresSameEntityAsBoundNode("class")),37 referenceType(pointee(38 hasDeclaration(declaresSameEntityAsBoundNode("class")))))));39 const auto IsAssign =40 cxxMethodDecl(unless(anyOf(isDeleted(), isPrivate(), isImplicit())),41 hasName("operator="), ofClass(recordDecl().bind("class")))42 .bind("method");43 const auto IsSelfAssign =44 cxxMethodDecl(IsAssign, firstParameter(parmVarDecl(hasType(IsSelf))))45 .bind("method");46 47 Finder->addMatcher(48 cxxMethodDecl(IsAssign, unless(HasGoodReturnType)).bind("ReturnType"),49 this);50 51 const auto BadSelf = qualType(hasCanonicalType(referenceType(52 anyOf(lValueReferenceType(pointee(unless(isConstQualified()))),53 rValueReferenceType(pointee(isConstQualified()))))));54 55 Finder->addMatcher(56 cxxMethodDecl(IsSelfAssign, firstParameter(parmVarDecl(hasType(BadSelf))))57 .bind("ArgumentType"),58 this);59 60 Finder->addMatcher(61 cxxMethodDecl(IsSelfAssign, anyOf(isConst(), isVirtual())).bind("cv"),62 this);63 64 const auto IsBadReturnStatement = returnStmt(unless(has(ignoringParenImpCasts(65 anyOf(unaryOperator(hasOperatorName("*"), hasUnaryOperand(cxxThisExpr())),66 cxxOperatorCallExpr(argumentCountIs(1),67 callee(unresolvedLookupExpr()),68 hasArgument(0, cxxThisExpr())),69 cxxOperatorCallExpr(70 hasOverloadedOperatorName("="),71 hasArgument(0, unaryOperator(hasOperatorName("*"),72 hasUnaryOperand(cxxThisExpr())))),73 binaryOperator(74 hasOperatorName("="),75 hasLHS(unaryOperator(hasOperatorName("*"),76 hasUnaryOperand(cxxThisExpr())))))))));77 const auto IsGoodAssign = cxxMethodDecl(IsAssign, HasGoodReturnType);78 79 Finder->addMatcher(returnStmt(IsBadReturnStatement, forFunction(IsGoodAssign))80 .bind("returnStmt"),81 this);82}83 84void UnconventionalAssignOperatorCheck::check(85 const MatchFinder::MatchResult &Result) {86 if (const auto *RetStmt = Result.Nodes.getNodeAs<ReturnStmt>("returnStmt")) {87 diag(RetStmt->getBeginLoc(), "operator=() should always return '*this'");88 } else {89 const auto *Method = Result.Nodes.getNodeAs<CXXMethodDecl>("method");90 if (Result.Nodes.getNodeAs<CXXMethodDecl>("ReturnType"))91 diag(Method->getBeginLoc(), "operator=() should return '%0&'")92 << Method->getParent()->getName();93 if (Result.Nodes.getNodeAs<CXXMethodDecl>("ArgumentType"))94 diag(Method->getBeginLoc(),95 "operator=() should take '%0 const&'%select{|, '%0&&'}1 or '%0'")96 << Method->getParent()->getName() << getLangOpts().CPlusPlus11;97 if (Result.Nodes.getNodeAs<CXXMethodDecl>("cv"))98 diag(Method->getBeginLoc(),99 "operator=() should not be marked '%select{const|virtual}0'")100 << !Method->isConst();101 }102}103 104} // namespace clang::tidy::misc105