Bug 1185044 - Unify type annotation logic between MOZ_*_CLASS and MOZ_MUST_USE; r=ehsan
authorMichael Layzell <michael@thelayzells.com>
Mon, 20 Jul 2015 19:43:12 -0400
changeset 254586 b8991390305ee6f350b0c4f72131d94d74691a9c
parent 254585 78ab441fb9c88314a488dca7f7fc9692e66497d7
child 254587 bf9c476468241687da76c7dbe2917f4f5692e942
push id29108
push userryanvm@gmail.com
push dateMon, 27 Jul 2015 14:12:01 +0000
treeherdermozilla-central@27ae736ef960 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan
bugs1185044
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1185044 - Unify type annotation logic between MOZ_*_CLASS and MOZ_MUST_USE; r=ehsan
build/clang-plugin/clang-plugin.cpp
build/clang-plugin/tests/TestGlobalClass.cpp
build/clang-plugin/tests/TestMultipleAnnotations.cpp
build/clang-plugin/tests/TestMustUse.cpp
build/clang-plugin/tests/TestNonHeapClass.cpp
build/clang-plugin/tests/TestStackClass.cpp
--- a/build/clang-plugin/clang-plugin.cpp
+++ b/build/clang-plugin/clang-plugin.cpp
@@ -51,25 +51,23 @@ private:
   public:
     enum Scope {
       eLocal,
       eGlobal
     };
     ScopeChecker(Scope scope_) :
       scope(scope_) {}
     virtual void run(const MatchFinder::MatchResult &Result);
-    void noteInferred(QualType T, DiagnosticsEngine &Diag);
   private:
     Scope scope;
   };
 
   class NonHeapClassChecker : public MatchFinder::MatchCallback {
   public:
     virtual void run(const MatchFinder::MatchResult &Result);
-    void noteInferred(QualType T, DiagnosticsEngine &Diag);
   };
 
   class ArithmeticArgChecker : public MatchFinder::MatchCallback {
   public:
     virtual void run(const MatchFinder::MatchResult &Result);
   };
 
   class TrivialCtorDtorChecker : public MatchFinder::MatchCallback {
@@ -212,16 +210,61 @@ bool isInterestingDeclForImplicitCtor(co
 
 bool isInterestingDeclForImplicitConversion(const Decl *decl) {
   return !isInIgnoredNamespaceForImplicitConversion(decl) &&
          !isIgnoredPathForImplicitConversion(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;
   DiagnosticsMatcher matcher;
 public:
   MozChecker(const CompilerInstance &CI) : Diag(CI.getDiagnostics()), CI(CI) {}
 
   ASTConsumerPtr getOtherConsumer() {
@@ -243,28 +286,23 @@ public:
     }
 
     return false;
   }
 
   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());
       }
     }
   }
 
   bool VisitCXXRecordDecl(CXXRecordDecl *d) {
     // We need definitions, not declarations
     if (!d->isThisDeclarationADefinition()) return true;
 
@@ -378,112 +416,16 @@ public:
     return true;
   }
   bool VisitBinComma(BinaryOperator* Op) {
     HandleUnusedExprResult(Op->getLHS());
     return true;
   }
 };
 
