On 18 May 2015 at 12:59, 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. > FYI, I'm getting an assertion firing: clang: llvm/tools/clang/lib/Sema/SemaExprCXX.cpp:2447: const clang::CXXNewExpr *(anonymous namespace)::MismatchingNewDeleteDetector::getNewExprFromInitListOrExpr(const clang::Expr *): Assertion `E != nullptr && "Expected a valid initializer expression"' failed. on presumed to be valid code (code that builds with a previous version of clang). I'm creduce'ing a testcase now. 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
