Bug 1323455 - add checker to detect creation of refcounted class outside refptr draft
authorTristan Bourvon <tbourvon@mozilla.com>
Wed, 05 Jul 2017 16:17:32 +0200
changeset 615177 fe31ad90275bbc1270371064204c27bad9b0fa27
parent 615176 32d9d1e81cc607320a36391845917f645f7a7f72
child 615178 0980b2da9773343aa82e9bb6b0b63c8ffd72e8f0
push id70262
push userbmo:tbourvon@mozilla.com
push dateTue, 25 Jul 2017 15:50:56 +0000
bugs1323455
milestone56.0a1
Bug 1323455 - add checker to detect creation of refcounted class outside refptr This checker detects the creation (using new) of refcounted objects outside of common ref pointers (notably RefPtr and nsCOMPtr). This includes an autofix suggestion to replace the raw pointer declaration (if it exists) with a RefPtr. MozReview-Commit-ID: 8iFkok0Se94
build/clang-plugin/Checks.inc
build/clang-plugin/ChecksIncludes.inc
build/clang-plugin/CustomMatchers.h
build/clang-plugin/RefCountedToRawAssignmentChecker.cpp
build/clang-plugin/RefCountedToRawAssignmentChecker.h
build/clang-plugin/Utils.h
build/clang-plugin/moz.build
build/clang-plugin/tests/TestKungFuDeathGrip.cpp
build/clang-plugin/tests/TestRefCountedCopyConstructor.cpp
build/clang-plugin/tests/TestRefCountedToRawAssignment.cpp
build/clang-plugin/tests/moz.build
mfbt/Attributes.h
mfbt/RefPtr.h
xpcom/base/OwningNonNull.h
xpcom/base/nsCOMPtr.h
--- a/build/clang-plugin/Checks.inc
+++ b/build/clang-plugin/Checks.inc
@@ -20,11 +20,12 @@ CHECK(NoDuplicateRefCntMemberChecker, "n
 CHECK(NoExplicitMoveConstructorChecker, "no-explicit-move-constructor")
 CHECK(NonMemMovableMemberChecker, "non-memmovable-member")
 CHECK(NonMemMovableTemplateArgChecker, "non-memmovable-template-arg")
 CHECK(NonParamInsideFunctionDeclChecker, "non-memmovable-template-arg")
 CHECK(OverrideBaseCallChecker, "override-base-call")
 CHECK(OverrideBaseCallUsageChecker, "override-base-call-usage")
 CHECK(RefCountedCopyConstructorChecker, "refcounted-copy-constructor")
 CHECK(RefCountedInsideLambdaChecker, "refcounted-inside-lambda")
+CHECK(RefCountedToRawAssignmentChecker, "refcounted-to-raw-assignment")
 CHECK(ScopeChecker, "scope")
 CHECK(SprintfLiteralChecker, "sprintf-literal")
 CHECK(TrivialCtorDtorChecker, "trivial-constructor-destructor")
--- a/build/clang-plugin/ChecksIncludes.inc
+++ b/build/clang-plugin/ChecksIncludes.inc
@@ -21,12 +21,13 @@
 #include "NoExplicitMoveConstructorChecker.h"
 #include "NonMemMovableMemberChecker.h"
 #include "NonMemMovableTemplateArgChecker.h"
 #include "NonParamInsideFunctionDeclChecker.h"
 #include "OverrideBaseCallChecker.h"
 #include "OverrideBaseCallUsageChecker.h"
 #include "RefCountedCopyConstructorChecker.h"
 #include "RefCountedInsideLambdaChecker.h"
+#include "RefCountedToRawAssignmentChecker.h"
 #include "ScopeChecker.h"
 #include "SprintfLiteralChecker.h"
 #include "TrivialCtorDtorChecker.h"
 
--- a/build/clang-plugin/CustomMatchers.h
+++ b/build/clang-plugin/CustomMatchers.h
@@ -24,21 +24,55 @@ AST_MATCHER(Decl, noArithmeticExprInArgs
 }
 
 /// This matcher will match any C++ class that is marked as having a trivial
 /// constructor and destructor.
 AST_MATCHER(CXXRecordDecl, hasTrivialCtorDtor) {
   return hasCustomAnnotation(&Node, "moz_trivial_ctor_dtor");
 }
 
+/// This matcher will match any function that is marked as allowed to take new
+/// raw ref counted pointers as argument.
+AST_MATCHER(FunctionDecl, canTakeNewRawRefCounted) {
+  return hasCustomAnnotation(&Node, "moz_can_take_new_raw_refcounted");
+}
+
+/// This is the custom matcher class corresponding to hasNonTrivialParent.
+template <typename T, typename ParentT>
+class HasNonTrivialParentMatcher : public internal::WrapperMatcherInterface<T> {
+  static_assert(internal::IsBaseType<ParentT>::value,
+                "has parent only accepts base type matcher");
+
+public:
+  explicit HasNonTrivialParentMatcher(
+      const internal::Matcher<ParentT> &NonTrivialParentMatcher)
+      : HasNonTrivialParentMatcher::WrapperMatcherInterface(
+            NonTrivialParentMatcher) {}
+
+  bool matches(const T &Node, internal::ASTMatchFinder *Finder,
+               internal::BoundNodesTreeBuilder *Builder) const override {
+    auto NewNode = IgnoreParentTrivials(Node, &Finder->getASTContext());
+
+    // We return the result of the inner matcher applied to the new node.
+    return this->InnerMatcher.matches(NewNode, Finder, Builder);
+  }
+};
+
+/// This matcher acts like hasParent, except it skips trivial constructs by
+/// traversing the AST tree upwards.
+const internal::ArgumentAdaptingMatcherFunc<
+    HasNonTrivialParentMatcher,
+    internal::TypeList<Decl, NestedNameSpecifierLoc, Stmt, TypeLoc>,
+    internal::TypeList<Decl, NestedNameSpecifierLoc, Stmt, TypeLoc>>
+    LLVM_ATTRIBUTE_UNUSED hasNonTrivialParent = {};
+
 /// This matcher will match any function declaration that is marked to prohibit
 /// calling AddRef or Release on its return value.
 AST_MATCHER(FunctionDecl, hasNoAddRefReleaseOnReturnAttr) {
-  return hasCustomAnnotation(&Node,
-                                         "moz_no_addref_release_on_return");
+  return hasCustomAnnotation(&Node, "moz_no_addref_release_on_return");
 }
 
 /// This matcher will match all arithmetic binary operators.
 AST_MATCHER(BinaryOperator, binaryArithmeticOperator) {
   BinaryOperatorKind OpCode = Node.getOpcode();
   return OpCode == BO_Mul || OpCode == BO_Div || OpCode == BO_Rem ||
          OpCode == BO_Add || OpCode == BO_Sub || OpCode == BO_Shl ||
          OpCode == BO_Shr || OpCode == BO_And || OpCode == BO_Xor ||
@@ -77,21 +111,17 @@ AST_MATCHER(QualType, isFloat) { return 
 /// with old clangs that we use on infra.
 AST_MATCHER(BinaryOperator, isInSystemHeader) {
   return ASTIsInSystemHeader(Finder->getASTContext(), Node);
 }
 
 /// This matcher will match a list of files.  These files contain
 /// known NaN-testing expressions which we would like to whitelist.
 AST_MATCHER(BinaryOperator, isInWhitelistForNaNExpr) {
-  const char* whitelist[] = {
-    "SkScalar.h",
-    "json_writer.cpp",
-    "State.cpp"
-  };
+  const char *whitelist[] = {"SkScalar.h", "json_writer.cpp", "State.cpp"};
 
   SourceLocation Loc = Node.getOperatorLoc();
   auto &SourceManager = Finder->getASTContext().getSourceManager();
   SmallString<1024> FileName = SourceManager.getFilename(Loc);
 
   for (auto itr = std::begin(whitelist); itr != std::end(whitelist); itr++) {
     if (llvm::sys::path::rbegin(FileName)->equals(*itr)) {
       return true;
@@ -107,21 +137,25 @@ AST_MATCHER(MemberExpr, isAddRefOrReleas
   CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Member);
   if (Method) {
     const auto &Name = getNameChecked(Method);
     return Name == "AddRef" || Name == "Release";
   }
   return false;
 }
 
-/// This matcher will select classes which are refcounted.
+/// This matcher will select classes which are refcounted AND have a ref count
+/// member.
 AST_MATCHER(CXXRecordDecl, hasRefCntMember) {
   return isClassRefCounted(&Node) && getClassRefCntMember(&Node);
 }
 
+/// This matcher will select classes which are refcounted.
+AST_MATCHER(CXXRecordDecl, isRefCounted) { return isClassRefCounted(&Node); }
+
 AST_MATCHER(QualType, hasVTable) { return typeHasVTable(Node); }
 
 AST_MATCHER(CXXRecordDecl, hasNeedsNoVTableTypeAttr) {
   return hasCustomAnnotation(&Node, "moz_needs_no_vtable_type");
 }
 
 /// This matcher will select classes which are non-memmovable
 AST_MATCHER(QualType, isNonMemMovable) {
@@ -179,57 +213,73 @@ AST_MATCHER(CXXConstructorDecl, isExplic
 AST_MATCHER(CXXConstructorDecl, isCompilerProvidedCopyConstructor) {
   return !Node.isUserProvided() && Node.isCopyConstructor();
 }
 
 AST_MATCHER(CallExpr, isAssertAssignmentTestFunc) {
   static const std::string AssertName = "MOZ_AssertAssignmentTest";
   const FunctionDecl *Method = Node.getDirectCallee();
 
-  return Method
-      && Method->getDeclName().isIdentifier()
-      && Method->getName() == AssertName;
+  return Method && Method->getDeclName().isIdentifier() &&
+         Method->getName() == AssertName;
 }
 
 AST_MATCHER(CallExpr, isSnprintfLikeFunc) {
   static const std::string Snprintf = "snprintf";
   static const std::string Vsnprintf = "vsnprintf";
   const FunctionDecl *Func = Node.getDirectCallee();
 
   if (!Func || isa<CXXMethodDecl>(Func)) {
     return false;
   }
 
   StringRef Name = getNameChecked(Func);
   if (Name != Snprintf && Name != Vsnprintf) {
     return false;
   }
 
-  return !isIgnoredPathForSprintfLiteral(&Node, Finder->getASTContext().getSourceManager());
+  return !isIgnoredPathForSprintfLiteral(
+      &Node, Finder->getASTContext().getSourceManager());
 }
 
-AST_MATCHER(CXXRecordDecl, isLambdaDecl) {
-  return Node.isLambda();
+AST_MATCHER(CXXRecordDecl, isLambdaDecl) { return Node.isLambda(); }
+
+AST_MATCHER(QualType, isRefPtr) {
+  auto *D = getNonTemplateSpecializedCXXRecordDecl(Node);
+  if (!D) {
+    return false;
+  }
+
+  D = D->getCanonicalDecl();
+
+  return D && hasCustomAnnotation(D, "moz_is_refptr");
 }
 
-AST_MATCHER(QualType, isRefPtr) {
-  return typeIsRefPtr(Node);
+AST_MATCHER(QualType, isSmartPtrToRefCounted) {
+  auto *D = getNonTemplateSpecializedCXXRecordDecl(Node);
+  if (!D) {
+    return false;
+  }
+
+  D = D->getCanonicalDecl();
+
+  return D && (hasCustomAnnotation(D, "moz_is_refptr") ||
+               hasCustomAnnotation(D, "moz_is_smartptr_to_refcounted"));
 }
 
 AST_MATCHER(CXXRecordDecl, hasBaseClasses) {
   const CXXRecordDecl *Decl = Node.getCanonicalDecl();
 
   // Must have definition and should inherit other classes
   return Decl && Decl->hasDefinition() && Decl->getNumBases();
 }
 
 AST_MATCHER(CXXMethodDecl, isRequiredBaseMethod) {
   const CXXMethodDecl *Decl = Node.getCanonicalDecl();
-  return Decl
-      && hasCustomAnnotation(Decl, "moz_required_base_method");
+  return Decl && hasCustomAnnotation(Decl, "moz_required_base_method");
 }
 
 AST_MATCHER(CXXMethodDecl, isNonVirtual) {
   const CXXMethodDecl *Decl = Node.getCanonicalDecl();
   return Decl && !Decl->isVirtual();
 }
 
 AST_MATCHER(FunctionDecl, isMozMustReturnFromCaller) {
new file mode 100644
--- /dev/null
+++ b/build/clang-plugin/RefCountedToRawAssignmentChecker.cpp
@@ -0,0 +1,157 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RefCountedToRawAssignmentChecker.h"
+#include "CustomMatchers.h"
+
+void RefCountedToRawAssignmentChecker::registerMatchers(
+    MatchFinder *AstMatcher) {
+  AstMatcher->addMatcher(
+      // This is a matcher on a new expression,
+      cxxNewExpr(
+          // which points to a class or struct,
+          hasType(pointsTo(cxxRecordDecl(
+              // and which is refcounted.
+              isRefCounted()))),
+
+          // and which doesn't have an ancestor,
+          unless(hasAncestor(
+              // which is a (possibly implicit) constructor expression,
+              cxxConstructExpr(
+                  // which is a ref counted pointer.
+                  hasType(isSmartPtrToRefCounted())))),
+
+          // and which doesn't have an ancestor,
+          unless(hasAncestor(
+              // which is a call expression,
+              callExpr(
+                  // which is allowed to take new raw refcounted pointers.
+                  callee(functionDecl(canTakeNewRawRefCounted()))))),
+
+          // and which optionally has a corresponding declaration.
+          anyOf(
+              hasNonTrivialParent(varDecl().bind("decl")),
+              // Here using hasParent(cxxConstructorInit()) doesn't work, so
+              // we need to match the constructor declaration and then find
+              // the initializer in the check() method.
+              hasNonTrivialParent(cxxConstructorDecl().bind("ctorDecl")),
+              hasNonTrivialParent(callExpr().bind("callExpr")),
+              // Assignment
+              hasNonTrivialParent(binaryOperator(
+                  hasOperatorName("="),
+                  hasLHS(
+                      // This is a bit basic but probably sufficient until we
+                      // have more powerful helpers to be able to have a full
+                      // coverage of this kind of cases.
+                      anyOf(declRefExpr(to(decl().bind("decl"))),
+                            memberExpr(hasDeclaration(decl().bind("decl"))))))),
+              // We put anything as the last option in the anyOf to basically
+              // make the previous declarations optional (there are cases
+              // where the new expression is not within a declaration, or
+              // within one we can't handle).
+              anything()),
+
+          expr().bind("newExpr")),
+      this);
+}
+
+void RefCountedToRawAssignmentChecker::check(
+    const MatchFinder::MatchResult &Result) {
+  const char *Error = "`new %0` must be immediately constructed into a smart "
+                      "pointer";
+  const char *Note = "consider making %0 a %1";
+
+  const CXXNewExpr *NewExpression =
+      Result.Nodes.getNodeAs<CXXNewExpr>("newExpr");
+
+  // Optional object bindings.
+  const DeclaratorDecl *Declaration =
+      Result.Nodes.getNodeAs<DeclaratorDecl>("decl");
+  const CallExpr *Call = Result.Nodes.getNodeAs<CallExpr>("callExpr");
+  const CXXConstructorDecl *CtorDeclaration =
+      Result.Nodes.getNodeAs<CXXConstructorDecl>("ctorDecl");
+
+  // Just in case.
+  if (!NewExpression) {
+    return;
+  }
+
+  // We need this to build a printing policy for C++ so that we have
+  // `<class name>` instead of `class <class name>`.
+  LangOptions LO;
+  PrintingPolicy PP(LO);
+  PP.adjustForCPlusPlus();
+
+  // We emit the error diagnostic indicating that we are assigning a refcounted
+  // object to a raw pointer.
+  diag(NewExpression->getStartLoc(), Error, DiagnosticIDs::Error)
+      << NewExpression->getAllocatedType().getAsString(PP);
+
+  // In case we don't directly have a declaration object, we might have to get
+  // it indirectly from the other objects we bound to the matches.
+  if (!Declaration) {
+    if (Call) {
+      // If this is a call, we get the param declaration from the call
+      // expression.
+      auto FuncDecl = Call->getDirectCallee();
+      if (!FuncDecl) {
+        return;
+      }
+
+      unsigned ArgNum = 0;
+      for (auto Arg : Call->arguments()) {
+        if (IgnoreTrivials(Arg) == NewExpression) {
+          if (ArgNum < FuncDecl->getNumParams()) {
+            Declaration = FuncDecl->getParamDecl(ArgNum);
+          }
+          // We break regardless of the previous condition, because if that
+          // expression is false, it will be false for all the next iterations
+          // as well.
+          break;
+        }
+
+        ++ArgNum;
+      }
+    } else if (CtorDeclaration) {
+      // If this is a constructor declaration, we get the field declaration from
+      // the constructor initializer.
+      for (auto Init : CtorDeclaration->inits()) {
+        if (IgnoreTrivials(Init->getInit()) == NewExpression) {
+          Declaration = Init->getMember();
+          break;
+        }
+      }
+    } else {
+      // No declaration, no call expression and no constructor declaration:
+      // we cannot suggest a fix.
+      return;
+    }
+
+    // Somehow we didn't find the corresponding declaration.
+    if (!Declaration) {
+      return;
+    }
+  }
+
+  StringRef FixitType;
+  if (Call) {
+    // If we are within a call we want to emit a note to change the type to
+    // already_AddRefed.
+    FixitType = "already_AddRefed";
+  } else {
+    // Otherwise we want to change the type to RefPtr.
+    FixitType = "RefPtr";
+  }
+
+  // We emit the note indicating the possible fix, along with the replacement
+  // hint to turn the raw pointer into the new type.
+  diag(Declaration->getLocation(), Note, DiagnosticIDs::Note)
+      << Declaration->getName()
+      << FixitType
+      << FixItHint::CreateReplacement(
+          Declaration->getTypeSourceInfo()->getTypeLoc().getSourceRange(),
+          FixitType.str() + "<" +
+              Declaration->getType()->getPointeeType().getAsString(PP) +
+              ">");
+}
new file mode 100644
--- /dev/null
+++ b/build/clang-plugin/RefCountedToRawAssignmentChecker.h
@@ -0,0 +1,19 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef RefCountedToRawAssignmentChecker_h__
+#define RefCountedToRawAssignmentChecker_h__
+
+#include "plugin.h"
+
+class RefCountedToRawAssignmentChecker : public BaseCheck {
+public:
+  RefCountedToRawAssignmentChecker(StringRef CheckName,
+                                   ContextType *Context = nullptr)
+      : BaseCheck(CheckName, Context) {}
+  void registerMatchers(MatchFinder *AstMatcher) override;
+  void check(const MatchFinder::MatchResult &Result) override;
+};
+
+#endif
--- a/build/clang-plugin/Utils.h
+++ b/build/clang-plugin/Utils.h
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef Utils_h__
 #define Utils_h__
 
+#include "ThirdPartyPaths.h"
 #include "plugin.h"
-#include "ThirdPartyPaths.h"
 
 // Check if the given expression contains an assignment expression.
 // This can either take the form of a Binary Operator or a
 // Overloaded Operator Call.
 inline bool hasSideEffectAssignment(const Expr *Expression) {
   if (auto OpCallExpr = dyn_cast_or_null<CXXOperatorCallExpr>(Expression)) {
     auto BinOp = OpCallExpr->getOperator();
     if (BinOp == OO_Equal || (BinOp >= OO_PlusEqual && BinOp <= OO_PipeEqual)) {
@@ -29,27 +29,27 @@ inline bool hasSideEffectAssignment(cons
     if (ChildExpr && hasSideEffectAssignment(ChildExpr)) {
       return true;
     }
   }
 
   return false;
 }
 
-template <class T> inline bool ASTIsInSystemHeader(const ASTContext &AC, const T &D) {
+template <class T>
+inline bool ASTIsInSystemHeader(const ASTContext &AC, const T &D) {
   auto &SourceManager = AC.getSourceManager();
   auto ExpansionLoc = SourceManager.getExpansionLoc(D.getLocStart());
   if (ExpansionLoc.isInvalid()) {
     return false;
   }
   return SourceManager.isInSystemHeader(ExpansionLoc);
 }
 
-template<typename T>
-inline StringRef getNameChecked(const T& D) {
+template <typename T> inline StringRef getNameChecked(const T &D) {
   return D->getIdentifier() ? D->getName() : "";
 }
 
 /// A cached data of whether classes are refcounted or not.
 typedef DenseMap<const CXXRecordDecl *, std::pair<const Decl *, bool>>
     RefCountedMap;
 extern RefCountedMap RefCountedClasses;
 
@@ -224,17 +224,18 @@ inline bool isIgnoredPathForImplicitConv
       // Ignore security/sandbox/chromium but not ipc/chromium.
       ++Begin;
       return Begin != End && Begin->compare_lower(StringRef("sandbox")) == 0;
     }
   }
   return false;
 }
 
-inline bool isIgnoredPathForSprintfLiteral(const CallExpr *Call, const SourceManager &SM) {
+inline bool isIgnoredPathForSprintfLiteral(const CallExpr *Call,
+                                           const SourceManager &SM) {
   SourceLocation Loc = Call->getLocStart();
   SmallString<1024> FileName = SM.getFilename(Loc);
   llvm::sys::fs::make_absolute(FileName);
   llvm::sys::path::reverse_iterator Begin = llvm::sys::path::rbegin(FileName),
                                     End = llvm::sys::path::rend(FileName);
   for (; Begin != End; ++Begin) {
     if (Begin->compare_lower(StringRef("angle")) == 0 ||
         Begin->compare_lower(StringRef("chromium")) == 0 ||
@@ -289,27 +290,21 @@ inline bool isIgnoredExprForMustUse(cons
 
   if (const BinaryOperator *Op = dyn_cast<BinaryOperator>(E)) {
     return Op->isAssignmentOp();
   }
 
   return false;
 }
 
-inline bool typeIsRefPtr(QualType Q) {
-  CXXRecordDecl *D = Q->getAsCXXRecordDecl();
-  if (!D || !D->getIdentifier()) {
-    return false;
-  }
-
-  StringRef name = D->getName();
-  if (name == "RefPtr" || name == "nsCOMPtr") {
-    return true;
-  }
-  return false;
+// This method returns true if the statement is trivial.
+inline bool IsTrivial(const Stmt *s) {
+  return s && (isa<ExprWithCleanups>(s) || isa<MaterializeTemporaryExpr>(s) ||
+               isa<CXXBindTemporaryExpr>(s) || isa<ImplicitCastExpr>(s) ||
+               isa<ParenExpr>(s));
 }
 
 // The method defined in clang for ignoring implicit nodes doesn't work with
 // some AST trees. To get around this, we define our own implementation of
 // IgnoreTrivials.
 inline const Stmt *IgnoreTrivials(const Stmt *s) {
   while (true) {
     if (!s) {
@@ -324,23 +319,47 @@ inline const Stmt *IgnoreTrivials(const 
       s = ice->getSubExpr();
     } else if (auto *pe = dyn_cast<ParenExpr>(s)) {
       s = pe->getSubExpr();
     } else {
       break;
     }
   }
 
+  assert(!IsTrivial(s));
   return s;
 }
 
 inline const Expr *IgnoreTrivials(const Expr *e) {
   return cast<Expr>(IgnoreTrivials(static_cast<const Stmt *>(e)));
 }
 
+// This method is like IgnoreTrivials but ignores the nodes upwards instead of
+// downwards.
+template <typename T>
+inline ast_type_traits::DynTypedNode IgnoreParentTrivials(const T &Node,
+                                                          ASTContext *Context) {
+  // We traverse the AST upward until we encounter a non-trivial node.
+  auto CurrentNode = ast_type_traits::DynTypedNode::create(Node);
+  do {
+    // We get the parents of the current node from the AST context.
+    auto Parents = Context->getParents(CurrentNode);
+
+    // Not implemented yet, but probably not very useful for the cases where
+    // we use this matcher.
+    if (Parents.size() != 1) {
+      break;
+    }
+
+    CurrentNode = Parents[0];
+  } while (IsTrivial(CurrentNode.template get<Stmt>()));
+
+  return CurrentNode;
+}
+
 const FieldDecl *getBaseRefCntMember(QualType T);
 
 inline const FieldDecl *getBaseRefCntMember(const CXXRecordDecl *D) {
   const FieldDecl *RefCntMember = getClassRefCntMember(D);
   if (RefCntMember && isClassRefCounted(D)) {
     return RefCntMember;
   }
 
@@ -375,18 +394,17 @@ inline bool hasCustomAnnotation(const De
   return false;
 }
 
 inline bool isPlacementNew(const CXXNewExpr *Expression) {
   // Regular new expressions aren't placement new
   if (Expression->getNumPlacementArgs() == 0)
     return false;
   const FunctionDecl *Declaration = Expression->getOperatorNew();
-  if (Declaration && hasCustomAnnotation(Declaration,
-                 "moz_heap_allocator")) {
+  if (Declaration && hasCustomAnnotation(Declaration, "moz_heap_allocator")) {
     return false;
   }
   return true;
 }
 
 inline bool inThirdPartyPath(SourceLocation Loc, const SourceManager &SM) {
   SmallString<1024> FileName = SM.getFilename(Loc);
   llvm::sys::fs::make_absolute(FileName);
@@ -423,9 +441,32 @@ inline bool inThirdPartyPath(SourceLocat
 inline bool inThirdPartyPath(const Decl *D) {
   D = D->getCanonicalDecl();
   SourceLocation Loc = D->getLocation();
   const SourceManager &SM = D->getASTContext().getSourceManager();
 
   return inThirdPartyPath(Loc, SM);
 }
 
+inline CXXRecordDecl* getNonTemplateSpecializedCXXRecordDecl(QualType Q) {
+  auto *D = Q->getAsCXXRecordDecl();
+
+  if (!D) {
+    auto TemplateQ = Q->getAs<TemplateSpecializationType>();
+    if (!TemplateQ) {
+      return nullptr;
+    }
+
+    auto TemplateDecl = TemplateQ->getTemplateName().getAsTemplateDecl();
+    if (!TemplateDecl) {
+      return nullptr;
+    }
+
+    D = dyn_cast_or_null<CXXRecordDecl>(TemplateDecl->getTemplatedDecl());
+    if (!D) {
+      return nullptr;
+    }
+  }
+
+  return D;
+}
+
 #endif
--- a/build/clang-plugin/moz.build
+++ b/build/clang-plugin/moz.build
@@ -28,16 +28,17 @@ UNIFIED_SOURCES += [
     'NoExplicitMoveConstructorChecker.cpp',
     'NonMemMovableMemberChecker.cpp',
     'NonMemMovableTemplateArgChecker.cpp',
     'NonParamInsideFunctionDeclChecker.cpp',
     'OverrideBaseCallChecker.cpp',
     'OverrideBaseCallUsageChecker.cpp',
     'RefCountedCopyConstructorChecker.cpp',
     'RefCountedInsideLambdaChecker.cpp',
+    'RefCountedToRawAssignmentChecker.cpp',
     'ScopeChecker.cpp',
     'SprintfLiteralChecker.cpp',
     'TrivialCtorDtorChecker.cpp',
 ]
 
 GENERATED_FILES += ['ThirdPartyPaths.cpp']
 third_party_paths = GENERATED_FILES['ThirdPartyPaths.cpp']
 third_party_paths.script = "ThirdPartyPaths.py:generate"
--- a/build/clang-plugin/tests/TestKungFuDeathGrip.cpp
+++ b/build/clang-plugin/tests/TestKungFuDeathGrip.cpp
@@ -1,29 +1,30 @@
 #define MOZ_IMPLICIT __attribute__((annotate("moz_implicit")))
+#define MOZ_IS_REFPTR __attribute__((annotate("moz_is_refptr")))
 
 template <typename T>
 class already_AddRefed {
 public:
   already_AddRefed();
   T* mPtr;
 };
 
 template <typename T>
-class RefPtr {
+class MOZ_IS_REFPTR RefPtr {
 public:
   RefPtr();
   MOZ_IMPLICIT RefPtr(T* aIn);
   MOZ_IMPLICIT RefPtr(already_AddRefed<T> aIn);
   ~RefPtr();
   T* mPtr;
 };
 
 template <typename T>
-class nsCOMPtr {
+class MOZ_IS_REFPTR nsCOMPtr {
 public:
   nsCOMPtr();
   MOZ_IMPLICIT nsCOMPtr(T* aIn);
   MOZ_IMPLICIT nsCOMPtr(already_AddRefed<T> aIn);
   ~nsCOMPtr();
   T* mPtr;
 };
 
--- a/build/clang-plugin/tests/TestRefCountedCopyConstructor.cpp
+++ b/build/clang-plugin/tests/TestRefCountedCopyConstructor.cpp
@@ -1,25 +1,31 @@
+#include <mozilla/RefPtr.h>
+
 // Implicit copy construct which is unused
 class RC1 {
+public:
   void AddRef();
   void Release();
+
+private:
   int mRefCnt;
 };
 
 // Explicit copy constructor which is used
 class RC2 {
 public:
   RC2();
   RC2(const RC2&);
-private:
   void AddRef();
   void Release();
+
+private:
   int mRefCnt;
 };
 
 void f() {
-  RC1* r1 = new RC1();
-  RC1* r1p = new RC1(*r1); // expected-error {{Invalid use of compiler-provided copy constructor on refcounted type}} expected-note {{The default copy constructor also copies the default mRefCnt property, leading to reference count imbalance issues. Please provide your own copy constructor which only copies the fields which need to be copied}}
+  RefPtr<RC1> r1 = new RC1();
+  RefPtr<RC1> r1p = new RC1(*r1); // expected-error {{Invalid use of compiler-provided copy constructor on refcounted type}} expected-note {{The default copy constructor also copies the default mRefCnt property, leading to reference count imbalance issues. Please provide your own copy constructor which only copies the fields which need to be copied}}
 
-  RC2* r2 = new RC2();
-  RC2* r2p = new RC2(*r2);
+  RefPtr<RC2> r2 = new RC2();
+  RefPtr<RC2> r2p = new RC2(*r2);
 }
new file mode 100644
--- /dev/null
+++ b/build/clang-plugin/tests/TestRefCountedToRawAssignment.cpp
@@ -0,0 +1,53 @@
+#include <mozilla/RefPtr.h>
+
+class RC1 {
+public:
+  void AddRef() {}
+  void Release() {}
+
+private:
+  int mRefCnt;
+};
+
+class RC2 : public RC1 {};
+
+class Test {
+public:
+  Test() : ptr(new RC1), refptr(new RC1) { // expected-error {{`new RC1` must be immediately constructed into a smart pointer}}
+    ptr2 = new RC1; // expected-error {{`new RC1` must be immediately constructed into a smart pointer}}
+  }
+
+private:
+  RC1 *ptr;  // expected-note {{consider making ptr a RefPtr}}
+  RC1 *ptr2; // expected-note {{consider making ptr2 a RefPtr}}
+
+  RefPtr<RC1> refptr;
+};
+
+void unused(RefPtr<RC1> refptr) {}
+
+void Func1(RC1 *ptr) {} // expected-note {{consider making ptr a already_AddRefed}}
+
+void TestFunc() {
+  new RC1; // expected-error {{`new RC1` must be immediately constructed into a smart pointer}}
+  RC1 *ptr = new RC1; // expected-error {{`new RC1` must be immediately constructed into a smart pointer}} \
+                         expected-note {{consider making ptr a RefPtr}}
+
+  RC1 *ptr2(new RC1); // expected-error {{`new RC1` must be immediately constructed into a smart pointer}} \
+                         expected-note {{consider making ptr2 a RefPtr}}
+
+  RC1 *ptr3;      // expected-note {{consider making ptr3 a RefPtr}}
+  ptr3 = new RC1; // expected-error {{`new RC1` must be immediately constructed into a smart pointer}}
+
+  RC1 *ptr4 = new RC2; // expected-error {{`new RC2` must be immediately constructed into a smart pointer}} \
+                          expected-note {{consider making ptr4 a RefPtr}}
+
+  RefPtr<RC1> refptr(static_cast<RC1 *>(new RC2));
+  unused(refptr);
+
+  RefPtr<RC1> refptr2(new RC1);
+
+  Func1(new RC1); // expected-error {{`new RC1` must be immediately constructed into a smart pointer}}
+
+  already_AddRefed<RC1> AddRefed = do_AddRef(new RC1);
+}
--- a/build/clang-plugin/tests/moz.build
+++ b/build/clang-plugin/tests/moz.build
@@ -33,15 +33,16 @@ SOURCES += [
     'TestNonMemMovableStd.cpp',
     'TestNonMemMovableStdAtomic.cpp',
     'TestNonParameterChecker.cpp',
     'TestNonTemporaryClass.cpp',
     'TestNoRefcountedInsideLambdas.cpp',
     'TestOverrideBaseCall.cpp',
     'TestOverrideBaseCallAnnotation.cpp',
     'TestRefCountedCopyConstructor.cpp',
+    'TestRefCountedToRawAssignment.cpp',
     'TestSprintfLiteral.cpp',
     'TestStackClass.cpp',
     'TestTrivialCtorDtor.cpp',
 ]
 
 DISABLE_STL_WRAPPING = True
 NO_VISIBILITY_FLAGS = True
--- a/mfbt/Attributes.h
+++ b/mfbt/Attributes.h
@@ -468,16 +468,23 @@
  *   non-trivial constructor or destructor for any reason.
  * MOZ_HEAP_ALLOCATOR: Applies to any function. This indicates that the return
  *   value is allocated on the heap, and will as a result check such allocations
  *   during MOZ_STACK_CLASS and MOZ_NONHEAP_CLASS annotation checking.
  * MOZ_IMPLICIT: Applies to constructors. Implicit conversion constructors
  *   are disallowed by default unless they are marked as MOZ_IMPLICIT. This
  *   attribute must be used for constructors which intend to provide implicit
  *   conversions.
+ * MOZ_CAN_TAKE_NEW_RAW_REFCOUNTED: Applies to function declarations which
+*   should be allowed to take new raw refcounted pointers.
+ * MOZ_IS_REFPTR: Applies to class declarations of ref pointer to mark them as
+ *   such for use with static-analysis.
+ * MOZ_IS_SMARTPTR_TO_REFCOUNTED: Applies to class declarations of smart
+ *   pointers to ref counted classes to mark them as such for use with
+ *   static-analysis.
  * MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT: Applies to functions. Makes it a compile
  *   time error to pass arithmetic expressions on variables to the function.
  * MOZ_OWNING_REF: Applies to declarations of pointers to reference counted
  *   types.  This attribute tells the compiler that the raw pointer is a strong
  *   reference, where ownership through methods such as AddRef and Release is
  *   managed manually.  This can make the compiler ignore these pointers when
  *   validating the usage of pointers otherwise.
  *
@@ -572,16 +579,19 @@
 #  ifdef DEBUG
      /* in debug builds, these classes do have non-trivial constructors. */
 #    define MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS __attribute__((annotate("moz_global_class")))
 #  else
 #    define MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS __attribute__((annotate("moz_global_class"))) \
             MOZ_TRIVIAL_CTOR_DTOR
 #  endif
 #  define MOZ_IMPLICIT __attribute__((annotate("moz_implicit")))
+#  define MOZ_CAN_TAKE_NEW_RAW_REFCOUNTED __attribute__((annotate("moz_can_take_new_raw_refcounted")))
+#  define MOZ_IS_REFPTR __attribute__((annotate("moz_is_refptr")))
+#  define MOZ_IS_SMARTPTR_TO_REFCOUNTED __attribute__((annotate("moz_is_smartptr_to_refcounted")))
 #  define MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT __attribute__((annotate("moz_no_arith_expr_in_arg")))
 #  define MOZ_OWNING_REF __attribute__((annotate("moz_strong_ref")))
 #  define MOZ_NON_OWNING_REF __attribute__((annotate("moz_weak_ref")))
 #  define MOZ_UNSAFE_REF(reason) __attribute__((annotate("moz_weak_ref")))
 #  define MOZ_NO_ADDREF_RELEASE_ON_RETURN __attribute__((annotate("moz_no_addref_release_on_return")))
 #  define MOZ_MUST_USE_TYPE __attribute__((annotate("moz_must_use_type")))
 #  define MOZ_NEEDS_NO_VTABLE_TYPE __attribute__((annotate("moz_needs_no_vtable_type")))
 #  define MOZ_NON_MEMMOVABLE __attribute__((annotate("moz_non_memmovable")))
@@ -616,16 +626,19 @@
 #  define MOZ_MUST_OVERRIDE /* nothing */
 #  define MOZ_STACK_CLASS /* nothing */
 #  define MOZ_NONHEAP_CLASS /* nothing */
 #  define MOZ_HEAP_CLASS /* nothing */
 #  define MOZ_NON_TEMPORARY_CLASS /* nothing */
 #  define MOZ_TRIVIAL_CTOR_DTOR /* nothing */
 #  define MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS /* nothing */
 #  define MOZ_IMPLICIT /* nothing */
+#  define MOZ_CAN_TAKE_NEW_RAW_REFCOUNTED /* nothing */
+#  define MOZ_IS_SMARTPTR_TO_REFCOUNTED /* nothing */
+#  define MOZ_IS_REFPTR /* nothing */
 #  define MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT /* nothing */
 #  define MOZ_HEAP_ALLOCATOR /* nothing */
 #  define MOZ_OWNING_REF /* nothing */
 #  define MOZ_NON_OWNING_REF /* nothing */
 #  define MOZ_UNSAFE_REF(reason) /* nothing */
 #  define MOZ_NO_ADDREF_RELEASE_ON_RETURN /* nothing */
 #  define MOZ_MUST_USE_TYPE /* nothing */
 #  define MOZ_NEEDS_NO_VTABLE_TYPE /* nothing */
--- a/mfbt/RefPtr.h
+++ b/mfbt/RefPtr.h
@@ -39,17 +39,17 @@ struct RefPtrTraits
   static void Release(U* aPtr) {
     aPtr->Release();
   }
 };
 
 } // namespace mozilla
 
 template <class T>
-class RefPtr
+class MOZ_IS_REFPTR RefPtr
 {
 private:
   void
   assign_with_AddRef(T* aRawPtr)
   {
     if (aRawPtr) {
       ConstRemovingRefPtrTraits<T>::AddRef(aRawPtr);
     }
@@ -609,17 +609,17 @@ operator!=(decltype(nullptr), const RefP
 {
   return nullptr != aRhs.get();
 }
 
 /*****************************************************************************/
 
 template <class T>
 inline already_AddRefed<T>
-do_AddRef(T* aObj)
+MOZ_CAN_TAKE_NEW_RAW_REFCOUNTED do_AddRef(T* aObj)
 {
   RefPtr<T> ref(aObj);
   return ref.forget();
 }
 
 template <class T>
 inline already_AddRefed<T>
 do_AddRef(const RefPtr<T>& aObj)
--- a/xpcom/base/OwningNonNull.h
+++ b/xpcom/base/OwningNonNull.h
@@ -10,17 +10,17 @@
 #define mozilla_OwningNonNull_h
 
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionNoteChild.h"
 
 namespace mozilla {
 
 template<class T>
-class OwningNonNull
+class MOZ_IS_SMARTPTR_TO_REFCOUNTED OwningNonNull
 {
 public:
   OwningNonNull() {}
 
   MOZ_IMPLICIT OwningNonNull(T& aValue)
   {
     init(&aValue);
   }
--- a/xpcom/base/nsCOMPtr.h
+++ b/xpcom/base/nsCOMPtr.h
@@ -340,17 +340,17 @@ protected:
 
 // Helper for assert_validity method
 template<class T>
 char (&TestForIID(decltype(&NS_GET_TEMPLATE_IID(T))))[2];
 template<class T>
 char TestForIID(...);
 
 template<class T>
-class nsCOMPtr final
+class MOZ_IS_REFPTR nsCOMPtr final
 #ifdef NSCAP_FEATURE_USE_BASE
   : private nsCOMPtr_base
 #endif
 {
 
 #ifdef NSCAP_FEATURE_USE_BASE
   #define NSCAP_CTOR_BASE(x) nsCOMPtr_base(x)
 #else