Bug 1123907 - Part 1: Add an analysis to ensure that a class marked MOZ_NEEDS_NO_VTABLE_TYPE cannot be instantiated by a class with a VTable; r=ehsan
authorMichael Layzell <michael@thelayzells.com>
Fri, 19 Jun 2015 19:30:51 -0400
changeset 254587 bf9c476468241687da76c7dbe2917f4f5692e942
parent 254586 b8991390305ee6f350b0c4f72131d94d74691a9c
child 254588 3822943d1e31a48bc6145bd57c9664e52f416848
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
bugs1123907
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 1123907 - Part 1: Add an analysis to ensure that a class marked MOZ_NEEDS_NO_VTABLE_TYPE cannot be instantiated by a class with a VTable; r=ehsan
build/clang-plugin/clang-plugin.cpp
build/clang-plugin/tests/TestNeedsNoVTableType.cpp
build/clang-plugin/tests/moz.build
--- a/build/clang-plugin/clang-plugin.cpp
+++ b/build/clang-plugin/clang-plugin.cpp
@@ -90,25 +90,31 @@ private:
     virtual void run(const MatchFinder::MatchResult &Result);
   };
 
   class ExplicitOperatorBoolChecker : public MatchFinder::MatchCallback {
   public:
     virtual void run(const MatchFinder::MatchResult &Result);
   };
 
+  class NeedsNoVTableTypeChecker : public MatchFinder::MatchCallback {
+  public:
+    virtual void run(const MatchFinder::MatchResult &Result);
+  };
+
   ScopeChecker stackClassChecker;
   ScopeChecker globalClassChecker;
   NonHeapClassChecker nonheapClassChecker;
   ArithmeticArgChecker arithmeticArgChecker;
   TrivialCtorDtorChecker trivialCtorDtorChecker;
   NaNExprChecker nanExprChecker;
   NoAddRefReleaseOnReturnChecker noAddRefReleaseOnReturnChecker;
   RefCountedInsideLambdaChecker refCountedInsideLambdaChecker;
   ExplicitOperatorBoolChecker explicitOperatorBoolChecker;
+  NeedsNoVTableTypeChecker needsNoVTableTypeChecker;
   MatchFinder astMatcher;
 };
 
 namespace {
 
 std::string getDeclarationNamespace(const Decl *decl) {
   const DeclContext *DC = decl->getDeclContext()->getEnclosingNamespaceContext();
   const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC);
@@ -482,16 +488,23 @@ bool IsInSystemHeader(const ASTContext &
   auto &SourceManager = AC.getSourceManager();
   auto ExpansionLoc = SourceManager.getExpansionLoc(D.getLocStart());
   if (ExpansionLoc.isInvalid()) {
     return false;
   }
   return SourceManager.isInSystemHeader(ExpansionLoc);
 }
 
+bool typeHasVTable(QualType T) {
+  while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
+    T = arrTy->getElementType();
+  CXXRecordDecl* offender = T->getAsCXXRecordDecl();
+  return offender && offender->hasDefinition() && offender->isDynamicClass();
+}
+
 }
 
 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) {
@@ -636,16 +649,24 @@ AST_POLYMORPHIC_MATCHER_P(equalsBoundNod
   };
   Visitor visitor(Node, ID, haveMatchingResult);
   bindings.visitMatches(&visitor);
   return haveMatchingResult;
 }
 
 #endif
 
+AST_MATCHER(QualType, hasVTable) {
+  return typeHasVTable(Node);
+}
+
+AST_MATCHER(CXXRecordDecl, hasNeedsNoVTableTypeAttr) {
+  return MozChecker::hasCustomAnnotation(&Node, "moz_needs_no_vtable_type");
+}
+
 }
 }
 
 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");
@@ -855,16 +876,21 @@ DiagnosticsMatcher::DiagnosticsMatcher()
     &refCountedInsideLambdaChecker);
 
   // Older clang versions such as the ones used on the infra recognize these
   // conversions as 'operator _Bool', but newer clang versions recognize these
   // as 'operator bool'.
   astMatcher.addMatcher(methodDecl(anyOf(hasName("operator bool"),
                                          hasName("operator _Bool"))).bind("node"),
     &explicitOperatorBoolChecker);
