mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 868285 - Fix static checking builds, part 1: infer MOZ_STACK_CLASS. r=bsmedberg
This commit is contained in:
parent
8bd1d9af1d
commit
2cf82af930
@ -11,6 +11,7 @@
|
|||||||
#include "clang/Frontend/FrontendPluginRegistry.h"
|
#include "clang/Frontend/FrontendPluginRegistry.h"
|
||||||
#include "clang/Frontend/MultiplexConsumer.h"
|
#include "clang/Frontend/MultiplexConsumer.h"
|
||||||
#include "clang/Sema/Sema.h"
|
#include "clang/Sema/Sema.h"
|
||||||
|
#include "llvm/ADT/DenseMap.h"
|
||||||
|
|
||||||
#define CLANG_VERSION_FULL (CLANG_VERSION_MAJOR * 100 + CLANG_VERSION_MINOR)
|
#define CLANG_VERSION_FULL (CLANG_VERSION_MAJOR * 100 + CLANG_VERSION_MINOR)
|
||||||
|
|
||||||
@ -32,6 +33,7 @@ private:
|
|||||||
class StackClassChecker : public MatchFinder::MatchCallback {
|
class StackClassChecker : public MatchFinder::MatchCallback {
|
||||||
public:
|
public:
|
||||||
virtual void run(const MatchFinder::MatchResult &Result);
|
virtual void run(const MatchFinder::MatchResult &Result);
|
||||||
|
void noteInferred(QualType T, DiagnosticsEngine &Diag);
|
||||||
};
|
};
|
||||||
|
|
||||||
StackClassChecker stackClassChecker;
|
StackClassChecker stackClassChecker;
|
||||||
@ -87,16 +89,6 @@ public:
|
|||||||
if (hasCustomAnnotation(*M, "moz_must_override"))
|
if (hasCustomAnnotation(*M, "moz_must_override"))
|
||||||
must_overrides.push_back(*M);
|
must_overrides.push_back(*M);
|
||||||
}
|
}
|
||||||
|
|
||||||
// While we are looping over parent classes, we'll also check to make sure
|
|
||||||
// that the subclass has the annotation if the superclass does.
|
|
||||||
if (hasCustomAnnotation(parent, "moz_stack_class") &&
|
|
||||||
!hasCustomAnnotation(d, "moz_stack_class")) {
|
|
||||||
unsigned badInheritID = Diag.getDiagnosticIDs()->getCustomDiagID(
|
|
||||||
DiagnosticIDs::Error, "%0 inherits from a stack class %1");
|
|
||||||
Diag.Report(d->getLocation(), badInheritID)
|
|
||||||
<< d->getDeclName() << parent->getDeclName();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (OverridesVector::iterator it = must_overrides.begin();
|
for (OverridesVector::iterator it = must_overrides.begin();
|
||||||
@ -123,6 +115,60 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
DenseMap<const CXXRecordDecl *, const Decl *> stackClassCauses;
|
||||||
|
|
||||||
|
bool isStackClass(QualType T);
|
||||||
|
|
||||||
|
bool isStackClass(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 false;
|
||||||
|
D = D->getDefinition();
|
||||||
|
// Base class: anyone with this annotation is obviously a stack class
|
||||||
|
if (MozChecker::hasCustomAnnotation(D, "moz_stack_class"))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// See if we cached the result.
|
||||||
|
DenseMap<const CXXRecordDecl *, const Decl *>::iterator it =
|
||||||
|
stackClassCauses.find(D);
|
||||||
|
if (it != stackClassCauses.end()) {
|
||||||
|
// If the cause is NULL, then this is not a stack class.
|
||||||
|
return it->second != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look through all base cases to figure out if the parent is a stack class.
|
||||||
|
for (CXXRecordDecl::base_class_iterator base = D->bases_begin(),
|
||||||
|
e = D->bases_end(); base != e; ++base) {
|
||||||
|
if (isStackClass(base->getType())) {
|
||||||
|
stackClassCauses.insert(std::make_pair(D,
|
||||||
|
base->getType()->getAsCXXRecordDecl()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
if (isStackClass(field->getType())) {
|
||||||
|
stackClassCauses.insert(std::make_pair(D, *field));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nope, this class is not a stack class.
|
||||||
|
stackClassCauses.insert(std::make_pair(D, (const Decl*)0));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isStackClass(QualType T) {
|
||||||
|
while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
|
||||||
|
T = arrTy->getElementType();
|
||||||
|
CXXRecordDecl *clazz = T->getAsCXXRecordDecl();
|
||||||
|
return clazz && isStackClass(clazz);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace clang {
|
namespace clang {
|
||||||
@ -131,11 +177,7 @@ namespace ast_matchers {
|
|||||||
/// This matcher will match any class with the stack class assertion or an
|
/// This matcher will match any class with the stack class assertion or an
|
||||||
/// array of such classes.
|
/// array of such classes.
|
||||||
AST_MATCHER(QualType, stackClassAggregate) {
|
AST_MATCHER(QualType, stackClassAggregate) {
|
||||||
QualType t = Node;
|
return isStackClass(Node);
|
||||||
while (const ArrayType *arrTy = t->getAsArrayTypeUnsafe())
|
|
||||||
t = arrTy->getElementType();
|
|
||||||
CXXRecordDecl *clazz = t->getAsCXXRecordDecl();
|
|
||||||
return clazz && MozChecker::hasCustomAnnotation(clazz, "moz_stack_class");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -151,10 +193,6 @@ DiagnosticsMatcher::DiagnosticsMatcher() {
|
|||||||
astMatcher.addMatcher(newExpr(hasType(pointerType(
|
astMatcher.addMatcher(newExpr(hasType(pointerType(
|
||||||
pointee(stackClassAggregate())
|
pointee(stackClassAggregate())
|
||||||
))).bind("node"), &stackClassChecker);
|
))).bind("node"), &stackClassChecker);
|
||||||
// Stack class assertion: a stack-class field is permitted only if it's a
|
|
||||||
// member of a class with the annotation
|
|
||||||
astMatcher.addMatcher(fieldDecl(hasType(stackClassAggregate())).bind("field"),
|
|
||||||
&stackClassChecker);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiagnosticsMatcher::StackClassChecker::run(
|
void DiagnosticsMatcher::StackClassChecker::run(
|
||||||
@ -166,28 +204,48 @@ void DiagnosticsMatcher::StackClassChecker::run(
|
|||||||
// Ignore the match if it's a local variable.
|
// Ignore the match if it's a local variable.
|
||||||
if (d->hasLocalStorage())
|
if (d->hasLocalStorage())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Diag.Report(d->getLocation(), stackID) << d->getType();
|
Diag.Report(d->getLocation(), stackID) << d->getType();
|
||||||
|
noteInferred(d->getType(), Diag);
|
||||||
} else if (const CXXNewExpr *expr =
|
} else if (const CXXNewExpr *expr =
|
||||||
Result.Nodes.getNodeAs<CXXNewExpr>("node")) {
|
Result.Nodes.getNodeAs<CXXNewExpr>("node")) {
|
||||||
// If it's placement new, then this match doesn't count.
|
// If it's placement new, then this match doesn't count.
|
||||||
if (expr->getNumPlacementArgs() > 0)
|
if (expr->getNumPlacementArgs() > 0)
|
||||||
return;
|
return;
|
||||||
Diag.Report(expr->getStartLoc(), stackID) << expr->getAllocatedType();
|
Diag.Report(expr->getStartLoc(), stackID) << expr->getAllocatedType();
|
||||||
} else if (const FieldDecl *field =
|
noteInferred(expr->getAllocatedType(), Diag);
|
||||||
Result.Nodes.getNodeAs<FieldDecl>("field")) {
|
|
||||||
// AST matchers don't let me get the class that contains a field...
|
|
||||||
const RecordDecl *parent = field->getParent();
|
|
||||||
if (!MozChecker::hasCustomAnnotation(parent, "moz_stack_class")) {
|
|
||||||
// We use a more verbose error message here.
|
|
||||||
unsigned stackID = Diag.getDiagnosticIDs()->getCustomDiagID(
|
|
||||||
DiagnosticIDs::Error,
|
|
||||||
"member of type %0 in class %1 that is not a stack class");
|
|
||||||
Diag.Report(field->getLocation(), stackID) << field->getType() <<
|
|
||||||
parent->getDeclName();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DiagnosticsMatcher::StackClassChecker::noteInferred(QualType T,
|
||||||
|
DiagnosticsEngine &Diag) {
|
||||||
|
unsigned inheritsID = Diag.getDiagnosticIDs()->getCustomDiagID(
|
||||||
|
DiagnosticIDs::Note,
|
||||||
|
"%0 is a stack class because it inherits from a stack class %1");
|
||||||
|
unsigned memberID = Diag.getDiagnosticIDs()->getCustomDiagID(
|
||||||
|
DiagnosticIDs::Note,
|
||||||
|
"%0 is a stack class because member %1 is a stack 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_stack_class"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const Decl *cause = stackClassCauses[clazz];
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
class MozCheckAction : public PluginASTAction {
|
class MozCheckAction : public PluginASTAction {
|
||||||
public:
|
public:
|
||||||
ASTConsumer *CreateASTConsumer(CompilerInstance &CI, StringRef fileName) {
|
ASTConsumer *CreateASTConsumer(CompilerInstance &CI, StringRef fileName) {
|
||||||
|
@ -35,7 +35,7 @@ void misuseStackClass(int len) {
|
|||||||
|
|
||||||
Stack notValid; // expected-error {{variable of type 'Stack' only valid on the stack}}
|
Stack notValid; // expected-error {{variable of type 'Stack' only valid on the stack}}
|
||||||
struct RandomClass {
|
struct RandomClass {
|
||||||
Stack nonstaticMember; // expected-error {{member of type 'Stack' in class 'RandomClass' that is not a stack class}}
|
Stack nonstaticMember; // expected-note {{'RandomClass' is a stack class because member 'nonstaticMember' is a stack class 'Stack'}}
|
||||||
static Stack staticMember; // expected-error {{variable of type 'Stack' only valid on the stack}}
|
static Stack staticMember; // expected-error {{variable of type 'Stack' only valid on the stack}}
|
||||||
};
|
};
|
||||||
struct MOZ_STACK_CLASS RandomStackClass {
|
struct MOZ_STACK_CLASS RandomStackClass {
|
||||||
@ -43,5 +43,8 @@ struct MOZ_STACK_CLASS RandomStackClass {
|
|||||||
static Stack staticMember; // expected-error {{variable of type 'Stack' only valid on the stack}}
|
static Stack staticMember; // expected-error {{variable of type 'Stack' only valid on the stack}}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BadInherit : Stack {}; // expected-error {{'BadInherit' inherits from a stack class 'Stack'}}
|
struct BadInherit : Stack {}; // expected-note {{'BadInherit' is a stack class because it inherits from a stack class 'Stack'}}
|
||||||
struct MOZ_STACK_CLASS GoodInherit : Stack {};
|
struct MOZ_STACK_CLASS GoodInherit : Stack {};
|
||||||
|
|
||||||
|
BadInherit moreInvalid; // expected-error {{variable of type 'BadInherit' only valid on the stack}}
|
||||||
|
RandomClass evenMoreInvalid; // expected-error {{variable of type 'RandomClass' only valid on the stack}}
|
||||||
|
@ -382,9 +382,10 @@
|
|||||||
* MOZ_STACK_CLASS: Applies to all classes. Any class with this annotation is
|
* MOZ_STACK_CLASS: Applies to all classes. Any class with this annotation is
|
||||||
* expected to live on the stack, so it is a compile-time error to use it, or
|
* expected to live on the stack, so it is a compile-time error to use it, or
|
||||||
* an array of such objects, as a global or static variable, or as the type of
|
* an array of such objects, as a global or static variable, or as the type of
|
||||||
* a new expression (unless placement new is being used). It may be a base or
|
* a new expression (unless placement new is being used). If a member of
|
||||||
* a member of another class only if both classes are marked with this
|
* another class uses this class, or if another class inherits from this
|
||||||
* annotation.
|
* class, then it is considered to be a stack class as well, although this
|
||||||
|
* attribute need not be provided in such cases.
|
||||||
*/
|
*/
|
||||||
#ifdef MOZ_CLANG_PLUGIN
|
#ifdef MOZ_CLANG_PLUGIN
|
||||||
# define MOZ_MUST_OVERRIDE __attribute__((annotate("moz_must_override")))
|
# define MOZ_MUST_OVERRIDE __attribute__((annotate("moz_must_override")))
|
||||||
|
Loading…
Reference in New Issue
Block a user