On Mon, May 18, 2015 at 11:12 PM, Ismail Pazarbasi <[email protected]> wrote: > 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. I have added LLVM_ENABLE_WERROR to my build config, and currently rebuilding.
Perhaps a cfe-dev discussion, but let me try here first; would it make sense to set LLVM_ENABLE_WERROR on by default? > >> >> 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