+
+  astMatcher.addMatcher(classTemplateSpecializationDecl(
+             allOf(hasAnyTemplateArgument(refersToType(hasVTable())),
+                   hasNeedsNoVTableTypeAttr())).bind("node"),
+     &needsNoVTableTypeChecker);
 }
 
 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(
@@ -1037,16 +1063,42 @@ void DiagnosticsMatcher::ExplicitOperato
       !MozChecker::hasCustomAnnotation(method, "moz_implicit") &&
       !IsInSystemHeader(method->getASTContext(), *method) &&
       isInterestingDeclForImplicitConversion(method)) {
     Diag.Report(method->getLocStart(), errorID) << clazz;
     Diag.Report(method->getLocStart(), noteID) << "'operator bool'";
   }
 }
 
+void DiagnosticsMatcher::NeedsNoVTableTypeChecker::run(
+    const MatchFinder::MatchResult &Result) {
+  DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
+  unsigned errorID = Diag.getDiagnosticIDs()->getCustomDiagID(
+      DiagnosticIDs::Error, "%0 cannot be instantiated because %1 has a VTable");
+  unsigned noteID = Diag.getDiagnosticIDs()->getCustomDiagID(
+      DiagnosticIDs::Note, "bad instantiation of %0 requested here");
+
+  const ClassTemplateSpecializationDecl *specialization =
+    Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("node");
+
+  // Get the offending template argument
+  QualType offender;
+  const TemplateArgumentList &args =
+    specialization->getTemplateInstantiationArgs();
+  for (unsigned i = 0; i < args.size(); ++i) {
+    offender = args[i].getAsType();
+    if (typeHasVTable(offender)) {
+      break;
+    }
+  }
+
+  Diag.Report(specialization->getLocStart(), errorID) << specialization << offender;
+  Diag.Report(specialization->getPointOfInstantiation(), noteID) << specialization;
+}
+
 class MozCheckAction : public PluginASTAction {
 public:
   ASTConsumerPtr CreateASTConsumer(CompilerInstance &CI, StringRef fileName) override {
 #if CLANG_VERSION_FULL >= 306
     std::unique_ptr<MozChecker> checker(llvm::make_unique<MozChecker>(CI));
     ASTConsumerPtr other(checker->getOtherConsumer());
 
     std::vector<ASTConsumerPtr> consumers;
new file mode 100644
--- /dev/null
+++ b/build/clang-plugin/tests/TestNeedsNoVTableType.cpp
@@ -0,0 +1,94 @@
+#define MOZ_NEEDS_NO_VTABLE_TYPE __attribute__((annotate("moz_needs_no_vtable_type")))
+
+template <class T>
+struct MOZ_NEEDS_NO_VTABLE_TYPE PickyConsumer { // expected-error {{'PickyConsumer<B>' cannot be instantiated because 'B' has a VTable}} expected-error {{'PickyConsumer<E>' cannot be instantiated because 'E' has a VTable}} expected-error {{'PickyConsumer<F>' cannot be instantiated because 'F' has a VTable}} expected-error {{'PickyConsumer<G>' cannot be instantiated because 'G' has a VTable}}
+  T *m;
+};
+
+template <class T>
+struct MOZ_NEEDS_NO_VTABLE_TYPE PickyConsumer_A { // expected-error {{'PickyConsumer_A<B>' cannot be instantiated because 'B' has a VTable}} expected-error {{'PickyConsumer_A<E>' cannot be instantiated because 'E' has a VTable}} expected-error {{'PickyConsumer_A<F>' cannot be instantiated because 'F' has a VTable}} expected-error {{'PickyConsumer_A<G>' cannot be instantiated because 'G' has a VTable}}
+  T *m;
+};
+template <class T>
+struct PickyConsumerWrapper {
+  PickyConsumer_A<T> m; // expected-note {{bad instantiation of 'PickyConsumer_A<B>' requested here}} expected-note {{bad instantiation of 'PickyConsumer_A<E>' requested here}} expected-note {{bad instantiation of 'PickyConsumer_A<F>' requested here}} expected-note {{bad instantiation of 'PickyConsumer_A<G>' requested here}}
+};
+
+template <class T>
+struct MOZ_NEEDS_NO_VTABLE_TYPE PickyConsumer_B { // expected-error {{'PickyConsumer_B<B>' cannot be instantiated because 'B' has a VTable}} expected-error {{'PickyConsumer_B<E>' cannot be instantiated because 'E' has a VTable}} expected-error {{'PickyConsumer_B<F>' cannot be instantiated because 'F' has a VTable}} expected-error {{'PickyConsumer_B<G>' cannot be instantiated because 'G' has a VTable}}
+  T *m;
+};
+template <class T>
+struct PickyConsumerSubclass : PickyConsumer_B<T> {}; // expected-note {{bad instantiation of 'PickyConsumer_B<B>' requested here}} expected-note {{bad instantiation of 'PickyConsumer_B<E>' requested here}} expected-note {{bad instantiation of 'PickyConsumer_B<F>' requested here}} expected-note {{bad instantiation of 'PickyConsumer_B<G>' requested here}}
+
+template <class T>
+struct NonPickyConsumer {
+  T *m;
+};
+
+struct A {};
+struct B : virtual A {};
+struct C : A {};
+struct D {
+  void d();
+};
+struct E {
+  virtual void e();
+};
+struct F : E {
+  virtual void e() final;
+};
+struct G {
+  virtual void e() = 0;
+};
+
+void f() {
+  {
+    PickyConsumer<A> a1;
+    PickyConsumerWrapper<A> a2;
+    PickyConsumerSubclass<A> a3;
+    NonPickyConsumer<A> a4;
+  }
+
+  {
+    PickyConsumer<B> a1; // expected-note {{bad instantiation of 'PickyConsumer<B>' requested here}}
+    PickyConsumerWrapper<B> a2;
+    PickyConsumerSubclass<B> a3;
+    NonPickyConsumer<B> a4;
+  }
+
+  {
+    PickyConsumer<C> a1;
+    PickyConsumerWrapper<C> a2;
+    PickyConsumerSubclass<C> a3;
+    NonPickyConsumer<C> a4;
+  }
+
+  {
+    PickyConsumer<D> a1;
+    PickyConsumerWrapper<D> a2;
+    PickyConsumerSubclass<D> a3;
+    NonPickyConsumer<D> a4;
+  }
+
+  {
+    PickyConsumer<E> a1; // expected-note {{bad instantiation of 'PickyConsumer<E>' requested here}}
+    PickyConsumerWrapper<E> a2;
+    PickyConsumerSubclass<E> a3;
+    NonPickyConsumer<E> a4;
+  }
+
+  {
+    PickyConsumer<F> a1; // expected-note {{bad instantiation of 'PickyConsumer<F>' requested here}}
+    PickyConsumerWrapper<F> a2;
+    PickyConsumerSubclass<F> a3;
+    NonPickyConsumer<F> a4;
+  }
+
+  {
+    PickyConsumer<G> a1; // expected-note {{bad instantiation of 'PickyConsumer<G>' requested here}}
+    PickyConsumerWrapper<G> a2;
+    PickyConsumerSubclass<G> a3;
+    NonPickyConsumer<G> a4;
+  }
+}
--- a/build/clang-plugin/tests/moz.build
+++ b/build/clang-plugin/tests/moz.build
@@ -9,16 +9,17 @@ SOURCES += [
     'TestCustomHeap.cpp',
     'TestExplicitOperatorBool.cpp',
     'TestGlobalClass.cpp',
     'TestMultipleAnnotations.cpp',
     'TestMustOverride.cpp',
     'TestMustUse.cpp',
     'TestNANTestingExpr.cpp',
     'TestNANTestingExprC.c',
+    'TestNeedsNoVTableType.cpp',
     'TestNoAddRefReleaseOnReturn.cpp',
     'TestNoArithmeticExprInArgument.cpp',
     'TestNonHeapClass.cpp',
     'TestNoRefcountedInsideLambdas.cpp',
     'TestStackClass.cpp',
     'TestTrivialCtorDtor.cpp',
 ]