Index: lib/StaticAnalyzer/Checkers/CMakeLists.txt =================================================================== --- lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -65,6 +65,7 @@ RetainCountChecker.cpp ReturnPointerRangeChecker.cpp ReturnUndefChecker.cpp + SelfAssignmentChecker.cpp SimpleStreamChecker.cpp StackAddrEscapeChecker.cpp StreamChecker.cpp Index: lib/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- lib/StaticAnalyzer/Checkers/Checkers.td +++ lib/StaticAnalyzer/Checkers/Checkers.td @@ -245,6 +245,10 @@ HelpText<"Check for memory leaks. Traces memory managed by new/delete.">, DescFile<"MallocChecker.cpp">; +def SelfAssignmentChecker : Checker<"SelfAssignment">, + HelpText<"Checks copy and move assignment operators for self assignment">, + DescFile<"SelfAssignmentChecker.cpp">; + } // end: "cplusplus" let ParentPackage = CplusplusAlpha in { Index: lib/StaticAnalyzer/Checkers/SelfAssignmentChecker.cpp =================================================================== --- /dev/null +++ lib/StaticAnalyzer/Checkers/SelfAssignmentChecker.cpp @@ -0,0 +1,58 @@ +//=== SelfAssignmentChecker.cpp --------------------------------*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines SelfAssignmentChecker, which checks all custom defined +// copy and move assignment operators for the case of self assignment, thus +// where the parameter refers to the same location where the this pointer +// points to. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { + +class SelfAssignmentChecker : public Checker { +public: + SelfAssignmentChecker(); + void checkBeginFunction(CheckerContext &C) const; +}; +} + +SelfAssignmentChecker::SelfAssignmentChecker() {} + +void SelfAssignmentChecker::checkBeginFunction(CheckerContext &C) const { + if (!C.inTopFrame()) + return; + const auto *LCtx = C.getLocationContext(); + const auto *MD = dyn_cast(LCtx->getDecl()); + if (!MD) + return; + if (!MD->isCopyAssignmentOperator() && !MD->isMoveAssignmentOperator()) + return; + auto &State = C.getState(); + auto &SVB = C.getSValBuilder(); + auto ThisVal = + State->getSVal(SVB.getCXXThis(MD, LCtx->getCurrentStackFrame())); + auto Param = SVB.makeLoc(State->getRegion(MD->getParamDecl(0), LCtx)); + auto ParamVal = State->getSVal(Param); + ProgramStateRef SelfAssignState = State->bindLoc(Param, ThisVal); + C.addTransition(SelfAssignState); + ProgramStateRef NonSelfAssignState = State->bindLoc(Param, ParamVal); + C.addTransition(NonSelfAssignState); +} + +void ento::registerSelfAssignmentChecker(CheckerManager &Mgr) { + Mgr.registerChecker(); +} Index: lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp =================================================================== --- lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -432,6 +432,10 @@ // Count naming convention errors more aggressively. if (isa(D)) return false; + if (const auto *MD = dyn_cast(D)) { + if(MD->isCopyAssignmentOperator()||MD->isMoveAssignmentOperator()) + return false; + } // Otherwise, if we visited the function before, do not reanalyze it. return Visited.count(D); @@ -443,9 +447,7 @@ // We want to reanalyze all ObjC methods as top level to report Retain // Count naming convention errors more aggressively. But we should tune down // inlining when reanalyzing an already inlined function. - if (Visited.count(D)) { - assert(isa(D) && - "We are only reanalyzing ObjCMethods."); + if (Visited.count(D)&&isa(D)) { const ObjCMethodDecl *ObjCM = cast(D); if (ObjCM->getMethodFamily() != OMF_init) return ExprEngine::Inline_Minimal; Index: test/Analysis/self-assign-unused.cpp =================================================================== --- /dev/null +++ test/Analysis/self-assign-unused.cpp @@ -0,0 +1,33 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=core,cplusplus,unix.Malloc,debug.ExprInspection %s -verify + +extern "C" char *strdup(const char* s); +extern "C" void free(void* ptr); + +class String { +public: + String(const char *s = "") : str(strdup(s)) {} + String(const String &rhs) : str(strdup(rhs.str)) {} + ~String(); + String& operator=(const String &rhs); + operator const char*() const; +private: + char *str; +}; + +String::~String() { + free(str); +} + +String& String::operator=(const String &rhs) { + free(str); + str = strdup(rhs.str); // expected-warning{{Use of memory after it is freed}} + return *this; +} + +String::operator const char*() const { + return str; +} + +int main() { + return 0; +} Index: test/Analysis/self-assign-used.cpp =================================================================== --- /dev/null +++ test/Analysis/self-assign-used.cpp @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=core,cplusplus,unix.Malloc,debug.ExprInspection %s -verify + +extern "C" char *strdup(const char* s); +extern "C" void free(void* ptr); + +class String { +public: + String(const char *s = "") : str(strdup(s)) {} + String(const String &rhs) : str(strdup(rhs.str)) {} + ~String(); + String& operator=(const String &rhs); + operator const char*() const; +private: + char *str; +}; + +String::~String() { + free(str); +} + +String& String::operator=(const String &rhs) { + free(str); + str = strdup(rhs.str); // expected-warning{{Use of memory after it is freed}} + return *this; +} + +String::operator const char*() const { + return str; +} + +int main() { + String s1 ("test"), s2; + s2 = s1; + return 0; +}