Bug 1185044 - Unify type annotation logic between MOZ_*_CLASS and MOZ_MUST_USE; r=ehsan

This commit is contained in:
Michael Layzell 2015-07-20 19:43:12 -04:00 committed by Ehsan Akhgari
parent b2987686b9
commit ffe699b5dc
6 changed files with 220 additions and 230 deletions

View File

@ -56,7 +56,6 @@ private:
ScopeChecker(Scope scope_) :
scope(scope_) {}
virtual void run(const MatchFinder::MatchResult &Result);
void noteInferred(QualType T, DiagnosticsEngine &Diag);
private:
Scope scope;
};
@ -64,7 +63,6 @@ private:
class NonHeapClassChecker : public MatchFinder::MatchCallback {
public:
virtual void run(const MatchFinder::MatchResult &Result);
void noteInferred(QualType T, DiagnosticsEngine &Diag);
};
class ArithmeticArgChecker : public MatchFinder::MatchCallback {
@ -217,6 +215,51 @@ bool isInterestingDeclForImplicitConversion(const Decl *decl) {
}
class CustomTypeAnnotation {
enum ReasonKind {
RK_None,
RK_Direct,
RK_ArrayElement,
RK_BaseClass,
RK_Field,
};
struct AnnotationReason {
QualType Type;
ReasonKind Kind;
const FieldDecl *Field;
bool valid() const { return Kind != RK_None; }
};
typedef DenseMap<void *, AnnotationReason> ReasonCache;
const char *Spelling;
const char *Pretty;
ReasonCache Cache;
public:
CustomTypeAnnotation(const char *Spelling, const char *Pretty)
: Spelling(Spelling), Pretty(Pretty) {};
// Checks if this custom annotation "effectively affects" the given type.
bool hasEffectiveAnnotation(QualType T) {
return directAnnotationReason(T).valid();
}
void dumpAnnotationReason(DiagnosticsEngine &Diag, QualType T, SourceLocation Loc);
private:
bool hasLiteralAnnotation(QualType T) const;
AnnotationReason directAnnotationReason(QualType T);
};
static CustomTypeAnnotation StackClass =
CustomTypeAnnotation("moz_stack_class", "stack");
static CustomTypeAnnotation GlobalClass =
CustomTypeAnnotation("moz_global_class", "global");
static CustomTypeAnnotation NonHeapClass =
CustomTypeAnnotation("moz_nonheap_class", "non-heap");
static CustomTypeAnnotation MustUse =
CustomTypeAnnotation("moz_must_use", "must-use");
class MozChecker : public ASTConsumer, public RecursiveASTVisitor<MozChecker> {
DiagnosticsEngine &Diag;
const CompilerInstance &CI;
@ -248,18 +291,13 @@ public:
void HandleUnusedExprResult(const Stmt *stmt) {
const Expr* E = dyn_cast_or_null<Expr>(stmt);
if (E) {
// XXX It would be nice if we could use getAsTagDecl,
// but our version of clang is too old.
// (getAsTagDecl would also cover enums etc.)
QualType T = E->getType();
CXXRecordDecl *decl = T->getAsCXXRecordDecl();
if (decl) {
decl = decl->getDefinition();
if (decl && hasCustomAnnotation(decl, "moz_must_use")) {
unsigned errorID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Error, "Unused MOZ_MUST_USE value of type %0");
Diag.Report(E->getLocStart(), errorID) << T;
}
if (MustUse.hasEffectiveAnnotation(T)) {
unsigned errorID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Error, "Unused value of must-use type %0");
Diag.Report(E->getLocStart(), errorID) << T;
MustUse.dumpAnnotationReason(Diag, T, E->getLocStart());
}
}
}
@ -383,102 +421,6 @@ public:
}
};
/**
* Where classes may be allocated. Regular classes can be allocated anywhere,
* non-heap classes on the stack or as static variables, and stack classes only
* on the stack. Note that stack classes subsumes non-heap classes.
*/
enum ClassAllocationNature {
RegularClass = 0,
NonHeapClass = 1,
StackClass = 2,
GlobalClass = 3
};
/// A cached data of whether classes are stack classes, non-heap classes, or
/// neither.
DenseMap<const CXXRecordDecl *,
std::pair<const Decl *, ClassAllocationNature> > inferredAllocCauses;
ClassAllocationNature getClassAttrs(QualType T);
ClassAllocationNature getClassAttrs(CXXRecordDecl *D) {
// Normalize so that D points to the definition if it exists. If it doesn't,
// then we can't allocate it anyways.
if (!D->hasDefinition())
return RegularClass;
D = D->getDefinition();
// Base class: anyone with this annotation is obviously a stack class
if (MozChecker::hasCustomAnnotation(D, "moz_stack_class"))
return StackClass;
// Base class: anyone with this annotation is obviously a global class
if (MozChecker::hasCustomAnnotation(D, "moz_global_class"))
return GlobalClass;
// See if we cached the result.
DenseMap<const CXXRecordDecl *,
std::pair<const Decl *, ClassAllocationNature> >::iterator it =
inferredAllocCauses.find(D);
if (it != inferredAllocCauses.end()) {
return it->second.second;
}
// Continue looking, we might be a stack class yet. Even if we're a nonheap
// class, it might be possible that we've inferred to be a stack class.
ClassAllocationNature type = RegularClass;
if (MozChecker::hasCustomAnnotation(D, "moz_nonheap_class")) {
type = NonHeapClass;
}
inferredAllocCauses.insert(std::make_pair(D,
std::make_pair((const Decl *)0, type)));
// Look through all base cases to figure out if the parent is a stack class or
// a non-heap class. Since we might later infer to also be a stack class, keep
// going.
for (CXXRecordDecl::base_class_iterator base = D->bases_begin(),
e = D->bases_end(); base != e; ++base) {
ClassAllocationNature super = getClassAttrs(base->getType());
if (super == StackClass) {
inferredAllocCauses[D] = std::make_pair(
base->getType()->getAsCXXRecordDecl(), StackClass);
return StackClass;
} else if (super == GlobalClass) {
inferredAllocCauses[D] = std::make_pair(
base->getType()->getAsCXXRecordDecl(), GlobalClass);
return GlobalClass;
} else if (super == NonHeapClass) {
inferredAllocCauses[D] = std::make_pair(
base->getType()->getAsCXXRecordDecl(), NonHeapClass);
type = NonHeapClass;
}
}
// Maybe it has a member which is a stack class.
for (RecordDecl::field_iterator field = D->field_begin(), e = D->field_end();
field != e; ++field) {
ClassAllocationNature fieldType = getClassAttrs(field->getType());
if (fieldType == StackClass) {
inferredAllocCauses[D] = std::make_pair(*field, StackClass);
return StackClass;
} else if (fieldType == GlobalClass) {
inferredAllocCauses[D] = std::make_pair(*field, GlobalClass);
return GlobalClass;
} else if (fieldType == NonHeapClass) {
inferredAllocCauses[D] = std::make_pair(*field, NonHeapClass);
type = NonHeapClass;
}
}
return type;
}
ClassAllocationNature getClassAttrs(QualType T) {
while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
T = arrTy->getElementType();
CXXRecordDecl *clazz = T->getAsCXXRecordDecl();
return clazz ? getClassAttrs(clazz) : RegularClass;
}
/// A cached data of whether classes are refcounted or not.
typedef DenseMap<const CXXRecordDecl *,
std::pair<const Decl *, bool> > RefCountedMap;
@ -532,7 +474,7 @@ bool isClassRefCounted(QualType T) {
while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
T = arrTy->getElementType();
CXXRecordDecl *clazz = T->getAsCXXRecordDecl();
return clazz ? isClassRefCounted(clazz) : RegularClass;
return clazz ? isClassRefCounted(clazz) : false;
}
template<class T>
@ -553,19 +495,19 @@ namespace ast_matchers {
/// This matcher will match any class with the stack class assertion or an
/// array of such classes.
AST_MATCHER(QualType, stackClassAggregate) {
return getClassAttrs(Node) == StackClass;
return StackClass.hasEffectiveAnnotation(Node);
}
/// This matcher will match any class with the global class assertion or an
/// array of such classes.
AST_MATCHER(QualType, globalClassAggregate) {
return getClassAttrs(Node) == GlobalClass;
return GlobalClass.hasEffectiveAnnotation(Node);
}
/// This matcher will match any class with the stack class assertion or an
/// array of such classes.
AST_MATCHER(QualType, nonheapClassAggregate) {
return getClassAttrs(Node) == NonHeapClass;
return NonHeapClass.hasEffectiveAnnotation(Node);
}
/// This matcher will match any function declaration that is declared as a heap
@ -704,6 +646,103 @@ AST_POLYMORPHIC_MATCHER_P(equalsBoundNode,
namespace {
void CustomTypeAnnotation::dumpAnnotationReason(DiagnosticsEngine &Diag, QualType T, SourceLocation Loc) {
unsigned InheritsID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Note, "%1 is a %0 type because it inherits from a %0 type %2");
unsigned MemberID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Note, "%1 is a %0 type because member %2 is a %0 type %3");
unsigned ArrayID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Note, "%1 is a %0 type because it is an array of %0 type %2");
unsigned TemplID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Note, "%1 is a %0 type because it has a template argument %0 type %2");
AnnotationReason Reason = directAnnotationReason(T);
for (;;) {
switch (Reason.Kind) {
case RK_ArrayElement:
Diag.Report(Loc, ArrayID)
<< Pretty << T << Reason.Type;
break;
case RK_BaseClass:
{
const CXXRecordDecl *Decl = T->getAsCXXRecordDecl();
assert(Decl && "This type should be a C++ class");
Diag.Report(Decl->getLocation(), InheritsID)
<< Pretty << T << Reason.Type;
break;
}
case RK_Field:
Diag.Report(Reason.Field->getLocation(), MemberID)
<< Pretty << T << Reason.Field << Reason.Type;
break;
default:
return;
}
T = Reason.Type;
Reason = directAnnotationReason(T);
}
}
bool CustomTypeAnnotation::hasLiteralAnnotation(QualType T) const {
if (const TagDecl *D = T->getAsTagDecl()) {
return MozChecker::hasCustomAnnotation(D, Spelling);
}
return false;
}
CustomTypeAnnotation::AnnotationReason CustomTypeAnnotation::directAnnotationReason(QualType T) {
if (hasLiteralAnnotation(T)) {
AnnotationReason Reason = { T, RK_Direct, nullptr };
return Reason;
}
// Check if we have a cached answer
void *Key = T.getAsOpaquePtr();
ReasonCache::iterator Cached = Cache.find(T.getAsOpaquePtr());
if (Cached != Cache.end()) {
return Cached->second;
}
// Check if we have a type which we can recurse into
if (const ArrayType *Array = T->getAsArrayTypeUnsafe()) {
if (hasEffectiveAnnotation(Array->getElementType())) {
AnnotationReason Reason = { Array->getElementType(), RK_ArrayElement, nullptr };
Cache[Key] = Reason;
return Reason;
}
}
// Recurse into base classes
if (const CXXRecordDecl *Decl = T->getAsCXXRecordDecl()) {
if (Decl->hasDefinition()) {
Decl = Decl->getDefinition();
for (const CXXBaseSpecifier &Base : Decl->bases()) {
if (hasEffectiveAnnotation(Base.getType())) {
AnnotationReason Reason = { Base.getType(), RK_BaseClass, nullptr };
Cache[Key] = Reason;
return Reason;
}
}
// Recurse into members
for (const FieldDecl *Field : Decl->fields()) {
if (hasEffectiveAnnotation(Field->getType())) {
AnnotationReason Reason = { Field->getType(), RK_Field, Field };
Cache[Key] = Reason;
return Reason;
}
}
}
}
AnnotationReason Reason = { QualType(), RK_None, nullptr };
Cache[Key] = Reason;
return Reason;
}
bool isPlacementNew(const CXXNewExpr *expr) {
// Regular new expressions aren't placement new
if (expr->getNumPlacementArgs() == 0)
@ -830,7 +869,9 @@ void DiagnosticsMatcher::ScopeChecker::run(
DiagnosticIDs::Error, "variable of type %0 only valid on the stack");
unsigned globalID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Error, "variable of type %0 only valid as global");
unsigned errorID = (scope == eGlobal) ? globalID : stackID;
SourceLocation Loc;
QualType T;
if (const VarDecl *d = Result.Nodes.getNodeAs<VarDecl>("node")) {
if (scope == eLocal) {
// Ignore the match if it's a local variable.
@ -845,56 +886,29 @@ void DiagnosticsMatcher::ScopeChecker::run(
return;
}
Diag.Report(d->getLocation(), errorID) << d->getType();
noteInferred(d->getType(), Diag);
Loc = d->getLocation();
T = d->getType();
} else if (const CXXNewExpr *expr =
Result.Nodes.getNodeAs<CXXNewExpr>("node")) {
// If it's placement new, then this match doesn't count.
if (scope == eLocal && isPlacementNew(expr))
return;
Diag.Report(expr->getStartLoc(), errorID) << expr->getAllocatedType();
noteInferred(expr->getAllocatedType(), Diag);
Loc = expr->getStartLoc();
T = expr->getAllocatedType();
} else if (const CallExpr *expr =
Result.Nodes.getNodeAs<CallExpr>("node")) {
QualType badType = GetCallReturnType(expr)->getPointeeType();
Diag.Report(expr->getLocStart(), errorID) << badType;
noteInferred(badType, Diag);
}
}
void DiagnosticsMatcher::ScopeChecker::noteInferred(QualType T,
DiagnosticsEngine &Diag) {
unsigned inheritsID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Note,
"%0 is a %2 class because it inherits from a %2 class %1");
unsigned memberID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Note,
"%0 is a %3 class because member %1 is a %3 class %2");
const char* attribute = (scope == eGlobal) ?
"moz_global_class" : "moz_stack_class";
const char* type = (scope == eGlobal) ?
"global" : "stack";
// Find the CXXRecordDecl that is the local/global class of interest
while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
T = arrTy->getElementType();
CXXRecordDecl *clazz = T->getAsCXXRecordDecl();
// Direct result, we're done.
if (MozChecker::hasCustomAnnotation(clazz, attribute))
return;
const Decl *cause = inferredAllocCauses[clazz].first;
if (const CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(cause)) {
Diag.Report(clazz->getLocation(), inheritsID) <<
T << CRD->getDeclName() << type;
} else if (const FieldDecl *FD = dyn_cast<FieldDecl>(cause)) {
Diag.Report(FD->getLocation(), memberID) <<
T << FD << FD->getType() << type;
Loc = expr->getLocStart();
T = GetCallReturnType(expr)->getPointeeType();
}
// Recursively follow this back.
noteInferred(cast<ValueDecl>(cause)->getType(), Diag);
if (scope == eLocal) {
Diag.Report(Loc, stackID) << T;
StackClass.dumpAnnotationReason(Diag, T, Loc);
} else if (scope == eGlobal) {
Diag.Report(Loc, globalID) << T;
GlobalClass.dumpAnnotationReason(Diag, T, Loc);
}
}
void DiagnosticsMatcher::NonHeapClassChecker::run(
@ -902,46 +916,22 @@ void DiagnosticsMatcher::NonHeapClassChecker::run(
DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
unsigned stackID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Error, "variable of type %0 is not valid on the heap");
SourceLocation Loc;
QualType T;
if (const CXXNewExpr *expr = Result.Nodes.getNodeAs<CXXNewExpr>("node")) {
// If it's placement new, then this match doesn't count.
if (isPlacementNew(expr))
return;
Diag.Report(expr->getStartLoc(), stackID) << expr->getAllocatedType();
noteInferred(expr->getAllocatedType(), Diag);
Loc = expr->getLocStart();
T = expr->getAllocatedType();
} else if (const CallExpr *expr = Result.Nodes.getNodeAs<CallExpr>("node")) {
QualType badType = GetCallReturnType(expr)->getPointeeType();
Diag.Report(expr->getLocStart(), stackID) << badType;
noteInferred(badType, Diag);
Loc = expr->getLocStart();
T = GetCallReturnType(expr)->getPointeeType();
}
}
void DiagnosticsMatcher::NonHeapClassChecker::noteInferred(QualType T,
DiagnosticsEngine &Diag) {
unsigned inheritsID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Note,
"%0 is a non-heap class because it inherits from a non-heap class %1");
unsigned memberID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Note,
"%0 is a non-heap class because member %1 is a non-heap class %2");
// Find the CXXRecordDecl that is the stack class of interest
while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
T = arrTy->getElementType();
CXXRecordDecl *clazz = T->getAsCXXRecordDecl();
// Direct result, we're done.
if (MozChecker::hasCustomAnnotation(clazz, "moz_nonheap_class"))
return;
const Decl *cause = inferredAllocCauses[clazz].first;
if (const CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(cause)) {
Diag.Report(clazz->getLocation(), inheritsID) << T << CRD->getDeclName();
} else if (const FieldDecl *FD = dyn_cast<FieldDecl>(cause)) {
Diag.Report(FD->getLocation(), memberID) << T << FD << FD->getType();
}
// Recursively follow this back.
noteInferred(cast<ValueDecl>(cause)->getType(), Diag);
Diag.Report(Loc, stackID) << T;
NonHeapClass.dumpAnnotationReason(Diag, T, Loc);
}
void DiagnosticsMatcher::ArithmeticArgChecker::run(

View File

@ -16,9 +16,9 @@ void gobble(void *) { }
void misuseGlobalClass(int len) {
Global notValid; // expected-error {{variable of type 'Global' only valid as global}}
Global alsoNotValid[2]; // expected-error {{variable of type 'Global [2]' only valid as global}}
Global alsoNotValid[2]; // expected-error {{variable of type 'Global [2]' only valid as global}} expected-note {{'Global [2]' is a global type because it is an array of global type 'Global'}}
static Global valid; // expected-error {{variable of type 'Global' only valid as global}}
static Global alsoValid[2]; // expected-error {{variable of type 'Global [2]' only valid as global}}
static Global alsoValid[2]; // expected-error {{variable of type 'Global [2]' only valid as global}} expected-note {{'Global [2]' is a global type because it is an array of global type 'Global'}}
gobble(&valid);
gobble(&notValid);
@ -35,7 +35,7 @@ void misuseGlobalClass(int len) {
Global valid;
struct RandomClass {
Global nonstaticMember; // expected-note {{'RandomClass' is a global class because member 'nonstaticMember' is a global class 'Global'}}
Global nonstaticMember; // expected-note {{'RandomClass' is a global type because member 'nonstaticMember' is a global type 'Global'}}
static Global staticMember;
};
struct MOZ_GLOBAL_CLASS RandomGlobalClass {
@ -43,7 +43,7 @@ struct MOZ_GLOBAL_CLASS RandomGlobalClass {
static Global staticMember;
};
struct BadInherit : Global {}; // expected-note {{'BadInherit' is a global class because it inherits from a global class 'Global'}}
struct BadInherit : Global {}; // expected-note {{'BadInherit' is a global type because it inherits from a global type 'Global'}}
struct MOZ_GLOBAL_CLASS GoodInherit : Global {};
void misuseGlobalClassEvenMore(int len) {

View File

@ -13,5 +13,5 @@ TestClass f()
void g()
{
f(); // expected-error {{Unused MOZ_MUST_USE value of type 'TestClass'}}
f(); // expected-error {{Unused value of must-use type 'TestClass'}}
}

View File

@ -20,42 +20,42 @@ void use(MayUse&&);
void use(bool);
void foo() {
producesMustUse(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
producesMustUsePointer();
producesMustUseRef(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
producesMayUse();
producesMayUsePointer();
producesMayUseRef();
{
producesMustUse(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
producesMustUsePointer();
producesMustUseRef(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
producesMayUse();
producesMayUsePointer();
producesMayUseRef();
}
if (true) {
producesMustUse(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
producesMustUsePointer();
producesMustUseRef(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
producesMayUse();
producesMayUsePointer();
producesMayUseRef();
} else {
producesMustUse(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
producesMustUsePointer();
producesMustUseRef(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
producesMayUse();
producesMayUsePointer();
producesMayUseRef();
}
if(true) producesMustUse(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
else producesMustUse(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
if(true) producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
else producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
if(true) producesMustUsePointer();
else producesMustUsePointer();
if(true) producesMustUseRef(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
else producesMustUseRef(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
if(true) producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
else producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
if(true) producesMayUse();
else producesMayUse();
if(true) producesMayUsePointer();
@ -63,18 +63,18 @@ void foo() {
if(true) producesMayUseRef();
else producesMayUseRef();
while (true) producesMustUse(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
while (true) producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
while (true) producesMustUsePointer();
while (true) producesMustUseRef(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
while (true) producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
while (true) producesMayUse();
while (true) producesMayUsePointer();
while (true) producesMayUseRef();
do producesMustUse(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
do producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
while (true);
do producesMustUsePointer();
while (true);
do producesMustUseRef(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
do producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
while (true);
do producesMayUse();
while (true);
@ -83,48 +83,48 @@ void foo() {
do producesMayUseRef();
while (true);
for (;;) producesMustUse(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
for (;;) producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
for (;;) producesMustUsePointer();
for (;;) producesMustUseRef(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
for (;;) producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
for (;;) producesMayUse();
for (;;) producesMayUsePointer();
for (;;) producesMayUseRef();
for (producesMustUse();;); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
for (producesMustUse();;); // expected-error {{Unused value of must-use type 'MustUse'}}
for (producesMustUsePointer();;);
for (producesMustUseRef();;); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
for (producesMustUseRef();;); // expected-error {{Unused value of must-use type 'MustUse'}}
for (producesMayUse();;);
for (producesMayUsePointer();;);
for (producesMayUseRef();;);
for (;;producesMustUse()); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
for (;;producesMustUse()); // expected-error {{Unused value of must-use type 'MustUse'}}
for (;;producesMustUsePointer());
for (;;producesMustUseRef()); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
for (;;producesMustUseRef()); // expected-error {{Unused value of must-use type 'MustUse'}}
for (;;producesMayUse());
for (;;producesMayUsePointer());
for (;;producesMayUseRef());
use((producesMustUse(), false)); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
use((producesMustUse(), false)); // expected-error {{Unused value of must-use type 'MustUse'}}
use((producesMustUsePointer(), false));
use((producesMustUseRef(), false)); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
use((producesMustUseRef(), false)); // expected-error {{Unused value of must-use type 'MustUse'}}
use((producesMayUse(), false));
use((producesMayUsePointer(), false));
use((producesMayUseRef(), false));
switch (1) {
case 1:
producesMustUse(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
producesMustUsePointer();
producesMustUseRef(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
producesMayUse();
producesMayUsePointer();
producesMayUseRef();
case 2:
producesMustUse(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
case 3:
producesMustUsePointer();
case 4:
producesMustUseRef(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
case 5:
producesMayUse();
case 6:
@ -132,9 +132,9 @@ void foo() {
case 7:
producesMayUseRef();
default:
producesMustUse(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
producesMustUsePointer();
producesMustUseRef(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
producesMayUse();
producesMayUsePointer();
producesMayUseRef();

View File

@ -36,7 +36,7 @@ void misuseNonHeapClass(int len) {
NonHeap validStatic;
struct RandomClass {
NonHeap nonstaticMember; // expected-note {{'RandomClass' is a non-heap class because member 'nonstaticMember' is a non-heap class 'NonHeap'}}
NonHeap nonstaticMember; // expected-note {{'RandomClass' is a non-heap type because member 'nonstaticMember' is a non-heap type 'NonHeap'}}
static NonHeap staticMember;
};
struct MOZ_NONHEAP_CLASS RandomNonHeapClass {
@ -44,7 +44,7 @@ struct MOZ_NONHEAP_CLASS RandomNonHeapClass {
static NonHeap staticMember;
};
struct BadInherit : NonHeap {}; // expected-note {{'BadInherit' is a non-heap class because it inherits from a non-heap class 'NonHeap'}}
struct BadInherit : NonHeap {}; // expected-note {{'BadInherit' is a non-heap type because it inherits from a non-heap type 'NonHeap'}}
struct MOZ_NONHEAP_CLASS GoodInherit : NonHeap {};
void useStuffWrongly() {
@ -52,11 +52,11 @@ void useStuffWrongly() {
gobble(new RandomClass); // expected-error {{variable of type 'RandomClass' is not valid on the heap}}
}
// Stack class overrides non-heap classes.
// Stack class overrides non-heap typees.
struct MOZ_STACK_CLASS StackClass {};
struct MOZ_NONHEAP_CLASS InferredStackClass : GoodInherit {
NonHeap nonstaticMember;
StackClass stackClass; // expected-note {{'InferredStackClass' is a stack class because member 'stackClass' is a stack class 'StackClass'}}
StackClass stackClass; // expected-note {{'InferredStackClass' is a stack type because member 'stackClass' is a stack type 'StackClass'}}
};
InferredStackClass global; // expected-error {{variable of type 'InferredStackClass' only valid on the stack}}

View File

@ -18,7 +18,7 @@ void misuseStackClass(int len) {
Stack valid;
Stack alsoValid[2];
static Stack notValid; // expected-error {{variable of type 'Stack' only valid on the stack}}
static Stack alsoNotValid[2]; // expected-error {{variable of type 'Stack [2]' only valid on the stack}}
static Stack alsoNotValid[2]; // expected-error {{variable of type 'Stack [2]' only valid on the stack}} expected-note {{'Stack [2]' is a stack type because it is an array of stack type 'Stack'}}
gobble(&valid);
gobble(&notValid);
@ -35,7 +35,7 @@ void misuseStackClass(int len) {
Stack notValid; // expected-error {{variable of type 'Stack' only valid on the stack}}
struct RandomClass {
Stack nonstaticMember; // expected-note {{'RandomClass' is a stack class because member 'nonstaticMember' is a stack class 'Stack'}}
Stack nonstaticMember; // expected-note {{'RandomClass' is a stack type because member 'nonstaticMember' is a stack type 'Stack'}}
static Stack staticMember; // expected-error {{variable of type 'Stack' only valid on the stack}}
};
struct MOZ_STACK_CLASS RandomStackClass {
@ -43,7 +43,7 @@ struct MOZ_STACK_CLASS RandomStackClass {
static Stack staticMember; // expected-error {{variable of type 'Stack' only valid on the stack}}
};
struct BadInherit : Stack {}; // expected-note {{'BadInherit' is a stack class because it inherits from a stack class 'Stack'}}
struct BadInherit : Stack {}; // expected-note {{'BadInherit' is a stack type because it inherits from a stack type 'Stack'}}
struct MOZ_STACK_CLASS GoodInherit : Stack {};
BadInherit moreInvalid; // expected-error {{variable of type 'BadInherit' only valid on the stack}}