clang-tools 22.0.0git
UnconventionalAssignOperatorCheck.cpp
Go to the documentation of this file.
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-exception
6//
7//===----------------------------------------------------------------------===//
8
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 unsigned N = Node.isExplicitObjectMemberFunction() ? 1 : 0;
22 return (N < Node.parameters().size() &&
23 InnerMatcher.matches(*Node.parameters()[N], Finder, Builder));
24}
25} // namespace
26
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
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::misc
void registerMatchers(ast_matchers::MatchFinder *Finder) override
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
AST_MATCHER_P(Stmt, isStatementIdenticalToBoundNode, std::string, ID)