Revert "Detect uses of mismatching forms of 'new' and 'delete'"
This reverts commit 742dc9b6c9686ab52860b7da39c3a126d8a97fbc. This is generating multiple segfaults in our internal builds. Test case coming up shortly. llvm-svn: 237391
This commit is contained in:
parent
4f0f708bf1
commit
c324b92c35
|
@ -5510,12 +5510,7 @@ def err_delete_explicit_conversion : Error<
|
||||||
"conversion function">;
|
"conversion function">;
|
||||||
def note_delete_conversion : Note<"conversion to pointer type %0">;
|
def note_delete_conversion : Note<"conversion to pointer type %0">;
|
||||||
def warn_delete_array_type : Warning<
|
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<
|
def err_no_suitable_delete_member_function_found : Error<
|
||||||
"no suitable member %0 in %1">;
|
"no suitable member %0 in %1">;
|
||||||
def err_ambiguous_suitable_delete_member_function_found : Error<
|
def err_ambiguous_suitable_delete_member_function_found : Error<
|
||||||
|
|
|
@ -27,7 +27,6 @@ template <class T, unsigned n> class SmallSetVector;
|
||||||
namespace clang {
|
namespace clang {
|
||||||
|
|
||||||
class CXXConstructorDecl;
|
class CXXConstructorDecl;
|
||||||
class CXXDeleteExpr;
|
|
||||||
class CXXRecordDecl;
|
class CXXRecordDecl;
|
||||||
class DeclaratorDecl;
|
class DeclaratorDecl;
|
||||||
class LookupResult;
|
class LookupResult;
|
||||||
|
@ -80,9 +79,6 @@ public:
|
||||||
virtual void ReadUndefinedButUsed(
|
virtual void ReadUndefinedButUsed(
|
||||||
llvm::DenseMap<NamedDecl*, SourceLocation> &Undefined);
|
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
|
/// \brief Do last resort, unqualified lookup on a LookupResult that
|
||||||
/// Sema cannot find.
|
/// Sema cannot find.
|
||||||
///
|
///
|
||||||
|
|
|
@ -230,10 +230,6 @@ public:
|
||||||
void ReadUndefinedButUsed(
|
void ReadUndefinedButUsed(
|
||||||
llvm::DenseMap<NamedDecl*, SourceLocation> &Undefined) override;
|
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
|
/// \brief Do last resort, unqualified lookup on a LookupResult that
|
||||||
/// Sema cannot find.
|
/// Sema cannot find.
|
||||||
///
|
///
|
||||||
|
|
|
@ -78,7 +78,6 @@ namespace clang {
|
||||||
typedef SmallVector<CXXBaseSpecifier*, 4> CXXCastPath;
|
typedef SmallVector<CXXBaseSpecifier*, 4> CXXCastPath;
|
||||||
class CXXConstructorDecl;
|
class CXXConstructorDecl;
|
||||||
class CXXConversionDecl;
|
class CXXConversionDecl;
|
||||||
class CXXDeleteExpr;
|
|
||||||
class CXXDestructorDecl;
|
class CXXDestructorDecl;
|
||||||
class CXXFieldCollector;
|
class CXXFieldCollector;
|
||||||
class CXXMemberCallExpr;
|
class CXXMemberCallExpr;
|
||||||
|
@ -403,15 +402,6 @@ public:
|
||||||
llvm::SmallSetVector<const TypedefNameDecl *, 4>
|
llvm::SmallSetVector<const TypedefNameDecl *, 4>
|
||||||
UnusedLocalTypedefNameCandidates;
|
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;
|
typedef llvm::SmallPtrSet<const CXXRecordDecl*, 8> RecordDeclSetTy;
|
||||||
|
|
||||||
/// PureVirtualClassDiagSet - a set of class declarations which we have
|
/// PureVirtualClassDiagSet - a set of class declarations which we have
|
||||||
|
@ -896,11 +886,6 @@ public:
|
||||||
void getUndefinedButUsed(
|
void getUndefinedButUsed(
|
||||||
SmallVectorImpl<std::pair<NamedDecl *, SourceLocation> > &Undefined);
|
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 std::pair<ObjCMethodList, ObjCMethodList> GlobalMethods;
|
||||||
typedef llvm::DenseMap<Selector, GlobalMethods> GlobalMethodPool;
|
typedef llvm::DenseMap<Selector, GlobalMethods> GlobalMethodPool;
|
||||||
|
|
||||||
|
@ -8654,9 +8639,6 @@ private:
|
||||||
/// attempts to add itself into the container
|
/// attempts to add itself into the container
|
||||||
void CheckObjCCircularContainer(ObjCMessageExpr *Message);
|
void CheckObjCCircularContainer(ObjCMessageExpr *Message);
|
||||||
|
|
||||||
void AnalyzeDeleteExprMismatch(const CXXDeleteExpr *DE);
|
|
||||||
void AnalyzeDeleteExprMismatch(FieldDecl *Field, SourceLocation DeleteLoc,
|
|
||||||
bool DeleteWasArrayForm);
|
|
||||||
public:
|
public:
|
||||||
/// \brief Register a magic integral constant to be used as a type tag.
|
/// \brief Register a magic integral constant to be used as a type tag.
|
||||||
void RegisterTypeTagForDatatype(const IdentifierInfo *ArgumentKind,
|
void RegisterTypeTagForDatatype(const IdentifierInfo *ArgumentKind,
|
||||||
|
|
|
@ -561,9 +561,6 @@ namespace clang {
|
||||||
/// \brief Record code for the table of offsets to CXXCtorInitializers
|
/// \brief Record code for the table of offsets to CXXCtorInitializers
|
||||||
/// lists.
|
/// lists.
|
||||||
CXX_CTOR_INITIALIZERS_OFFSETS = 53,
|
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.
|
/// \brief Record types used within a source manager block.
|
||||||
|
|
|
@ -756,9 +756,6 @@ private:
|
||||||
/// SourceLocation of a matching ODR-use.
|
/// SourceLocation of a matching ODR-use.
|
||||||
SmallVector<uint64_t, 8> UndefinedButUsed;
|
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.
|
// \brief A list of late parsed template function data.
|
||||||
SmallVector<uint64_t, 1> LateParsedTemplates;
|
SmallVector<uint64_t, 1> LateParsedTemplates;
|
||||||
|
|
||||||
|
@ -1739,10 +1736,6 @@ public:
|
||||||
void ReadUndefinedButUsed(
|
void ReadUndefinedButUsed(
|
||||||
llvm::DenseMap<NamedDecl *, SourceLocation> &Undefined) override;
|
llvm::DenseMap<NamedDecl *, SourceLocation> &Undefined) override;
|
||||||
|
|
||||||
void ReadMismatchingDeleteExpressions(llvm::MapVector<
|
|
||||||
FieldDecl *, llvm::SmallVector<std::pair<SourceLocation, bool>, 4>> &
|
|
||||||
Exprs);
|
|
||||||
|
|
||||||
void ReadTentativeDefinitions(
|
void ReadTentativeDefinitions(
|
||||||
SmallVectorImpl<VarDecl *> &TentativeDefs) override;
|
SmallVectorImpl<VarDecl *> &TentativeDefs) override;
|
||||||
|
|
||||||
|
|
|
@ -212,15 +212,7 @@ void MultiplexExternalSemaSource::ReadUndefinedButUsed(
|
||||||
for(size_t i = 0; i < Sources.size(); ++i)
|
for(size_t i = 0; i < Sources.size(); ++i)
|
||||||
Sources[i]->ReadUndefinedButUsed(Undefined);
|
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){
|
bool MultiplexExternalSemaSource::LookupUnqualified(LookupResult &R, Scope *S){
|
||||||
for(size_t i = 0; i < Sources.size(); ++i)
|
for(size_t i = 0; i < Sources.size(); ++i)
|
||||||
Sources[i]->LookupUnqualified(R, S);
|
Sources[i]->LookupUnqualified(R, S);
|
||||||
|
|
|
@ -860,17 +860,6 @@ 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
|
// 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
|
// variable. If we miss cases, then at best we have a performance issue and
|
||||||
// at worst a rejects-valid bug.
|
// at worst a rejects-valid bug.
|
||||||
|
@ -1230,9 +1219,6 @@ void ExternalSemaSource::ReadUndefinedButUsed(
|
||||||
llvm::DenseMap<NamedDecl *, SourceLocation> &Undefined) {
|
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 {
|
void PrettyDeclStackTraceEntry::print(raw_ostream &OS) const {
|
||||||
SourceLocation Loc = this->Loc;
|
SourceLocation Loc = this->Loc;
|
||||||
if (!Loc.isValid() && TheDecl) Loc = TheDecl->getLocation();
|
if (!Loc.isValid() && TheDecl) Loc = TheDecl->getLocation();
|
||||||
|
@ -1481,8 +1467,3 @@ CapturedRegionScopeInfo *Sema::getCurCapturedRegion() {
|
||||||
|
|
||||||
return dyn_cast<CapturedRegionScopeInfo>(FunctionScopes.back());
|
return dyn_cast<CapturedRegionScopeInfo>(FunctionScopes.back());
|
||||||
}
|
}
|
||||||
|
|
||||||
const llvm::MapVector<FieldDecl *, Sema::DeleteLocs> &
|
|
||||||
Sema::getMismatchingDeleteExpressions() const {
|
|
||||||
return DeleteExprs;
|
|
||||||
}
|
|
||||||
|
|
|
@ -2339,261 +2339,6 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD,
|
||||||
return false;
|
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<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:
|
/// ActOnCXXDelete - Parsed a C++ 'delete' expression (C++ 5.3.5), as in:
|
||||||
/// @code ::delete ptr; @endcode
|
/// @code ::delete ptr; @endcode
|
||||||
/// or
|
/// or
|
||||||
|
@ -2709,6 +2454,12 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
if (Pointee->isArrayType() && !ArrayForm) {
|
||||||
Diag(StartLoc, diag::warn_delete_array_type)
|
Diag(StartLoc, diag::warn_delete_array_type)
|
||||||
<< Type << Ex.get()->getSourceRange()
|
<< Type << Ex.get()->getSourceRange()
|
||||||
|
@ -2783,7 +2534,7 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
|
||||||
DeleteName);
|
DeleteName);
|
||||||
|
|
||||||
MarkFunctionReferenced(StartLoc, OperatorDelete);
|
MarkFunctionReferenced(StartLoc, OperatorDelete);
|
||||||
|
|
||||||
// Check access and ambiguity of operator delete and destructor.
|
// Check access and ambiguity of operator delete and destructor.
|
||||||
if (PointeeRD) {
|
if (PointeeRD) {
|
||||||
if (CXXDestructorDecl *Dtor = LookupDestructor(PointeeRD)) {
|
if (CXXDestructorDecl *Dtor = LookupDestructor(PointeeRD)) {
|
||||||
|
@ -2793,11 +2544,9 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CXXDeleteExpr *Result = new (Context) CXXDeleteExpr(
|
return new (Context) CXXDeleteExpr(
|
||||||
Context.VoidTy, UseGlobal, ArrayForm, ArrayFormAsWritten,
|
Context.VoidTy, UseGlobal, ArrayForm, ArrayFormAsWritten,
|
||||||
UsualArrayDeleteWantsSize, OperatorDelete, Ex.get(), StartLoc);
|
UsualArrayDeleteWantsSize, OperatorDelete, Ex.get(), StartLoc);
|
||||||
AnalyzeDeleteExprMismatch(Result);
|
|
||||||
return Result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Check the use of the given variable as a C++ condition in an if,
|
/// \brief Check the use of the given variable as a C++ condition in an if,
|
||||||
|
|
|
@ -3021,18 +3021,6 @@ ASTReader::ReadASTBlock(ModuleFile &F, unsigned ClientLoadCapabilities) {
|
||||||
ReadSourceLocation(F, Record, I).getRawEncoding());
|
ReadSourceLocation(F, Record, I).getRawEncoding());
|
||||||
}
|
}
|
||||||
break;
|
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: {
|
case IMPORTED_MODULES: {
|
||||||
if (F.Kind != MK_ImplicitModule && F.Kind != MK_ExplicitModule) {
|
if (F.Kind != MK_ImplicitModule && F.Kind != MK_ExplicitModule) {
|
||||||
|
@ -7025,21 +7013,6 @@ 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(
|
void ASTReader::ReadTentativeDefinitions(
|
||||||
SmallVectorImpl<VarDecl *> &TentativeDefs) {
|
SmallVectorImpl<VarDecl *> &TentativeDefs) {
|
||||||
for (unsigned I = 0, N = TentativeDefinitions.size(); I != N; ++I) {
|
for (unsigned I = 0, N = TentativeDefinitions.size(); I != N; ++I) {
|
||||||
|
|
|
@ -4155,20 +4155,6 @@ void ASTWriter::WriteASTCore(Sema &SemaRef,
|
||||||
AddSourceLocation(I->second, UndefinedButUsed);
|
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
|
// Write the control block
|
||||||
WriteControlBlock(PP, Context, isysroot, OutputFile);
|
WriteControlBlock(PP, Context, isysroot, OutputFile);
|
||||||
|
|
||||||
|
@ -4438,10 +4424,7 @@ void ASTWriter::WriteASTCore(Sema &SemaRef,
|
||||||
// Write the undefined internal functions and variables, and inline functions.
|
// Write the undefined internal functions and variables, and inline functions.
|
||||||
if (!UndefinedButUsed.empty())
|
if (!UndefinedButUsed.empty())
|
||||||
Stream.EmitRecord(UNDEFINED_BUT_USED, UndefinedButUsed);
|
Stream.EmitRecord(UNDEFINED_BUT_USED, UndefinedButUsed);
|
||||||
|
|
||||||
if (!DeleteExprsToAnalyze.empty())
|
|
||||||
Stream.EmitRecord(DELETE_EXPRS_TO_ANALYZE, DeleteExprsToAnalyze);
|
|
||||||
|
|
||||||
// Write the visible updates to DeclContexts.
|
// Write the visible updates to DeclContexts.
|
||||||
for (auto *DC : UpdatedDeclContexts)
|
for (auto *DC : UpdatedDeclContexts)
|
||||||
WriteDeclContextVisibleUpdate(DC);
|
WriteDeclContextVisibleUpdate(DC);
|
||||||
|
|
|
@ -97,11 +97,9 @@ void testShouldReportDoubleFreeNotMismatched() {
|
||||||
free(p);
|
free(p);
|
||||||
delete globalPtr; // expected-warning {{Attempt to free released memory}}
|
delete globalPtr; // expected-warning {{Attempt to free released memory}}
|
||||||
}
|
}
|
||||||
int *allocIntArray(unsigned c) {
|
|
||||||
return new int[c];
|
|
||||||
}
|
|
||||||
void testMismatchedChangePointeeThroughAssignment() {
|
void testMismatchedChangePointeeThroughAssignment() {
|
||||||
int *arr = allocIntArray(4);
|
int *arr = new int[4];
|
||||||
globalPtr = arr;
|
globalPtr = arr;
|
||||||
delete arr; // expected-warning{{Memory allocated by 'new[]' should be deallocated by 'delete[]', not 'delete'}}
|
delete arr; // expected-warning{{Memory allocated by 'new[]' should be deallocated by 'delete[]', not 'delete'}}
|
||||||
}
|
}
|
|
@ -95,11 +95,8 @@ void testNew6() {
|
||||||
realloc(p, sizeof(long)); // expected-warning{{Memory allocated by 'new[]' should be deallocated by 'delete[]', not realloc()}}
|
realloc(p, sizeof(long)); // expected-warning{{Memory allocated by 'new[]' should be deallocated by 'delete[]', not realloc()}}
|
||||||
}
|
}
|
||||||
|
|
||||||
int *allocInt() {
|
|
||||||
return new int;
|
|
||||||
}
|
|
||||||
void testNew7() {
|
void testNew7() {
|
||||||
int *p = allocInt();
|
int *p = new int;
|
||||||
delete[] p; // expected-warning{{Memory allocated by 'new' should be deallocated by 'delete', not 'delete[]'}}
|
delete[] p; // expected-warning{{Memory allocated by 'new' should be deallocated by 'delete', not 'delete[]'}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,12 +105,8 @@ void testNew8() {
|
||||||
delete[] p; // expected-warning{{Memory allocated by operator new should be deallocated by 'delete', not 'delete[]'}}
|
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() {
|
void testNew9() {
|
||||||
int *p = allocIntArray(1);
|
int *p = new int[1];
|
||||||
delete p; // expected-warning{{Memory allocated by 'new[]' should be deallocated by 'delete[]', not 'delete'}}
|
delete p; // expected-warning{{Memory allocated by 'new[]' should be deallocated by 'delete[]', not 'delete'}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,9 @@
|
||||||
// RUN: FileCheck --input-file=%t.plist %s
|
// RUN: FileCheck --input-file=%t.plist %s
|
||||||
|
|
||||||
void changePointee(int *p);
|
void changePointee(int *p);
|
||||||
int *allocIntArray(unsigned c) {
|
|
||||||
return new int[c]; // expected-note {{Memory is allocated}}
|
|
||||||
}
|
|
||||||
void test() {
|
void test() {
|
||||||
int *p = allocIntArray(1); // expected-note {{Calling 'allocIntArray'}}
|
int *p = new int[1];
|
||||||
// expected-note@-1 {{Returned allocated memory}}
|
// expected-note@-1 {{Memory is allocated}}
|
||||||
changePointee(p);
|
changePointee(p);
|
||||||
delete p; // expected-warning {{Memory allocated by 'new[]' should be deallocated by 'delete[]', not 'delete'}}
|
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'}}
|
// expected-note@-1 {{Memory allocated by 'new[]' should be deallocated by 'delete[]', not 'delete'}}
|
||||||
|
@ -27,12 +24,12 @@ void test() {
|
||||||
// CHECK-NEXT: <key>start</key>
|
// CHECK-NEXT: <key>start</key>
|
||||||
// CHECK-NEXT: <array>
|
// CHECK-NEXT: <array>
|
||||||
// CHECK-NEXT: <dict>
|
// CHECK-NEXT: <dict>
|
||||||
// CHECK-NEXT: <key>line</key><integer>10</integer>
|
// CHECK-NEXT: <key>line</key><integer>7</integer>
|
||||||
// CHECK-NEXT: <key>col</key><integer>3</integer>
|
// CHECK-NEXT: <key>col</key><integer>3</integer>
|
||||||
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
||||||
// CHECK-NEXT: </dict>
|
// CHECK-NEXT: </dict>
|
||||||
// CHECK-NEXT: <dict>
|
// CHECK-NEXT: <dict>
|
||||||
// CHECK-NEXT: <key>line</key><integer>10</integer>
|
// CHECK-NEXT: <key>line</key><integer>7</integer>
|
||||||
// CHECK-NEXT: <key>col</key><integer>5</integer>
|
// CHECK-NEXT: <key>col</key><integer>5</integer>
|
||||||
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
||||||
// CHECK-NEXT: </dict>
|
// CHECK-NEXT: </dict>
|
||||||
|
@ -40,126 +37,15 @@ void test() {
|
||||||
// CHECK-NEXT: <key>end</key>
|
// CHECK-NEXT: <key>end</key>
|
||||||
// CHECK-NEXT: <array>
|
// CHECK-NEXT: <array>
|
||||||
// CHECK-NEXT: <dict>
|
// 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>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>
|
|
||||||
// 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>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>line</key><integer>7</integer>
|
||||||
// CHECK-NEXT: <key>col</key><integer>12</integer>
|
// CHECK-NEXT: <key>col</key><integer>12</integer>
|
||||||
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
||||||
// CHECK-NEXT: </dict>
|
// 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>file</key><integer>0</integer>
|
||||||
|
// CHECK-NEXT: </dict>
|
||||||
// CHECK-NEXT: </array>
|
// CHECK-NEXT: </array>
|
||||||
// CHECK-NEXT: </dict>
|
// CHECK-NEXT: </dict>
|
||||||
// CHECK-NEXT: </array>
|
// CHECK-NEXT: </array>
|
||||||
|
@ -169,35 +55,6 @@ void test() {
|
||||||
// CHECK-NEXT: <key>location</key>
|
// CHECK-NEXT: <key>location</key>
|
||||||
// CHECK-NEXT: <dict>
|
// CHECK-NEXT: <dict>
|
||||||
// CHECK-NEXT: <key>line</key><integer>7</integer>
|
// CHECK-NEXT: <key>line</key><integer>7</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>
|
|
||||||
// CHECK-NEXT: <array>
|
|
||||||
// CHECK-NEXT: <array>
|
|
||||||
// CHECK-NEXT: <dict>
|
|
||||||
// CHECK-NEXT: <key>line</key><integer>7</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>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>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>col</key><integer>12</integer>
|
||||||
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
||||||
// CHECK-NEXT: </dict>
|
// CHECK-NEXT: </dict>
|
||||||
|
@ -205,22 +62,22 @@ void test() {
|
||||||
// CHECK-NEXT: <array>
|
// CHECK-NEXT: <array>
|
||||||
// CHECK-NEXT: <array>
|
// CHECK-NEXT: <array>
|
||||||
// CHECK-NEXT: <dict>
|
// CHECK-NEXT: <dict>
|
||||||
// CHECK-NEXT: <key>line</key><integer>10</integer>
|
// CHECK-NEXT: <key>line</key><integer>7</integer>
|
||||||
// CHECK-NEXT: <key>col</key><integer>12</integer>
|
// CHECK-NEXT: <key>col</key><integer>12</integer>
|
||||||
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
||||||
// CHECK-NEXT: </dict>
|
// CHECK-NEXT: </dict>
|
||||||
// CHECK-NEXT: <dict>
|
// CHECK-NEXT: <dict>
|
||||||
// CHECK-NEXT: <key>line</key><integer>10</integer>
|
// CHECK-NEXT: <key>line</key><integer>7</integer>
|
||||||
// CHECK-NEXT: <key>col</key><integer>27</integer>
|
// CHECK-NEXT: <key>col</key><integer>21</integer>
|
||||||
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
||||||
// CHECK-NEXT: </dict>
|
// CHECK-NEXT: </dict>
|
||||||
// CHECK-NEXT: </array>
|
// CHECK-NEXT: </array>
|
||||||
// CHECK-NEXT: </array>
|
// CHECK-NEXT: </array>
|
||||||
// CHECK-NEXT: <key>depth</key><integer>0</integer>
|
// CHECK-NEXT: <key>depth</key><integer>0</integer>
|
||||||
// CHECK-NEXT: <key>extended_message</key>
|
// CHECK-NEXT: <key>extended_message</key>
|
||||||
// CHECK-NEXT: <string>Returned allocated memory</string>
|
// CHECK-NEXT: <string>Memory is allocated</string>
|
||||||
// CHECK-NEXT: <key>message</key>
|
// CHECK-NEXT: <key>message</key>
|
||||||
// CHECK-NEXT: <string>Returned allocated memory</string>
|
// CHECK-NEXT: <string>Memory is allocated</string>
|
||||||
// CHECK-NEXT: </dict>
|
// CHECK-NEXT: </dict>
|
||||||
// CHECK-NEXT: <dict>
|
// CHECK-NEXT: <dict>
|
||||||
// CHECK-NEXT: <key>kind</key><string>control</string>
|
// CHECK-NEXT: <key>kind</key><string>control</string>
|
||||||
|
@ -230,25 +87,25 @@ void test() {
|
||||||
// CHECK-NEXT: <key>start</key>
|
// CHECK-NEXT: <key>start</key>
|
||||||
// CHECK-NEXT: <array>
|
// CHECK-NEXT: <array>
|
||||||
// CHECK-NEXT: <dict>
|
// CHECK-NEXT: <dict>
|
||||||
// CHECK-NEXT: <key>line</key><integer>10</integer>
|
// CHECK-NEXT: <key>line</key><integer>7</integer>
|
||||||
// CHECK-NEXT: <key>col</key><integer>12</integer>
|
// CHECK-NEXT: <key>col</key><integer>12</integer>
|
||||||
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
||||||
// CHECK-NEXT: </dict>
|
// CHECK-NEXT: </dict>
|
||||||
// CHECK-NEXT: <dict>
|
// CHECK-NEXT: <dict>
|
||||||
// CHECK-NEXT: <key>line</key><integer>10</integer>
|
// CHECK-NEXT: <key>line</key><integer>7</integer>
|
||||||
// CHECK-NEXT: <key>col</key><integer>24</integer>
|
// CHECK-NEXT: <key>col</key><integer>14</integer>
|
||||||
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
||||||
// CHECK-NEXT: </dict>
|
// CHECK-NEXT: </dict>
|
||||||
// CHECK-NEXT: </array>
|
// CHECK-NEXT: </array>
|
||||||
// CHECK-NEXT: <key>end</key>
|
// CHECK-NEXT: <key>end</key>
|
||||||
// CHECK-NEXT: <array>
|
// CHECK-NEXT: <array>
|
||||||
// CHECK-NEXT: <dict>
|
// CHECK-NEXT: <dict>
|
||||||
// CHECK-NEXT: <key>line</key><integer>13</integer>
|
// CHECK-NEXT: <key>line</key><integer>10</integer>
|
||||||
// CHECK-NEXT: <key>col</key><integer>3</integer>
|
// CHECK-NEXT: <key>col</key><integer>3</integer>
|
||||||
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
||||||
// CHECK-NEXT: </dict>
|
// CHECK-NEXT: </dict>
|
||||||
// CHECK-NEXT: <dict>
|
// CHECK-NEXT: <dict>
|
||||||
// CHECK-NEXT: <key>line</key><integer>13</integer>
|
// CHECK-NEXT: <key>line</key><integer>10</integer>
|
||||||
// CHECK-NEXT: <key>col</key><integer>8</integer>
|
// CHECK-NEXT: <key>col</key><integer>8</integer>
|
||||||
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
||||||
// CHECK-NEXT: </dict>
|
// CHECK-NEXT: </dict>
|
||||||
|
@ -260,7 +117,7 @@ void test() {
|
||||||
// CHECK-NEXT: <key>kind</key><string>event</string>
|
// CHECK-NEXT: <key>kind</key><string>event</string>
|
||||||
// CHECK-NEXT: <key>location</key>
|
// CHECK-NEXT: <key>location</key>
|
||||||
// CHECK-NEXT: <dict>
|
// CHECK-NEXT: <dict>
|
||||||
// CHECK-NEXT: <key>line</key><integer>13</integer>
|
// CHECK-NEXT: <key>line</key><integer>10</integer>
|
||||||
// CHECK-NEXT: <key>col</key><integer>3</integer>
|
// CHECK-NEXT: <key>col</key><integer>3</integer>
|
||||||
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
||||||
// CHECK-NEXT: </dict>
|
// CHECK-NEXT: </dict>
|
||||||
|
@ -268,12 +125,12 @@ void test() {
|
||||||
// CHECK-NEXT: <array>
|
// CHECK-NEXT: <array>
|
||||||
// CHECK-NEXT: <array>
|
// CHECK-NEXT: <array>
|
||||||
// CHECK-NEXT: <dict>
|
// CHECK-NEXT: <dict>
|
||||||
// CHECK-NEXT: <key>line</key><integer>13</integer>
|
// CHECK-NEXT: <key>line</key><integer>10</integer>
|
||||||
// CHECK-NEXT: <key>col</key><integer>10</integer>
|
// CHECK-NEXT: <key>col</key><integer>10</integer>
|
||||||
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
||||||
// CHECK-NEXT: </dict>
|
// CHECK-NEXT: </dict>
|
||||||
// CHECK-NEXT: <dict>
|
// CHECK-NEXT: <dict>
|
||||||
// CHECK-NEXT: <key>line</key><integer>13</integer>
|
// CHECK-NEXT: <key>line</key><integer>10</integer>
|
||||||
// CHECK-NEXT: <key>col</key><integer>10</integer>
|
// CHECK-NEXT: <key>col</key><integer>10</integer>
|
||||||
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
||||||
// CHECK-NEXT: </dict>
|
// CHECK-NEXT: </dict>
|
||||||
|
@ -295,7 +152,7 @@ void test() {
|
||||||
// CHECK-NEXT: <key>issue_hash</key><string>4</string>
|
// CHECK-NEXT: <key>issue_hash</key><string>4</string>
|
||||||
// CHECK-NEXT: <key>location</key>
|
// CHECK-NEXT: <key>location</key>
|
||||||
// CHECK-NEXT: <dict>
|
// CHECK-NEXT: <dict>
|
||||||
// CHECK-NEXT: <key>line</key><integer>13</integer>
|
// CHECK-NEXT: <key>line</key><integer>10</integer>
|
||||||
// CHECK-NEXT: <key>col</key><integer>3</integer>
|
// CHECK-NEXT: <key>col</key><integer>3</integer>
|
||||||
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
||||||
// CHECK-NEXT: </dict>
|
// CHECK-NEXT: </dict>
|
||||||
|
|
|
@ -321,14 +321,14 @@ namespace N3664 {
|
||||||
// CHECK-LABEL: define void @_ZN5N36641fEv
|
// CHECK-LABEL: define void @_ZN5N36641fEv
|
||||||
void f() {
|
void f() {
|
||||||
// CHECK: call noalias i8* @_Znwm(i64 4) [[ATTR_BUILTIN_NEW:#[^ ]*]]
|
// CHECK: call noalias i8* @_Znwm(i64 4) [[ATTR_BUILTIN_NEW:#[^ ]*]]
|
||||||
int *p = new int; // expected-note {{allocated with 'new' here}}
|
int *p = new int;
|
||||||
// CHECK: call void @_ZdlPv({{.*}}) [[ATTR_BUILTIN_DELETE:#[^ ]*]]
|
// CHECK: call void @_ZdlPv({{.*}}) [[ATTR_BUILTIN_DELETE:#[^ ]*]]
|
||||||
delete p;
|
delete p;
|
||||||
|
|
||||||
// CHECK: call noalias i8* @_Znam(i64 12) [[ATTR_BUILTIN_NEW]]
|
// CHECK: call noalias i8* @_Znam(i64 12) [[ATTR_BUILTIN_NEW]]
|
||||||
int *q = new int[3];
|
int *q = new int[3];
|
||||||
// CHECK: call void @_ZdaPv({{.*}}) [[ATTR_BUILTIN_DELETE]]
|
// CHECK: call void @_ZdaPv({{.*}}) [[ATTR_BUILTIN_DELETE]]
|
||||||
delete[] p; // expected-warning {{'delete[]' applied to a pointer that was allocated with 'new'; did you mean 'delete'?}}
|
delete [] p;
|
||||||
|
|
||||||
// CHECK: call i8* @_ZnamRKSt9nothrow_t(i64 3, {{.*}}) [[ATTR_BUILTIN_NOTHROW_NEW:#[^ ]*]]
|
// CHECK: call i8* @_ZnamRKSt9nothrow_t(i64 3, {{.*}}) [[ATTR_BUILTIN_NOTHROW_NEW:#[^ ]*]]
|
||||||
(void) new (nothrow) S[3];
|
(void) new (nothrow) S[3];
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
// 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}:"[]"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,129 +1,9 @@
|
||||||
// Test without PCH
|
// RUN: %clang_cc1 -fsyntax-only -verify %s
|
||||||
// RUN: %clang_cc1 -fsyntax-only -include %S/delete-mismatch.h -fdiagnostics-parseable-fixits -std=c++11 %s 2>&1 | FileCheck %s
|
// RUN: cp %s %t
|
||||||
|
// RUN: %clang_cc1 -fixit -x c++ %t
|
||||||
// Test with PCH
|
// RUN: %clang_cc1 -E -o - %t | FileCheck %s
|
||||||
// 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]) {
|
void f(int a[10][20]) {
|
||||||
|
// CHECK: delete[] a;
|
||||||
delete a; // expected-warning {{'delete' applied to a pointer-to-array type}}
|
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;
|
|
||||||
InitList() : p{new derived[1]} {} // expected-note {{allocated with 'new[]' here}}
|
|
||||||
explicit InitList(unsigned c) : p(new derived[c]) {} // expected-note {{allocated with 'new[]' here}}
|
|
||||||
InitList(unsigned c, unsigned) : p{new derived[c]} {} // expected-note {{allocated with 'new[]' here}}
|
|
||||||
InitList(const char *) : p{new derived[1]} {} // 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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
Loading…
Reference in New Issue