-/**
- * 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;
 RefCountedMap refCountedClasses;
 
 bool classHasAddRefRelease(const CXXRecordDecl *D) {
   const RefCountedMap::iterator& it = refCountedClasses.find(D);
   if (it != refCountedClasses.end()) {
@@ -527,17 +469,17 @@ bool isClassRefCounted(const CXXRecordDe
 
   return false;
 }
 
 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>
 bool IsInSystemHeader(const ASTContext &AC, const T &D) {
   auto &SourceManager = AC.getSourceManager();
   auto ExpansionLoc = SourceManager.getExpansionLoc(D.getLocStart());
   if (ExpansionLoc.isInvalid()) {
     return false;
@@ -548,29 +490,29 @@ bool IsInSystemHeader(const ASTContext &
 }
 
 namespace clang {
 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
 /// allocator.
 AST_MATCHER(FunctionDecl, heapAllocator) {
   return MozChecker::hasCustomAnnotation(&Node, "moz_heap_allocator");
 }
 
@@ -699,16 +641,113 @@ AST_POLYMORPHIC_MATCHER_P(equalsBoundNod
 
 #endif
 
 }
 }
 
 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)
     return false;
   if (MozChecker::hasCustomAnnotation(expr->getOperatorNew(),
       "moz_heap_allocator"))
     return false;
   return true;
@@ -825,128 +864,79 @@ DiagnosticsMatcher::DiagnosticsMatcher()
 
 void DiagnosticsMatcher::ScopeChecker::run(
     const MatchFinder::MatchResult &Result) {
   DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
   unsigned stackID = Diag.getDiagnosticIDs()->getCustomDiagID(
     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.
       if (d->hasLocalStorage())
         return;
     } else if (scope == eGlobal) {
       // Ignore the match if it's a global variable or a static member of a
       // class.  The latter is technically not in the global scope, but for the
       // use case of classes that intend to avoid introducing static
       // initializers that is fine.
       if (d->hasGlobalStorage() && !d->isStaticLocal())
         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(
     const MatchFinder::MatchResult &Result) {
   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(
     const MatchFinder::MatchResult &Result) {
   DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
   unsigned errorID = Diag.getDiagnosticIDs()->getCustomDiagID(
       DiagnosticIDs::Error, "cannot pass an arithmetic expression of built-in types to %0");
   const Expr *expr = Result.Nodes.getNodeAs<Expr>("node");
--- a/build/clang-plugin/tests/TestGlobalClass.cpp
+++ b/build/clang-plugin/tests/TestGlobalClass.cpp
@@ -11,42 +11,42 @@ template <class T>
 struct MOZ_GLOBAL_CLASS TemplateClass {
   T i;
 };
 
 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);
   gobble(&alsoValid[0]);
 
   gobble(new Global); // expected-error {{variable of type 'Global' only valid as global}}
   gobble(new Global[10]); // expected-error {{variable of type 'Global' only valid as global}}
   gobble(new TemplateClass<int>); // expected-error {{variable of type 'TemplateClass<int>' only valid as global}}
   gobble(len <= 5 ? &valid : new Global); // expected-error {{variable of type 'Global' only valid as global}}
 
   char buffer[sizeof(Global)];
   gobble(new (buffer) Global); // expected-error {{variable of type 'Global' only valid as global}}
 }
 
 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 {
   Global nonstaticMember;
   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) {
   BadInherit moreInvalid; // expected-error {{variable of type 'BadInherit' only valid as global}}
   RandomClass evenMoreInvalid; // expected-error {{variable of type 'RandomClass' only valid as global}}
 }
--- a/build/clang-plugin/tests/TestMultipleAnnotations.cpp
+++ b/build/clang-plugin/tests/TestMultipleAnnotations.cpp
@@ -8,10 +8,10 @@ TestClass foo; // expected-error {{varia
 TestClass f()
 {
   TestClass bar;
   return bar;
 }
 
 void g()
 {
-  f(); // expected-error {{Unused MOZ_MUST_USE value of type 'TestClass'}}
+  f(); // expected-error {{Unused value of must-use type 'TestClass'}}
 }
--- a/build/clang-plugin/tests/TestMustUse.cpp
+++ b/build/clang-plugin/tests/TestMustUse.cpp
@@ -15,131 +15,131 @@ void use(MustUse*);
 void use(MustUse&);
 void use(MustUse&&);
 void use(MayUse*);
 void use(MayUse&);
 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();
   else producesMayUsePointer();
   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);
   do producesMayUsePointer();
   while (true);
   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:
     producesMayUsePointer();
   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();
   }
 
   use(producesMustUse());
   use(producesMustUsePointer());
   use(producesMustUseRef());
--- a/build/clang-plugin/tests/TestNonHeapClass.cpp
+++ b/build/clang-plugin/tests/TestNonHeapClass.cpp
@@ -31,32 +31,32 @@ void misuseNonHeapClass(int len) {
   gobble(len <= 5 ? &valid : new NonHeap); // expected-error {{variable of type 'NonHeap' is not valid on the heap}}
 
   char buffer[sizeof(NonHeap)];
   gobble(new (buffer) NonHeap);
 }
 
 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 {
   NonHeap nonstaticMember;
   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() {
   gobble(new BadInherit); // expected-error {{variable of type 'BadInherit' is not valid on the heap}}
   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}}
--- a/build/clang-plugin/tests/TestStackClass.cpp
+++ b/build/clang-plugin/tests/TestStackClass.cpp
@@ -13,38 +13,38 @@ struct MOZ_STACK_CLASS TemplateClass {
 };
 
 void gobble(void *) { }
 
 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);
   gobble(&alsoValid[0]);
 
   gobble(new Stack); // expected-error {{variable of type 'Stack' only valid on the stack}}
   gobble(new Stack[10]); // expected-error {{variable of type 'Stack' only valid on the stack}}
   gobble(new TemplateClass<int>); // expected-error {{variable of type 'TemplateClass<int>' only valid on the stack}}
   gobble(len <= 5 ? &valid : new Stack); // expected-error {{variable of type 'Stack' only valid on the stack}}
 
   char buffer[sizeof(Stack)];
   gobble(new (buffer) Stack);
 }
 
 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 {
   Stack nonstaticMember;
   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}}
 RandomClass evenMoreInvalid; // expected-error {{variable of type 'RandomClass' only valid on the stack}}