brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.9 KiB · b696089 Raw
133 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 "UnhandledSelfAssignmentCheck.h"10#include "clang/AST/ASTContext.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12 13using namespace clang::ast_matchers;14 15namespace clang::tidy::bugprone {16 17UnhandledSelfAssignmentCheck::UnhandledSelfAssignmentCheck(18    StringRef Name, ClangTidyContext *Context)19    : ClangTidyCheck(Name, Context),20      WarnOnlyIfThisHasSuspiciousField(21          Options.get("WarnOnlyIfThisHasSuspiciousField", true)) {}22 23void UnhandledSelfAssignmentCheck::storeOptions(24    ClangTidyOptions::OptionMap &Opts) {25  Options.store(Opts, "WarnOnlyIfThisHasSuspiciousField",26                WarnOnlyIfThisHasSuspiciousField);27}28 29void UnhandledSelfAssignmentCheck::registerMatchers(MatchFinder *Finder) {30  // We don't care about deleted, default or implicit operator implementations.31  const auto IsUserDefined = cxxMethodDecl(32      isDefinition(), unless(anyOf(isDeleted(), isImplicit(), isDefaulted())));33 34  // We don't need to worry when a copy assignment operator gets the other35  // object by value.36  const auto HasReferenceParam =37      cxxMethodDecl(hasParameter(0, parmVarDecl(hasType(referenceType()))));38 39  // Self-check: Code compares something with 'this' pointer. We don't check40  // whether it is actually the parameter what we compare.41  const auto HasNoSelfCheck = cxxMethodDecl(unless(hasDescendant(42      binaryOperation(hasAnyOperatorName("==", "!="),43                      hasEitherOperand(ignoringParenCasts(cxxThisExpr()))))));44 45  // Both copy-and-swap and copy-and-move method creates a copy first and46  // assign it to 'this' with swap or move.47  // In the non-template case, we can search for the copy constructor call.48  const auto HasNonTemplateSelfCopy = cxxMethodDecl(49      ofClass(cxxRecordDecl(unless(hasAncestor(classTemplateDecl())))),50      traverse(TK_AsIs,51               hasDescendant(cxxConstructExpr(hasDeclaration(cxxConstructorDecl(52                   isCopyConstructor(), ofClass(equalsBoundNode("class"))))))));53 54  // In the template case, we need to handle two separate cases: 1) a local55  // variable is created with the copy, 2) copy is created only as a temporary56  // object.57  const auto HasTemplateSelfCopy = cxxMethodDecl(58      ofClass(cxxRecordDecl(hasAncestor(classTemplateDecl()))),59      anyOf(hasDescendant(60                varDecl(hasType(cxxRecordDecl(equalsBoundNode("class"))),61                        hasDescendant(parenListExpr()))),62            hasDescendant(cxxUnresolvedConstructExpr(hasDescendant(declRefExpr(63                hasType(cxxRecordDecl(equalsBoundNode("class")))))))));64 65  // If inside the copy assignment operator another assignment operator is66  // called on 'this' we assume that self-check might be handled inside67  // this nested operator.68  const auto HasNoNestedSelfAssign =69      cxxMethodDecl(unless(hasDescendant(cxxMemberCallExpr(callee(cxxMethodDecl(70          hasName("operator="), ofClass(equalsBoundNode("class"))))))));71 72  // Checking that some kind of constructor is called and followed by a `swap`:73  // T& operator=(const T& other) {74  //    T tmp{this->internal_data(), some, other, args};75  //    swap(tmp);76  //    return *this;77  // }78  const auto HasCopyAndSwap = cxxMethodDecl(79      ofClass(cxxRecordDecl()),80      hasBody(compoundStmt(81          hasDescendant(82              varDecl(hasType(cxxRecordDecl(equalsBoundNode("class"))))83                  .bind("tmp_var")),84          hasDescendant(stmt(anyOf(85              cxxMemberCallExpr(hasArgument(86                  0, declRefExpr(to(varDecl(equalsBoundNode("tmp_var")))))),87              callExpr(88                  callee(functionDecl(hasName("swap"))), argumentCountIs(2),89                  hasAnyArgument(90                      declRefExpr(to(varDecl(equalsBoundNode("tmp_var"))))),91                  hasAnyArgument(unaryOperator(has(cxxThisExpr()),92                                               hasOperatorName("*"))))))))));93 94  DeclarationMatcher AdditionalMatcher = cxxMethodDecl();95  if (WarnOnlyIfThisHasSuspiciousField) {96    // Matcher for standard smart pointers.97    const auto SmartPointerType = qualType(hasUnqualifiedDesugaredType(98        recordType(hasDeclaration(classTemplateSpecializationDecl(99            anyOf(allOf(hasAnyName("::std::shared_ptr", "::std::weak_ptr",100                                   "::std::auto_ptr"),101                        templateArgumentCountIs(1)),102                  allOf(hasName("::std::unique_ptr"),103                        templateArgumentCountIs(2))))))));104 105    // We will warn only if the class has a pointer or a C array field which106    // probably causes a problem during self-assignment (e.g. first resetting107    // the pointer member, then trying to access the object pointed by the108    // pointer, or memcpy overlapping arrays).109    AdditionalMatcher = cxxMethodDecl(ofClass(cxxRecordDecl(110        has(fieldDecl(anyOf(hasType(pointerType()), hasType(SmartPointerType),111                            hasType(arrayType())))))));112  }113 114  Finder->addMatcher(115      cxxMethodDecl(116          ofClass(cxxRecordDecl().bind("class")), isCopyAssignmentOperator(),117          IsUserDefined, HasReferenceParam, HasNoSelfCheck,118          unless(HasNonTemplateSelfCopy), unless(HasTemplateSelfCopy),119          unless(HasCopyAndSwap), HasNoNestedSelfAssign, AdditionalMatcher)120          .bind("copyAssignmentOperator"),121      this);122}123 124void UnhandledSelfAssignmentCheck::check(125    const MatchFinder::MatchResult &Result) {126  const auto *MatchedDecl =127      Result.Nodes.getNodeAs<CXXMethodDecl>("copyAssignmentOperator");128  diag(MatchedDecl->getLocation(),129       "operator=() does not handle self-assignment properly");130}131 132} // namespace clang::tidy::bugprone133