r237608 - Detect uses of mismatching forms of 'new' and 'delete'
Ismail Pazarbasi
ismail.pazarbasi at gmail.com
Mon May 18 14:12:13 PDT 2015
On Mon, May 18, 2015 at 10:57 PM, Richard Smith <richard at metafoo.co.uk> wrote:
> This has introduced a new warning:
>
> include/clang/Serialization/ASTReader.h:1746:8: warning:
> 'ReadMismatchingDeleteExpressions' overrides a member function but is not
> marked 'override' [-Winconsistent-missing-override]
> void ReadMismatchingDeleteExpressions(llvm::MapVector<
> ^
> include/clang/Sema/ExternalSemaSource.h:83:16: note: overridden virtual
> function is here
> virtual void ReadMismatchingDeleteExpressions(llvm::MapVector<
> ^
>
> Please can you fix this?
Yes, already landed in r237613.
>
> On Mon, May 18, 2015 at 12:59 PM, Ismail Pazarbasi
> <ismail.pazarbasi at gmail.com> 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 at -1 {{Memory is allocated}}
>> + int *p = allocIntArray(1); // expected-note {{Calling 'allocIntArray'}}
>> + // expected-note at -1 {{Returned allocated memory}}
>> changePointee(p);
>> delete p; // expected-warning {{Memory allocated by 'new[]' should be
>> deallocated by 'delete[]', not 'delete'}}
>> // expected-note at -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 at -1 {{allocated with 'new' here}}
>> +
>> +S::S(int i)
>> +: b(new int[i]), c(new int) {} // expected-note3 {{allocated with 'new[]'
>> here}}
>> +// expected-note at -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
>> cfe-commits at cs.uiuc.edu
>> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
>
>
More information about the cfe-commits
mailing list