On Mon, May 18, 2015 at 10:57 PM, Richard Smith <[email protected]> wrote: > This has introduced a new warning: > > include/clang/Serialization/ASTReader.h:1746:8: warning: > 'ReadMismatchingDeleteExpressions' overrides a member function but is not > marked 'override' [-Winconsistent-missing-override] > void ReadMismatchingDeleteExpressions(llvm::MapVector< > ^ > include/clang/Sema/ExternalSemaSource.h:83:16: note: overridden virtual > function is here > virtual void ReadMismatchingDeleteExpressions(llvm::MapVector< > ^ > > Please can you fix this? Yes, already landed in r237613.
> > On Mon, May 18, 2015 at 12:59 PM, Ismail Pazarbasi > <[email protected]> wrote: >> >> Author: ismailp >> Date: Mon May 18 14:59:11 2015 >> New Revision: 237608 >> >> URL: http://llvm.org/viewvc/llvm-project?rev=237608&view=rev >> Log: >> Detect uses of mismatching forms of 'new' and 'delete' >> >> Emit warning when operand to `delete` is allocated with `new[]` or >> operand to `delete[]` is allocated with `new`. >> >> rev 2 update: >> `getNewExprFromInitListOrExpr` should return `dyn_cast_or_null` >> instead of `dyn_cast`, since `E` might be null. >> >> Reviewers: rtrieu, jordan_rose, rsmith >> >> Subscribers: majnemer, cfe-commits >> >> Differential Revision: http://reviews.llvm.org/D4661 >> >> Added: >> cfe/trunk/test/SemaCXX/delete-mismatch.h >> Modified: >> cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td >> cfe/trunk/include/clang/Sema/ExternalSemaSource.h >> cfe/trunk/include/clang/Sema/MultiplexExternalSemaSource.h >> cfe/trunk/include/clang/Sema/Sema.h >> cfe/trunk/include/clang/Serialization/ASTBitCodes.h >> cfe/trunk/include/clang/Serialization/ASTReader.h >> cfe/trunk/lib/Sema/MultiplexExternalSemaSource.cpp >> cfe/trunk/lib/Sema/Sema.cpp >> cfe/trunk/lib/Sema/SemaExprCXX.cpp >> cfe/trunk/lib/Serialization/ASTReader.cpp >> cfe/trunk/lib/Serialization/ASTWriter.cpp >> cfe/trunk/test/Analysis/Malloc+MismatchedDeallocator+NewDelete.cpp >> cfe/trunk/test/Analysis/MismatchedDeallocator-checker-test.mm >> cfe/trunk/test/Analysis/MismatchedDeallocator-path-notes.cpp >> cfe/trunk/test/CodeGenCXX/new.cpp >> cfe/trunk/test/SemaCXX/delete.cpp >> >> Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=237608&r1=237607&r2=237608&view=diff >> >> ============================================================================== >> --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original) >> +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Mon May 18 >> 14:59:11 2015 >> @@ -5516,7 +5516,12 @@ def err_delete_explicit_conversion : Err >> "conversion function">; >> def note_delete_conversion : Note<"conversion to pointer type %0">; >> def warn_delete_array_type : Warning< >> - "'delete' applied to a pointer-to-array type %0 treated as delete[]">; >> + "'delete' applied to a pointer-to-array type %0 treated as >> 'delete[]'">; >> +def warn_mismatched_delete_new : Warning< >> + "'delete%select{|[]}0' applied to a pointer that was allocated with " >> + "'new%select{[]|}0'; did you mean 'delete%select{[]|}0'?">, >> + InGroup<DiagGroup<"mismatched-new-delete">>; >> +def note_allocated_here : Note<"allocated with 'new%select{[]|}0' here">; >> def err_no_suitable_delete_member_function_found : Error< >> "no suitable member %0 in %1">; >> def err_ambiguous_suitable_delete_member_function_found : Error< >> >> Modified: cfe/trunk/include/clang/Sema/ExternalSemaSource.h >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/ExternalSemaSource.h?rev=237608&r1=237607&r2=237608&view=diff >> >> ============================================================================== >> --- cfe/trunk/include/clang/Sema/ExternalSemaSource.h (original) >> +++ cfe/trunk/include/clang/Sema/ExternalSemaSource.h Mon May 18 14:59:11 >> 2015 >> @@ -27,6 +27,7 @@ template <class T, unsigned n> class Sma >> namespace clang { >> >> class CXXConstructorDecl; >> +class CXXDeleteExpr; >> class CXXRecordDecl; >> class DeclaratorDecl; >> class LookupResult; >> @@ -79,6 +80,9 @@ public: >> virtual void ReadUndefinedButUsed( >> llvm::DenseMap<NamedDecl*, SourceLocation> >> &Undefined); >> >> + virtual void ReadMismatchingDeleteExpressions(llvm::MapVector< >> + FieldDecl *, llvm::SmallVector<std::pair<SourceLocation, bool>, 4>> >> &); >> + >> /// \brief Do last resort, unqualified lookup on a LookupResult that >> /// Sema cannot find. >> /// >> >> Modified: cfe/trunk/include/clang/Sema/MultiplexExternalSemaSource.h >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/MultiplexExternalSemaSource.h?rev=237608&r1=237607&r2=237608&view=diff >> >> ============================================================================== >> --- cfe/trunk/include/clang/Sema/MultiplexExternalSemaSource.h (original) >> +++ cfe/trunk/include/clang/Sema/MultiplexExternalSemaSource.h Mon May 18 >> 14:59:11 2015 >> @@ -230,6 +230,10 @@ public: >> void ReadUndefinedButUsed( >> llvm::DenseMap<NamedDecl*, SourceLocation> &Undefined) >> override; >> >> + void ReadMismatchingDeleteExpressions(llvm::MapVector< >> + FieldDecl *, llvm::SmallVector<std::pair<SourceLocation, bool>, 4>> >> & >> + Exprs) override; >> + >> /// \brief Do last resort, unqualified lookup on a LookupResult that >> /// Sema cannot find. >> /// >> >> Modified: cfe/trunk/include/clang/Sema/Sema.h >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=237608&r1=237607&r2=237608&view=diff >> >> ============================================================================== >> --- cfe/trunk/include/clang/Sema/Sema.h (original) >> +++ cfe/trunk/include/clang/Sema/Sema.h Mon May 18 14:59:11 2015 >> @@ -78,6 +78,7 @@ namespace clang { >> typedef SmallVector<CXXBaseSpecifier*, 4> CXXCastPath; >> class CXXConstructorDecl; >> class CXXConversionDecl; >> + class CXXDeleteExpr; >> class CXXDestructorDecl; >> class CXXFieldCollector; >> class CXXMemberCallExpr; >> @@ -404,6 +405,15 @@ public: >> llvm::SmallSetVector<const TypedefNameDecl *, 4> >> UnusedLocalTypedefNameCandidates; >> >> + /// \brief Delete-expressions to be analyzed at the end of translation >> unit >> + /// >> + /// This list contains class members, and locations of >> delete-expressions >> + /// that could not be proven as to whether they mismatch with >> new-expression >> + /// used in initializer of the field. >> + typedef std::pair<SourceLocation, bool> DeleteExprLoc; >> + typedef llvm::SmallVector<DeleteExprLoc, 4> DeleteLocs; >> + llvm::MapVector<FieldDecl *, DeleteLocs> DeleteExprs; >> + >> typedef llvm::SmallPtrSet<const CXXRecordDecl*, 8> RecordDeclSetTy; >> >> /// PureVirtualClassDiagSet - a set of class declarations which we have >> @@ -888,6 +898,11 @@ public: >> void getUndefinedButUsed( >> SmallVectorImpl<std::pair<NamedDecl *, SourceLocation> > >> &Undefined); >> >> + /// Retrieves list of suspicious delete-expressions that will be >> checked at >> + /// the end of translation unit. >> + const llvm::MapVector<FieldDecl *, DeleteLocs> & >> + getMismatchingDeleteExpressions() const; >> + >> typedef std::pair<ObjCMethodList, ObjCMethodList> GlobalMethods; >> typedef llvm::DenseMap<Selector, GlobalMethods> GlobalMethodPool; >> >> @@ -8663,6 +8678,9 @@ private: >> /// attempts to add itself into the container >> void CheckObjCCircularContainer(ObjCMessageExpr *Message); >> >> + void AnalyzeDeleteExprMismatch(const CXXDeleteExpr *DE); >> + void AnalyzeDeleteExprMismatch(FieldDecl *Field, SourceLocation >> DeleteLoc, >> + bool DeleteWasArrayForm); >> public: >> /// \brief Register a magic integral constant to be used as a type tag. >> void RegisterTypeTagForDatatype(const IdentifierInfo *ArgumentKind, >> >> Modified: cfe/trunk/include/clang/Serialization/ASTBitCodes.h >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Serialization/ASTBitCodes.h?rev=237608&r1=237607&r2=237608&view=diff >> >> ============================================================================== >> --- cfe/trunk/include/clang/Serialization/ASTBitCodes.h (original) >> +++ cfe/trunk/include/clang/Serialization/ASTBitCodes.h Mon May 18 >> 14:59:11 2015 >> @@ -561,6 +561,9 @@ namespace clang { >> /// \brief Record code for the table of offsets to >> CXXCtorInitializers >> /// lists. >> CXX_CTOR_INITIALIZERS_OFFSETS = 53, >> + >> + /// \brief Delete expressions that will be analyzed later. >> + DELETE_EXPRS_TO_ANALYZE = 54 >> }; >> >> /// \brief Record types used within a source manager block. >> >> Modified: cfe/trunk/include/clang/Serialization/ASTReader.h >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Serialization/ASTReader.h?rev=237608&r1=237607&r2=237608&view=diff >> >> ============================================================================== >> --- cfe/trunk/include/clang/Serialization/ASTReader.h (original) >> +++ cfe/trunk/include/clang/Serialization/ASTReader.h Mon May 18 14:59:11 >> 2015 >> @@ -760,6 +760,9 @@ private: >> /// SourceLocation of a matching ODR-use. >> SmallVector<uint64_t, 8> UndefinedButUsed; >> >> + /// \brief Delete expressions to analyze at the end of translation >> unit. >> + SmallVector<uint64_t, 8> DelayedDeleteExprs; >> + >> // \brief A list of late parsed template function data. >> SmallVector<uint64_t, 1> LateParsedTemplates; >> >> @@ -1740,6 +1743,10 @@ public: >> void ReadUndefinedButUsed( >> llvm::DenseMap<NamedDecl *, SourceLocation> &Undefined) >> override; >> >> + void ReadMismatchingDeleteExpressions(llvm::MapVector< >> + FieldDecl *, llvm::SmallVector<std::pair<SourceLocation, bool>, 4>> >> & >> + Exprs); >> + >> void ReadTentativeDefinitions( >> SmallVectorImpl<VarDecl *> &TentativeDefs) >> override; >> >> >> Modified: cfe/trunk/lib/Sema/MultiplexExternalSemaSource.cpp >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/MultiplexExternalSemaSource.cpp?rev=237608&r1=237607&r2=237608&view=diff >> >> ============================================================================== >> --- cfe/trunk/lib/Sema/MultiplexExternalSemaSource.cpp (original) >> +++ cfe/trunk/lib/Sema/MultiplexExternalSemaSource.cpp Mon May 18 14:59:11 >> 2015 >> @@ -212,7 +212,15 @@ void MultiplexExternalSemaSource::ReadUn >> for(size_t i = 0; i < Sources.size(); ++i) >> Sources[i]->ReadUndefinedButUsed(Undefined); >> } >> - >> + >> +void MultiplexExternalSemaSource::ReadMismatchingDeleteExpressions( >> + llvm::MapVector<FieldDecl *, >> + llvm::SmallVector<std::pair<SourceLocation, bool>, >> 4>> & >> + Exprs) { >> + for (auto &Source : Sources) >> + Source->ReadMismatchingDeleteExpressions(Exprs); >> +} >> + >> bool MultiplexExternalSemaSource::LookupUnqualified(LookupResult &R, >> Scope *S){ >> for(size_t i = 0; i < Sources.size(); ++i) >> Sources[i]->LookupUnqualified(R, S); >> >> Modified: cfe/trunk/lib/Sema/Sema.cpp >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.cpp?rev=237608&r1=237607&r2=237608&view=diff >> >> ============================================================================== >> --- cfe/trunk/lib/Sema/Sema.cpp (original) >> +++ cfe/trunk/lib/Sema/Sema.cpp Mon May 18 14:59:11 2015 >> @@ -861,6 +861,17 @@ void Sema::ActOnEndOfTranslationUnit() { >> } >> } >> >> + if (!Diags.isIgnored(diag::warn_mismatched_delete_new, >> SourceLocation())) { >> + if (ExternalSource) >> + ExternalSource->ReadMismatchingDeleteExpressions(DeleteExprs); >> + for (const auto &DeletedFieldInfo : DeleteExprs) { >> + for (const auto &DeleteExprLoc : DeletedFieldInfo.second) { >> + AnalyzeDeleteExprMismatch(DeletedFieldInfo.first, >> DeleteExprLoc.first, >> + DeleteExprLoc.second); >> + } >> + } >> + } >> + >> // Check we've noticed that we're no longer parsing the initializer for >> every >> // variable. If we miss cases, then at best we have a performance issue >> and >> // at worst a rejects-valid bug. >> @@ -1220,6 +1231,9 @@ void ExternalSemaSource::ReadUndefinedBu >> llvm::DenseMap<NamedDecl *, SourceLocation> >> &Undefined) { >> } >> >> +void >> ExternalSemaSource::ReadMismatchingDeleteExpressions(llvm::MapVector< >> + FieldDecl *, llvm::SmallVector<std::pair<SourceLocation, bool>, 4>> >> &) {} >> + >> void PrettyDeclStackTraceEntry::print(raw_ostream &OS) const { >> SourceLocation Loc = this->Loc; >> if (!Loc.isValid() && TheDecl) Loc = TheDecl->getLocation(); >> @@ -1468,3 +1482,8 @@ CapturedRegionScopeInfo *Sema::getCurCap >> >> return dyn_cast<CapturedRegionScopeInfo>(FunctionScopes.back()); >> } >> + >> +const llvm::MapVector<FieldDecl *, Sema::DeleteLocs> & >> +Sema::getMismatchingDeleteExpressions() const { >> + return DeleteExprs; >> +} >> >> Modified: cfe/trunk/lib/Sema/SemaExprCXX.cpp >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprCXX.cpp?rev=237608&r1=237607&r2=237608&view=diff >> >> ============================================================================== >> --- cfe/trunk/lib/Sema/SemaExprCXX.cpp (original) >> +++ cfe/trunk/lib/Sema/SemaExprCXX.cpp Mon May 18 14:59:11 2015 >> @@ -2339,6 +2339,261 @@ bool Sema::FindDeallocationFunction(Sour >> return false; >> } >> >> +namespace { >> +/// \brief Checks whether delete-expression, and new-expression used for >> +/// initializing deletee have the same array form. >> +class MismatchingNewDeleteDetector { >> +public: >> + enum MismatchResult { >> + /// Indicates that there is no mismatch or a mismatch cannot be >> proven. >> + NoMismatch, >> + /// Indicates that variable is initialized with mismatching form of >> \a new. >> + VarInitMismatches, >> + /// Indicates that member is initialized with mismatching form of \a >> new. >> + MemberInitMismatches, >> + /// Indicates that 1 or more constructors' definitions could not been >> + /// analyzed, and they will be checked again at the end of >> translation unit. >> + AnalyzeLater >> + }; >> + >> + /// \param EndOfTU True, if this is the final analysis at the end of >> + /// translation unit. False, if this is the initial analysis at the >> point >> + /// delete-expression was encountered. >> + explicit MismatchingNewDeleteDetector(bool EndOfTU) >> + : IsArrayForm(false), Field(nullptr), EndOfTU(EndOfTU), >> + HasUndefinedConstructors(false) {} >> + >> + /// \brief Checks whether pointee of a delete-expression is initialized >> with >> + /// matching form of new-expression. >> + /// >> + /// If return value is \c VarInitMismatches or \c MemberInitMismatches >> at the >> + /// point where delete-expression is encountered, then a warning will >> be >> + /// issued immediately. If return value is \c AnalyzeLater at the point >> where >> + /// delete-expression is seen, then member will be analyzed at the end >> of >> + /// translation unit. \c AnalyzeLater is returned iff at least one >> constructor >> + /// couldn't be analyzed. If at least one constructor initializes the >> member >> + /// with matching type of new, the return value is \c NoMismatch. >> + MismatchResult analyzeDeleteExpr(const CXXDeleteExpr *DE); >> + /// \brief Analyzes a class member. >> + /// \param Field Class member to analyze. >> + /// \param DeleteWasArrayForm Array form-ness of the delete-expression >> used >> + /// for deleting the \p Field. >> + MismatchResult analyzeField(FieldDecl *Field, bool DeleteWasArrayForm); >> + /// List of mismatching new-expressions used for initialization of the >> pointee >> + llvm::SmallVector<const CXXNewExpr *, 4> NewExprs; >> + /// Indicates whether delete-expression was in array form. >> + bool IsArrayForm; >> + FieldDecl *Field; >> + >> +private: >> + const bool EndOfTU; >> + /// \brief Indicates that there is at least one constructor without >> body. >> + bool HasUndefinedConstructors; >> + /// \brief Returns \c CXXNewExpr from given initialization expression. >> + /// \param E Expression used for initializing pointee in >> delete-expression. >> + /// \param E can be a single-element \c InitListExpr consisting of >> + /// \param E new-expression. >> + const CXXNewExpr *getNewExprFromInitListOrExpr(const Expr *E); >> + /// \brief Returns whether member is initialized with mismatching form >> of >> + /// \c new either by the member initializer or in-class initialization. >> + /// >> + /// If bodies of all constructors are not visible at the end of >> translation >> + /// unit or at least one constructor initializes member with the >> matching >> + /// form of \c new, mismatch cannot be proven, and this function will >> return >> + /// \c NoMismatch. >> + MismatchResult analyzeMemberExpr(const MemberExpr *ME); >> + /// \brief Returns whether variable is initialized with mismatching >> form of >> + /// \c new. >> + /// >> + /// If variable is initialized with matching form of \c new or variable >> is not >> + /// initialized with a \c new expression, this function will return >> true. >> + /// If variable is initialized with mismatching form of \c new, returns >> false. >> + /// \param D Variable to analyze. >> + bool hasMatchingVarInit(const DeclRefExpr *D); >> + /// \brief Checks whether the constructor initializes pointee with >> mismatching >> + /// form of \c new. >> + /// >> + /// Returns true, if member is initialized with matching form of \c new >> in >> + /// member initializer list. Returns false, if member is initialized >> with the >> + /// matching form of \c new in this constructor's initializer or given >> + /// constructor isn't defined at the point where delete-expression is >> seen, or >> + /// member isn't initialized by the constructor. >> + bool hasMatchingNewInCtor(const CXXConstructorDecl *CD); >> + /// \brief Checks whether member is initialized with matching form of >> + /// \c new in member initializer list. >> + bool hasMatchingNewInCtorInit(const CXXCtorInitializer *CI); >> + /// Checks whether member is initialized with mismatching form of \c >> new by >> + /// in-class initializer. >> + MismatchResult analyzeInClassInitializer(); >> +}; >> +} >> + >> +MismatchingNewDeleteDetector::MismatchResult >> +MismatchingNewDeleteDetector::analyzeDeleteExpr(const CXXDeleteExpr *DE) >> { >> + NewExprs.clear(); >> + assert(DE && "Expected delete-expression"); >> + IsArrayForm = DE->isArrayForm(); >> + const Expr *E = DE->getArgument()->IgnoreParenImpCasts(); >> + if (const MemberExpr *ME = dyn_cast<const MemberExpr>(E)) { >> + return analyzeMemberExpr(ME); >> + } else if (const DeclRefExpr *D = dyn_cast<const DeclRefExpr>(E)) { >> + if (!hasMatchingVarInit(D)) >> + return VarInitMismatches; >> + } >> + return NoMismatch; >> +} >> + >> +const CXXNewExpr * >> +MismatchingNewDeleteDetector::getNewExprFromInitListOrExpr(const Expr *E) >> { >> + assert(E != nullptr && "Expected a valid initializer expression"); >> + E = E->IgnoreParenImpCasts(); >> + if (const InitListExpr *ILE = dyn_cast<const InitListExpr>(E)) { >> + if (ILE->getNumInits() == 1) >> + E = dyn_cast<const >> CXXNewExpr>(ILE->getInit(0)->IgnoreParenImpCasts()); >> + } >> + >> + return dyn_cast_or_null<const CXXNewExpr>(E); >> +} >> + >> +bool MismatchingNewDeleteDetector::hasMatchingNewInCtorInit( >> + const CXXCtorInitializer *CI) { >> + const CXXNewExpr *NE = nullptr; >> + if (Field == CI->getMember() && >> + (NE = getNewExprFromInitListOrExpr(CI->getInit()))) { >> + if (NE->isArray() == IsArrayForm) >> + return true; >> + else >> + NewExprs.push_back(NE); >> + } >> + return false; >> +} >> + >> +bool MismatchingNewDeleteDetector::hasMatchingNewInCtor( >> + const CXXConstructorDecl *CD) { >> + if (CD->isImplicit()) >> + return false; >> + const FunctionDecl *Definition = CD; >> + if (!CD->isThisDeclarationADefinition() && !CD->isDefined(Definition)) >> { >> + HasUndefinedConstructors = true; >> + return EndOfTU; >> + } >> + for (const auto *CI : cast<const >> CXXConstructorDecl>(Definition)->inits()) { >> + if (hasMatchingNewInCtorInit(CI)) >> + return true; >> + } >> + return false; >> +} >> + >> +MismatchingNewDeleteDetector::MismatchResult >> +MismatchingNewDeleteDetector::analyzeInClassInitializer() { >> + assert(Field != nullptr && "This should be called only for members"); >> + if (const CXXNewExpr *NE = >> + getNewExprFromInitListOrExpr(Field->getInClassInitializer())) { >> + if (NE->isArray() != IsArrayForm) { >> + NewExprs.push_back(NE); >> + return MemberInitMismatches; >> + } >> + } >> + return NoMismatch; >> +} >> + >> +MismatchingNewDeleteDetector::MismatchResult >> +MismatchingNewDeleteDetector::analyzeField(FieldDecl *Field, >> + bool DeleteWasArrayForm) { >> + assert(Field != nullptr && "Analysis requires a valid class member."); >> + this->Field = Field; >> + IsArrayForm = DeleteWasArrayForm; >> + const CXXRecordDecl *RD = cast<const >> CXXRecordDecl>(Field->getParent()); >> + for (const auto *CD : RD->ctors()) { >> + if (hasMatchingNewInCtor(CD)) >> + return NoMismatch; >> + } >> + if (HasUndefinedConstructors) >> + return EndOfTU ? NoMismatch : AnalyzeLater; >> + if (!NewExprs.empty()) >> + return MemberInitMismatches; >> + return Field->hasInClassInitializer() ? analyzeInClassInitializer() >> + : NoMismatch; >> +} >> + >> +MismatchingNewDeleteDetector::MismatchResult >> +MismatchingNewDeleteDetector::analyzeMemberExpr(const MemberExpr *ME) { >> + assert(ME != nullptr && "Expected a member expression"); >> + if (FieldDecl *F = dyn_cast<FieldDecl>(ME->getMemberDecl())) >> + return analyzeField(F, IsArrayForm); >> + return NoMismatch; >> +} >> + >> +bool MismatchingNewDeleteDetector::hasMatchingVarInit(const DeclRefExpr >> *D) { >> + const CXXNewExpr *NE = nullptr; >> + if (const VarDecl *VD = dyn_cast<const VarDecl>(D->getDecl())) { >> + if (VD->hasInit() && (NE = >> getNewExprFromInitListOrExpr(VD->getInit())) && >> + NE->isArray() != IsArrayForm) { >> + NewExprs.push_back(NE); >> + } >> + } >> + return NewExprs.empty(); >> +} >> + >> +static void >> +DiagnoseMismatchedNewDelete(Sema &SemaRef, SourceLocation DeleteLoc, >> + const MismatchingNewDeleteDetector &Detector) >> { >> + SourceLocation EndOfDelete = SemaRef.getLocForEndOfToken(DeleteLoc); >> + FixItHint H; >> + if (!Detector.IsArrayForm) >> + H = FixItHint::CreateInsertion(EndOfDelete, "[]"); >> + else { >> + SourceLocation RSquare = Lexer::findLocationAfterToken( >> + DeleteLoc, tok::l_square, SemaRef.getSourceManager(), >> + SemaRef.getLangOpts(), true); >> + if (RSquare.isValid()) >> + H = FixItHint::CreateRemoval(SourceRange(EndOfDelete, RSquare)); >> + } >> + SemaRef.Diag(DeleteLoc, diag::warn_mismatched_delete_new) >> + << Detector.IsArrayForm << H; >> + >> + for (const auto *NE : Detector.NewExprs) >> + SemaRef.Diag(NE->getExprLoc(), diag::note_allocated_here) >> + << Detector.IsArrayForm; >> +} >> + >> +void Sema::AnalyzeDeleteExprMismatch(const CXXDeleteExpr *DE) { >> + if (Diags.isIgnored(diag::warn_mismatched_delete_new, >> SourceLocation())) >> + return; >> + MismatchingNewDeleteDetector Detector(/*EndOfTU=*/false); >> + switch (Detector.analyzeDeleteExpr(DE)) { >> + case MismatchingNewDeleteDetector::VarInitMismatches: >> + case MismatchingNewDeleteDetector::MemberInitMismatches: { >> + DiagnoseMismatchedNewDelete(*this, DE->getLocStart(), Detector); >> + break; >> + } >> + case MismatchingNewDeleteDetector::AnalyzeLater: { >> + DeleteExprs[Detector.Field].push_back( >> + std::make_pair(DE->getLocStart(), DE->isArrayForm())); >> + break; >> + } >> + case MismatchingNewDeleteDetector::NoMismatch: >> + break; >> + } >> +} >> + >> +void Sema::AnalyzeDeleteExprMismatch(FieldDecl *Field, SourceLocation >> DeleteLoc, >> + bool DeleteWasArrayForm) { >> + MismatchingNewDeleteDetector Detector(/*EndOfTU=*/true); >> + switch (Detector.analyzeField(Field, DeleteWasArrayForm)) { >> + case MismatchingNewDeleteDetector::VarInitMismatches: >> + llvm_unreachable("This analysis should have been done for class >> members."); >> + case MismatchingNewDeleteDetector::AnalyzeLater: >> + llvm_unreachable("Analysis cannot be postponed any point beyond end >> of " >> + "translation unit."); >> + case MismatchingNewDeleteDetector::MemberInitMismatches: >> + DiagnoseMismatchedNewDelete(*this, DeleteLoc, Detector); >> + break; >> + case MismatchingNewDeleteDetector::NoMismatch: >> + break; >> + } >> +} >> + >> /// ActOnCXXDelete - Parsed a C++ 'delete' expression (C++ 5.3.5), as in: >> /// @code ::delete ptr; @endcode >> /// or >> @@ -2454,12 +2709,6 @@ Sema::ActOnCXXDelete(SourceLocation Star >> } >> } >> >> - // C++ [expr.delete]p2: >> - // [Note: a pointer to a const type can be the operand of a >> - // delete-expression; it is not necessary to cast away the >> constness >> - // (5.2.11) of the pointer expression before it is used as the >> operand >> - // of the delete-expression. ] >> - >> if (Pointee->isArrayType() && !ArrayForm) { >> Diag(StartLoc, diag::warn_delete_array_type) >> << Type << Ex.get()->getSourceRange() >> @@ -2534,7 +2783,7 @@ Sema::ActOnCXXDelete(SourceLocation Star >> DeleteName); >> >> MarkFunctionReferenced(StartLoc, OperatorDelete); >> - >> + >> // Check access and ambiguity of operator delete and destructor. >> if (PointeeRD) { >> if (CXXDestructorDecl *Dtor = LookupDestructor(PointeeRD)) { >> @@ -2544,9 +2793,11 @@ Sema::ActOnCXXDelete(SourceLocation Star >> } >> } >> >> - return new (Context) CXXDeleteExpr( >> + CXXDeleteExpr *Result = new (Context) CXXDeleteExpr( >> Context.VoidTy, UseGlobal, ArrayForm, ArrayFormAsWritten, >> UsualArrayDeleteWantsSize, OperatorDelete, Ex.get(), StartLoc); >> + AnalyzeDeleteExprMismatch(Result); >> + return Result; >> } >> >> /// \brief Check the use of the given variable as a C++ condition in an >> if, >> >> Modified: cfe/trunk/lib/Serialization/ASTReader.cpp >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReader.cpp?rev=237608&r1=237607&r2=237608&view=diff >> >> ============================================================================== >> --- cfe/trunk/lib/Serialization/ASTReader.cpp (original) >> +++ cfe/trunk/lib/Serialization/ASTReader.cpp Mon May 18 14:59:11 2015 >> @@ -3021,6 +3021,18 @@ ASTReader::ReadASTBlock(ModuleFile &F, u >> ReadSourceLocation(F, Record, I).getRawEncoding()); >> } >> break; >> + case DELETE_EXPRS_TO_ANALYZE: >> + for (unsigned I = 0, N = Record.size(); I != N;) { >> + DelayedDeleteExprs.push_back(getGlobalDeclID(F, Record[I++])); >> + const uint64_t Count = Record[I++]; >> + DelayedDeleteExprs.push_back(Count); >> + for (uint64_t C = 0; C < Count; ++C) { >> + DelayedDeleteExprs.push_back(ReadSourceLocation(F, Record, >> I).getRawEncoding()); >> + bool IsArrayForm = Record[I++] == 1; >> + DelayedDeleteExprs.push_back(IsArrayForm); >> + } >> + } >> + break; >> >> case IMPORTED_MODULES: { >> if (F.Kind != MK_ImplicitModule && F.Kind != MK_ExplicitModule) { >> @@ -7016,6 +7028,21 @@ void ASTReader::ReadUndefinedButUsed( >> } >> } >> >> +void ASTReader::ReadMismatchingDeleteExpressions(llvm::MapVector< >> + FieldDecl *, llvm::SmallVector<std::pair<SourceLocation, bool>, 4>> & >> + Exprs) { >> + for (unsigned Idx = 0, N = DelayedDeleteExprs.size(); Idx != N;) { >> + FieldDecl *FD = cast<FieldDecl>(GetDecl(DelayedDeleteExprs[Idx++])); >> + uint64_t Count = DelayedDeleteExprs[Idx++]; >> + for (uint64_t C = 0; C < Count; ++C) { >> + SourceLocation DeleteLoc = >> + SourceLocation::getFromRawEncoding(DelayedDeleteExprs[Idx++]); >> + const bool IsArrayForm = DelayedDeleteExprs[Idx++]; >> + Exprs[FD].push_back(std::make_pair(DeleteLoc, IsArrayForm)); >> + } >> + } >> +} >> + >> void ASTReader::ReadTentativeDefinitions( >> SmallVectorImpl<VarDecl *> &TentativeDefs) { >> for (unsigned I = 0, N = TentativeDefinitions.size(); I != N; ++I) { >> >> Modified: cfe/trunk/lib/Serialization/ASTWriter.cpp >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriter.cpp?rev=237608&r1=237607&r2=237608&view=diff >> >> ============================================================================== >> --- cfe/trunk/lib/Serialization/ASTWriter.cpp (original) >> +++ cfe/trunk/lib/Serialization/ASTWriter.cpp Mon May 18 14:59:11 2015 >> @@ -4155,6 +4155,20 @@ void ASTWriter::WriteASTCore(Sema &SemaR >> AddSourceLocation(I->second, UndefinedButUsed); >> } >> >> + // Build a record containing all delete-expressions that we would like >> to >> + // analyze later in AST. >> + RecordData DeleteExprsToAnalyze; >> + >> + for (const auto &DeleteExprsInfo : >> + SemaRef.getMismatchingDeleteExpressions()) { >> + AddDeclRef(DeleteExprsInfo.first, DeleteExprsToAnalyze); >> + DeleteExprsToAnalyze.push_back(DeleteExprsInfo.second.size()); >> + for (const auto &DeleteLoc : DeleteExprsInfo.second) { >> + AddSourceLocation(DeleteLoc.first, DeleteExprsToAnalyze); >> + DeleteExprsToAnalyze.push_back(DeleteLoc.second); >> + } >> + } >> + >> // Write the control block >> WriteControlBlock(PP, Context, isysroot, OutputFile); >> >> @@ -4424,7 +4438,10 @@ void ASTWriter::WriteASTCore(Sema &SemaR >> // Write the undefined internal functions and variables, and inline >> functions. >> if (!UndefinedButUsed.empty()) >> Stream.EmitRecord(UNDEFINED_BUT_USED, UndefinedButUsed); >> - >> + >> + if (!DeleteExprsToAnalyze.empty()) >> + Stream.EmitRecord(DELETE_EXPRS_TO_ANALYZE, DeleteExprsToAnalyze); >> + >> // Write the visible updates to DeclContexts. >> for (auto *DC : UpdatedDeclContexts) >> WriteDeclContextVisibleUpdate(DC); >> >> Modified: >> cfe/trunk/test/Analysis/Malloc+MismatchedDeallocator+NewDelete.cpp >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/Malloc%2BMismatchedDeallocator%2BNewDelete.cpp?rev=237608&r1=237607&r2=237608&view=diff >> >> ============================================================================== >> --- cfe/trunk/test/Analysis/Malloc+MismatchedDeallocator+NewDelete.cpp >> (original) >> +++ cfe/trunk/test/Analysis/Malloc+MismatchedDeallocator+NewDelete.cpp Mon >> May 18 14:59:11 2015 >> @@ -97,9 +97,11 @@ void testShouldReportDoubleFreeNotMismat >> free(p); >> delete globalPtr; // expected-warning {{Attempt to free released >> memory}} >> } >> - >> +int *allocIntArray(unsigned c) { >> + return new int[c]; >> +} >> void testMismatchedChangePointeeThroughAssignment() { >> - int *arr = new int[4]; >> + int *arr = allocIntArray(4); >> globalPtr = arr; >> delete arr; // expected-warning{{Memory allocated by 'new[]' should be >> deallocated by 'delete[]', not 'delete'}} >> -} >> \ No newline at end of file >> +} >> >> Modified: cfe/trunk/test/Analysis/MismatchedDeallocator-checker-test.mm >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/MismatchedDeallocator-checker-test.mm?rev=237608&r1=237607&r2=237608&view=diff >> >> ============================================================================== >> --- cfe/trunk/test/Analysis/MismatchedDeallocator-checker-test.mm >> (original) >> +++ cfe/trunk/test/Analysis/MismatchedDeallocator-checker-test.mm Mon May >> 18 14:59:11 2015 >> @@ -95,8 +95,11 @@ void testNew6() { >> realloc(p, sizeof(long)); // expected-warning{{Memory allocated by >> 'new[]' should be deallocated by 'delete[]', not realloc()}} >> } >> >> +int *allocInt() { >> + return new int; >> +} >> void testNew7() { >> - int *p = new int; >> + int *p = allocInt(); >> delete[] p; // expected-warning{{Memory allocated by 'new' should be >> deallocated by 'delete', not 'delete[]'}} >> } >> >> @@ -105,8 +108,12 @@ void testNew8() { >> delete[] p; // expected-warning{{Memory allocated by operator new >> should be deallocated by 'delete', not 'delete[]'}} >> } >> >> +int *allocIntArray(unsigned c) { >> + return new int[c]; >> +} >> + >> void testNew9() { >> - int *p = new int[1]; >> + int *p = allocIntArray(1); >> delete p; // expected-warning{{Memory allocated by 'new[]' should be >> deallocated by 'delete[]', not 'delete'}} >> } >> >> >> Modified: cfe/trunk/test/Analysis/MismatchedDeallocator-path-notes.cpp >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/MismatchedDeallocator-path-notes.cpp?rev=237608&r1=237607&r2=237608&view=diff >> >> ============================================================================== >> --- cfe/trunk/test/Analysis/MismatchedDeallocator-path-notes.cpp >> (original) >> +++ cfe/trunk/test/Analysis/MismatchedDeallocator-path-notes.cpp Mon May >> 18 14:59:11 2015 >> @@ -3,9 +3,12 @@ >> // RUN: FileCheck --input-file=%t.plist %s >> >> void changePointee(int *p); >> +int *allocIntArray(unsigned c) { >> + return new int[c]; // expected-note {{Memory is allocated}} >> +} >> void test() { >> - int *p = new int[1]; >> - // expected-note@-1 {{Memory is allocated}} >> + int *p = allocIntArray(1); // expected-note {{Calling 'allocIntArray'}} >> + // expected-note@-1 {{Returned allocated memory}} >> changePointee(p); >> delete p; // expected-warning {{Memory allocated by 'new[]' should be >> deallocated by 'delete[]', not 'delete'}} >> // expected-note@-1 {{Memory allocated by 'new[]' should be deallocated >> by 'delete[]', not 'delete'}} >> @@ -24,13 +27,124 @@ void test() { >> // CHECK-NEXT: <key>start</key> >> // CHECK-NEXT: <array> >> // CHECK-NEXT: <dict> >> +// CHECK-NEXT: <key>line</key><integer>10</integer> >> +// CHECK-NEXT: <key>col</key><integer>3</integer> >> +// CHECK-NEXT: <key>file</key><integer>0</integer> >> +// CHECK-NEXT: </dict> >> +// CHECK-NEXT: <dict> >> +// CHECK-NEXT: <key>line</key><integer>10</integer> >> +// CHECK-NEXT: <key>col</key><integer>5</integer> >> +// CHECK-NEXT: <key>file</key><integer>0</integer> >> +// CHECK-NEXT: </dict> >> +// CHECK-NEXT: </array> >> +// CHECK-NEXT: <key>end</key> >> +// CHECK-NEXT: <array> >> +// CHECK-NEXT: <dict> >> +// CHECK-NEXT: <key>line</key><integer>10</integer> >> +// CHECK-NEXT: <key>col</key><integer>12</integer> >> +// CHECK-NEXT: <key>file</key><integer>0</integer> >> +// CHECK-NEXT: </dict> >> +// CHECK-NEXT: <dict> >> +// CHECK-NEXT: <key>line</key><integer>10</integer> >> +// CHECK-NEXT: <key>col</key><integer>24</integer> >> +// CHECK-NEXT: <key>file</key><integer>0</integer> >> +// CHECK-NEXT: </dict> >> +// CHECK-NEXT: </array> >> +// CHECK-NEXT: </dict> >> +// CHECK-NEXT: </array> >> +// CHECK-NEXT: </dict> >> +// CHECK-NEXT: <dict> >> +// CHECK-NEXT: <key>kind</key><string>event</string> >> +// CHECK-NEXT: <key>location</key> >> +// CHECK-NEXT: <dict> >> +// CHECK-NEXT: <key>line</key><integer>10</integer> >> +// CHECK-NEXT: <key>col</key><integer>12</integer> >> +// CHECK-NEXT: <key>file</key><integer>0</integer> >> +// CHECK-NEXT: </dict> >> +// CHECK-NEXT: <key>ranges</key> >> +// CHECK-NEXT: <array> >> +// CHECK-NEXT: <array> >> +// CHECK-NEXT: <dict> >> +// CHECK-NEXT: <key>line</key><integer>10</integer> >> +// CHECK-NEXT: <key>col</key><integer>12</integer> >> +// CHECK-NEXT: <key>file</key><integer>0</integer> >> +// CHECK-NEXT: </dict> >> +// CHECK-NEXT: <dict> >> +// CHECK-NEXT: <key>line</key><integer>10</integer> >> +// CHECK-NEXT: <key>col</key><integer>27</integer> >> +// CHECK-NEXT: <key>file</key><integer>0</integer> >> +// CHECK-NEXT: </dict> >> +// CHECK-NEXT: </array> >> +// CHECK-NEXT: </array> >> +// CHECK-NEXT: <key>depth</key><integer>0</integer> >> +// CHECK-NEXT: <key>extended_message</key> >> +// CHECK-NEXT: <string>Calling 'allocIntArray'</string> >> +// CHECK-NEXT: <key>message</key> >> +// CHECK-NEXT: <string>Calling 'allocIntArray'</string> >> +// CHECK-NEXT: </dict> >> +// CHECK-NEXT: <dict> >> +// CHECK-NEXT: <key>kind</key><string>event</string> >> +// CHECK-NEXT: <key>location</key> >> +// CHECK-NEXT: <dict> >> +// CHECK-NEXT: <key>line</key><integer>6</integer> >> +// CHECK-NEXT: <key>col</key><integer>1</integer> >> +// CHECK-NEXT: <key>file</key><integer>0</integer> >> +// CHECK-NEXT: </dict> >> +// CHECK-NEXT: <key>depth</key><integer>1</integer> >> +// CHECK-NEXT: <key>extended_message</key> >> +// CHECK-NEXT: <string>Entered call from 'test'</string> >> +// CHECK-NEXT: <key>message</key> >> +// CHECK-NEXT: <string>Entered call from 'test'</string> >> +// CHECK-NEXT: </dict> >> +// CHECK-NEXT: <dict> >> +// CHECK-NEXT: <key>kind</key><string>control</string> >> +// CHECK-NEXT: <key>edges</key> >> +// CHECK-NEXT: <array> >> +// CHECK-NEXT: <dict> >> +// CHECK-NEXT: <key>start</key> >> +// CHECK-NEXT: <array> >> +// CHECK-NEXT: <dict> >> +// CHECK-NEXT: <key>line</key><integer>6</integer> >> +// CHECK-NEXT: <key>col</key><integer>1</integer> >> +// CHECK-NEXT: <key>file</key><integer>0</integer> >> +// CHECK-NEXT: </dict> >> +// CHECK-NEXT: <dict> >> +// CHECK-NEXT: <key>line</key><integer>6</integer> >> +// CHECK-NEXT: <key>col</key><integer>3</integer> >> +// CHECK-NEXT: <key>file</key><integer>0</integer> >> +// CHECK-NEXT: </dict> >> +// CHECK-NEXT: </array> >> +// CHECK-NEXT: <key>end</key> >> +// CHECK-NEXT: <array> >> +// CHECK-NEXT: <dict> >> // CHECK-NEXT: <key>line</key><integer>7</integer> >> // CHECK-NEXT: <key>col</key><integer>3</integer> >> // CHECK-NEXT: <key>file</key><integer>0</integer> >> // CHECK-NEXT: </dict> >> // CHECK-NEXT: <dict> >> // CHECK-NEXT: <key>line</key><integer>7</integer> >> -// CHECK-NEXT: <key>col</key><integer>5</integer> >> +// CHECK-NEXT: <key>col</key><integer>8</integer> >> +// CHECK-NEXT: <key>file</key><integer>0</integer> >> +// CHECK-NEXT: </dict> >> +// CHECK-NEXT: </array> >> +// CHECK-NEXT: </dict> >> +// CHECK-NEXT: </array> >> +// CHECK-NEXT: </dict> >> +// CHECK-NEXT: <dict> >> +// CHECK-NEXT: <key>kind</key><string>control</string> >> +// CHECK-NEXT: <key>edges</key> >> +// CHECK-NEXT: <array> >> +// CHECK-NEXT: <dict> >> +// CHECK-NEXT: <key>start</key> >> +// CHECK-NEXT: <array> >> +// CHECK-NEXT: <dict> >> +// CHECK-NEXT: <key>line</key><integer>7</integer> >> +// CHECK-NEXT: <key>col</key><integer>3</integer> >> +// CHECK-NEXT: <key>file</key><integer>0</integer> >> +// CHECK-NEXT: </dict> >> +// CHECK-NEXT: <dict> >> +// CHECK-NEXT: <key>line</key><integer>7</integer> >> +// CHECK-NEXT: <key>col</key><integer>8</integer> >> // CHECK-NEXT: <key>file</key><integer>0</integer> >> // CHECK-NEXT: </dict> >> // CHECK-NEXT: </array> >> @@ -38,12 +152,12 @@ void test() { >> // CHECK-NEXT: <array> >> // CHECK-NEXT: <dict> >> // CHECK-NEXT: <key>line</key><integer>7</integer> >> -// CHECK-NEXT: <key>col</key><integer>12</integer> >> +// CHECK-NEXT: <key>col</key><integer>10</integer> >> // CHECK-NEXT: <key>file</key><integer>0</integer> >> // CHECK-NEXT: </dict> >> // CHECK-NEXT: <dict> >> // CHECK-NEXT: <key>line</key><integer>7</integer> >> -// CHECK-NEXT: <key>col</key><integer>14</integer> >> +// CHECK-NEXT: <key>col</key><integer>12</integer> >> // CHECK-NEXT: <key>file</key><integer>0</integer> >> // CHECK-NEXT: </dict> >> // CHECK-NEXT: </array> >> @@ -55,7 +169,7 @@ void test() { >> // CHECK-NEXT: <key>location</key> >> // CHECK-NEXT: <dict> >> // CHECK-NEXT: <key>line</key><integer>7</integer> >> -// CHECK-NEXT: <key>col</key><integer>12</integer> >> +// CHECK-NEXT: <key>col</key><integer>10</integer> >> // CHECK-NEXT: <key>file</key><integer>0</integer> >> // CHECK-NEXT: </dict> >> // CHECK-NEXT: <key>ranges</key> >> @@ -63,23 +177,52 @@ void test() { >> // CHECK-NEXT: <array> >> // CHECK-NEXT: <dict> >> // CHECK-NEXT: <key>line</key><integer>7</integer> >> -// CHECK-NEXT: <key>col</key><integer>12</integer> >> +// CHECK-NEXT: <key>col</key><integer>10</integer> >> // CHECK-NEXT: <key>file</key><integer>0</integer> >> // CHECK-NEXT: </dict> >> // CHECK-NEXT: <dict> >> // CHECK-NEXT: <key>line</key><integer>7</integer> >> -// CHECK-NEXT: <key>col</key><integer>21</integer> >> +// CHECK-NEXT: <key>col</key><integer>19</integer> >> // CHECK-NEXT: <key>file</key><integer>0</integer> >> // CHECK-NEXT: </dict> >> // CHECK-NEXT: </array> >> // CHECK-NEXT: </array> >> -// CHECK-NEXT: <key>depth</key><integer>0</integer> >> +// CHECK-NEXT: <key>depth</key><integer>1</integer> >> // CHECK-NEXT: <key>extended_message</key> >> // CHECK-NEXT: <string>Memory is allocated</string> >> // CHECK-NEXT: <key>message</key> >> // CHECK-NEXT: <string>Memory is allocated</string> >> // CHECK-NEXT: </dict> >> // CHECK-NEXT: <dict> >> +// CHECK-NEXT: <key>kind</key><string>event</string> >> +// CHECK-NEXT: <key>location</key> >> +// CHECK-NEXT: <dict> >> +// CHECK-NEXT: <key>line</key><integer>10</integer> >> +// CHECK-NEXT: <key>col</key><integer>12</integer> >> +// CHECK-NEXT: <key>file</key><integer>0</integer> >> +// CHECK-NEXT: </dict> >> +// CHECK-NEXT: <key>ranges</key> >> +// CHECK-NEXT: <array> >> +// CHECK-NEXT: <array> >> +// CHECK-NEXT: <dict> >> +// CHECK-NEXT: <key>line</key><integer>10</integer> >> +// CHECK-NEXT: <key>col</key><integer>12</integer> >> +// CHECK-NEXT: <key>file</key><integer>0</integer> >> +// CHECK-NEXT: </dict> >> +// CHECK-NEXT: <dict> >> +// CHECK-NEXT: <key>line</key><integer>10</integer> >> +// CHECK-NEXT: <key>col</key><integer>27</integer> >> +// CHECK-NEXT: <key>file</key><integer>0</integer> >> +// CHECK-NEXT: </dict> >> +// CHECK-NEXT: </array> >> +// CHECK-NEXT: </array> >> +// CHECK-NEXT: <key>depth</key><integer>0</integer> >> +// CHECK-NEXT: <key>extended_message</key> >> +// CHECK-NEXT: <string>Returned allocated memory</string> >> +// CHECK-NEXT: <key>message</key> >> +// CHECK-NEXT: <string>Returned allocated memory</string> >> +// CHECK-NEXT: </dict> >> +// CHECK-NEXT: <dict> >> // CHECK-NEXT: <key>kind</key><string>control</string> >> // CHECK-NEXT: <key>edges</key> >> // CHECK-NEXT: <array> >> @@ -87,25 +230,25 @@ void test() { >> // CHECK-NEXT: <key>start</key> >> // CHECK-NEXT: <array> >> // CHECK-NEXT: <dict> >> -// CHECK-NEXT: <key>line</key><integer>7</integer> >> +// CHECK-NEXT: <key>line</key><integer>10</integer> >> // CHECK-NEXT: <key>col</key><integer>12</integer> >> // CHECK-NEXT: <key>file</key><integer>0</integer> >> // CHECK-NEXT: </dict> >> // CHECK-NEXT: <dict> >> -// CHECK-NEXT: <key>line</key><integer>7</integer> >> -// CHECK-NEXT: <key>col</key><integer>14</integer> >> +// CHECK-NEXT: <key>line</key><integer>10</integer> >> +// CHECK-NEXT: <key>col</key><integer>24</integer> >> // CHECK-NEXT: <key>file</key><integer>0</integer> >> // CHECK-NEXT: </dict> >> // CHECK-NEXT: </array> >> // CHECK-NEXT: <key>end</key> >> // CHECK-NEXT: <array> >> // CHECK-NEXT: <dict> >> -// CHECK-NEXT: <key>line</key><integer>10</integer> >> +// CHECK-NEXT: <key>line</key><integer>13</integer> >> // CHECK-NEXT: <key>col</key><integer>3</integer> >> // CHECK-NEXT: <key>file</key><integer>0</integer> >> // CHECK-NEXT: </dict> >> // CHECK-NEXT: <dict> >> -// CHECK-NEXT: <key>line</key><integer>10</integer> >> +// CHECK-NEXT: <key>line</key><integer>13</integer> >> // CHECK-NEXT: <key>col</key><integer>8</integer> >> // CHECK-NEXT: <key>file</key><integer>0</integer> >> // CHECK-NEXT: </dict> >> @@ -117,7 +260,7 @@ void test() { >> // CHECK-NEXT: <key>kind</key><string>event</string> >> // CHECK-NEXT: <key>location</key> >> // CHECK-NEXT: <dict> >> -// CHECK-NEXT: <key>line</key><integer>10</integer> >> +// CHECK-NEXT: <key>line</key><integer>13</integer> >> // CHECK-NEXT: <key>col</key><integer>3</integer> >> // CHECK-NEXT: <key>file</key><integer>0</integer> >> // CHECK-NEXT: </dict> >> @@ -125,12 +268,12 @@ void test() { >> // CHECK-NEXT: <array> >> // CHECK-NEXT: <array> >> // CHECK-NEXT: <dict> >> -// CHECK-NEXT: <key>line</key><integer>10</integer> >> +// CHECK-NEXT: <key>line</key><integer>13</integer> >> // CHECK-NEXT: <key>col</key><integer>10</integer> >> // CHECK-NEXT: <key>file</key><integer>0</integer> >> // CHECK-NEXT: </dict> >> // CHECK-NEXT: <dict> >> -// CHECK-NEXT: <key>line</key><integer>10</integer> >> +// CHECK-NEXT: <key>line</key><integer>13</integer> >> // CHECK-NEXT: <key>col</key><integer>10</integer> >> // CHECK-NEXT: <key>file</key><integer>0</integer> >> // CHECK-NEXT: </dict> >> @@ -152,7 +295,7 @@ void test() { >> // CHECK-NEXT: <key>issue_hash</key><string>4</string> >> // CHECK-NEXT: <key>location</key> >> // CHECK-NEXT: <dict> >> -// CHECK-NEXT: <key>line</key><integer>10</integer> >> +// CHECK-NEXT: <key>line</key><integer>13</integer> >> // CHECK-NEXT: <key>col</key><integer>3</integer> >> // CHECK-NEXT: <key>file</key><integer>0</integer> >> // CHECK-NEXT: </dict> >> >> Modified: cfe/trunk/test/CodeGenCXX/new.cpp >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/new.cpp?rev=237608&r1=237607&r2=237608&view=diff >> >> ============================================================================== >> --- cfe/trunk/test/CodeGenCXX/new.cpp (original) >> +++ cfe/trunk/test/CodeGenCXX/new.cpp Mon May 18 14:59:11 2015 >> @@ -321,14 +321,14 @@ namespace N3664 { >> // CHECK-LABEL: define void @_ZN5N36641fEv >> void f() { >> // CHECK: call noalias i8* @_Znwm(i64 4) [[ATTR_BUILTIN_NEW:#[^ ]*]] >> - int *p = new int; >> + int *p = new int; // expected-note {{allocated with 'new' here}} >> // CHECK: call void @_ZdlPv({{.*}}) [[ATTR_BUILTIN_DELETE:#[^ ]*]] >> delete p; >> >> // CHECK: call noalias i8* @_Znam(i64 12) [[ATTR_BUILTIN_NEW]] >> int *q = new int[3]; >> // CHECK: call void @_ZdaPv({{.*}}) [[ATTR_BUILTIN_DELETE]] >> - delete [] p; >> + delete[] p; // expected-warning {{'delete[]' applied to a pointer >> that was allocated with 'new'; did you mean 'delete'?}} >> >> // CHECK: call i8* @_ZnamRKSt9nothrow_t(i64 3, {{.*}}) >> [[ATTR_BUILTIN_NOTHROW_NEW:#[^ ]*]] >> (void) new (nothrow) S[3]; >> >> Added: cfe/trunk/test/SemaCXX/delete-mismatch.h >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/delete-mismatch.h?rev=237608&view=auto >> >> ============================================================================== >> --- cfe/trunk/test/SemaCXX/delete-mismatch.h (added) >> +++ cfe/trunk/test/SemaCXX/delete-mismatch.h Mon May 18 14:59:11 2015 >> @@ -0,0 +1,15 @@ >> +// Header for PCH test delete.cpp >> +namespace pch_test { >> +struct X { >> + int *a; >> + X(); >> + X(int); >> + X(bool) >> + : a(new int[1]) { } // expected-note{{allocated with 'new[]' here}} >> + ~X() >> + { >> + delete a; // expected-warning{{'delete' applied to a pointer that was >> allocated with 'new[]'; did you mean 'delete[]'?}} >> + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:9-[[@LINE-1]]:9}:"[]" >> + } >> +}; >> +} >> >> Modified: cfe/trunk/test/SemaCXX/delete.cpp >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/delete.cpp?rev=237608&r1=237607&r2=237608&view=diff >> >> ============================================================================== >> --- cfe/trunk/test/SemaCXX/delete.cpp (original) >> +++ cfe/trunk/test/SemaCXX/delete.cpp Mon May 18 14:59:11 2015 >> @@ -1,9 +1,130 @@ >> -// RUN: %clang_cc1 -fsyntax-only -verify %s >> -// RUN: cp %s %t >> -// RUN: %clang_cc1 -fixit -x c++ %t >> -// RUN: %clang_cc1 -E -o - %t | FileCheck %s >> +// Test without PCH >> +// RUN: %clang_cc1 -fsyntax-only -include %S/delete-mismatch.h >> -fdiagnostics-parseable-fixits -std=c++11 %s 2>&1 | FileCheck %s >> + >> +// Test with PCH >> +// RUN: %clang_cc1 -x c++-header -std=c++11 -emit-pch -o %t >> %S/delete-mismatch.h >> +// RUN: %clang_cc1 -std=c++11 -include-pch %t -DWITH_PCH -fsyntax-only >> -verify %s -ast-dump >> >> void f(int a[10][20]) { >> - // CHECK: delete[] a; >> delete a; // expected-warning {{'delete' applied to a pointer-to-array >> type}} >> + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:9-[[@LINE-1]]:9}:"[]" >> +} >> +namespace MemberCheck { >> +struct S { >> + int *a = new int[5]; // expected-note4 {{allocated with 'new[]' here}} >> + int *b; >> + int *c; >> + static int *d; >> + S(); >> + S(int); >> + ~S() { >> + delete a; // expected-warning {{'delete' applied to a pointer that >> was allocated with 'new[]'; did you mean 'delete[]'?}} >> + delete b; // expected-warning {{'delete' applied to a pointer that >> was allocated with 'new[]'; did you mean 'delete[]'?}} >> + delete[] c; // expected-warning {{'delete[]' applied to a pointer >> that was allocated with 'new'; did you mean 'delete'?}} >> + } >> + void f(); >> +}; >> + >> +void S::f() >> +{ >> + delete a; // expected-warning {{'delete' applied to a pointer that was >> allocated with 'new[]'; did you mean 'delete[]'?}} >> + delete b; // expected-warning {{'delete' applied to a pointer that was >> allocated with 'new[]'; did you mean 'delete[]'?}} >> +} >> + >> +S::S() >> +: b(new int[1]), c(new int) {} // expected-note3 {{allocated with 'new[]' >> here}} >> +// expected-note@-1 {{allocated with 'new' here}} >> + >> +S::S(int i) >> +: b(new int[i]), c(new int) {} // expected-note3 {{allocated with 'new[]' >> here}} >> +// expected-note@-1 {{allocated with 'new' here}} >> + >> +struct S2 : S { >> + ~S2() { >> + delete a; // expected-warning {{'delete' applied to a pointer that >> was allocated with 'new[]'; did you mean 'delete[]'?}} >> + } >> +}; >> +int *S::d = new int[42]; // expected-note {{allocated with 'new[]' here}} >> +void f(S *s) { >> + int *a = new int[1]; // expected-note {{allocated with 'new[]' here}} >> + delete a; // expected-warning {{'delete' applied to a pointer that was >> allocated with 'new[]'; did you mean 'delete[]'?}} >> + delete s->a; // expected-warning {{'delete' applied to a pointer that >> was allocated with 'new[]'; did you mean 'delete[]'?}} >> + delete s->b; // expected-warning {{'delete' applied to a pointer that >> was allocated with 'new[]'; did you mean 'delete[]'?}} >> + delete s->c; >> + delete s->d; >> + delete S::d; // expected-warning {{'delete' applied to a pointer that >> was allocated with 'new[]'; did you mean 'delete[]'?}} >> +} >> + >> +// At least one constructor initializes field with matching form of >> 'new'. >> +struct MatchingNewIsOK { >> + int *p; >> + bool is_array_; >> + MatchingNewIsOK() : p{new int}, is_array_(false) {} >> + explicit MatchingNewIsOK(unsigned c) : p{new int[c]}, is_array_(true) >> {} >> + ~MatchingNewIsOK() { >> + if (is_array_) >> + delete[] p; >> + else >> + delete p; >> + } >> +}; >> + >> +// At least one constructor's body is missing; no proof of mismatch. >> +struct CantProve_MissingCtorDefinition { >> + int *p; >> + CantProve_MissingCtorDefinition(); >> + CantProve_MissingCtorDefinition(int); >> + ~CantProve_MissingCtorDefinition(); >> +}; >> + >> +CantProve_MissingCtorDefinition::CantProve_MissingCtorDefinition() >> + : p(new int) >> +{ } >> + >> +CantProve_MissingCtorDefinition::~CantProve_MissingCtorDefinition() >> +{ >> + delete[] p; >> +} >> + >> +struct base {}; >> +struct derived : base {}; >> +struct InitList { >> + base *p, *p2 = nullptr, *p3{nullptr}, *p4; >> + InitList(unsigned c) : p(new derived[c]), p4(nullptr) {} // >> expected-note {{allocated with 'new[]' here}} >> + InitList(unsigned c, unsigned) : p{new derived[c]}, p4{nullptr} {} // >> expected-note {{allocated with 'new[]' here}} >> + ~InitList() { >> + delete p; // expected-warning {{'delete' applied to a pointer that >> was allocated with 'new[]'; did you mean 'delete[]'?}} >> + delete [] p; >> + delete p2; >> + delete [] p3; >> + delete p4; >> + } >> +}; >> +} >> + >> +namespace NonMemberCheck { >> +#define DELETE_ARRAY(x) delete[] (x) >> +#define DELETE(x) delete (x) >> +void f() { >> + int *a = new int(5); // expected-note2 {{allocated with 'new' here}} >> + delete[] a; // expected-warning {{'delete[]' applied to a >> pointer that was allocated with 'new'; did you mean 'delete'?}} >> + int *b = new int; >> + delete b; >> + int *c{new int}; // expected-note {{allocated with 'new' here}} >> + int *d{new int[1]}; // expected-note2 {{allocated with 'new[]' here}} >> + delete [ ] c; // expected-warning {{'delete[]' applied to a >> pointer that was allocated with 'new'; did you mean 'delete'?}} >> + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:9-[[@LINE-1]]:17}:"" >> + delete d; // expected-warning {{'delete' applied to a pointer >> that was allocated with 'new[]'; did you mean 'delete[]'?}} >> + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:9-[[@LINE-1]]:9}:"[]" >> + DELETE_ARRAY(a); // expected-warning {{'delete[]' applied to a >> pointer that was allocated with 'new'; did you mean 'delete'?}} >> + DELETE(d); // expected-warning {{'delete' applied to a pointer >> that was allocated with 'new[]'; did you mean 'delete[]'?}} >> +} >> } >> +#ifndef WITH_PCH >> +pch_test::X::X() >> + : a(new int[1]) // expected-note{{allocated with 'new[]' here}} >> +{ } >> +pch_test::X::X(int i) >> + : a(new int[i]) // expected-note{{allocated with 'new[]' here}} >> +{ } >> +#endif >> >> >> _______________________________________________ >> cfe-commits mailing list >> [email protected] >> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits > > _______________________________________________ cfe-commits mailing list [email protected] http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
