diff --git a/clang-tools-extra/CMakeLists.txt b/clang-tools-extra/CMakeLists.txt
--- a/clang-tools-extra/CMakeLists.txt
+++ b/clang-tools-extra/CMakeLists.txt
@@ -8,6 +8,7 @@
 add_subdirectory(modularize)
 add_subdirectory(clang-tidy)
 
+add_subdirectory(clang-cast)
 add_subdirectory(clang-change-namespace)
 add_subdirectory(clang-doc)
 add_subdirectory(clang-include-fixer)
diff --git a/clang-tools-extra/clang-cast/CMakeLists.txt b/clang-tools-extra/clang-cast/CMakeLists.txt
new file mode 100644
--- /dev/null
+++ b/clang-tools-extra/clang-cast/CMakeLists.txt
@@ -0,0 +1,14 @@
+set(LLVM_LINK_COMPONENTS
+        support)
+
+add_clang_tool(clang-cast
+  ClangCast.cpp
+  )
+
+target_link_libraries(clang-cast
+  PRIVATE
+  clangTooling
+  clangBasic
+  clangRewriteFrontend
+  clangASTMatchers
+  )
diff --git a/clang-tools-extra/clang-cast/Cast.h b/clang-tools-extra/clang-cast/Cast.h
new file mode 100644
--- /dev/null
+++ b/clang-tools-extra/clang-cast/Cast.h
@@ -0,0 +1,497 @@
+//===--- Cast.h - clang-cast ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains classes that manage semantics of C style casts.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_CAST_CAST_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_CAST_CAST_H
+
+#include "CastOptions.h"
+#include "CastUtils.h"
+#include "clang/AST/OperationKinds.h"
+#include <exception>
+
+//===----------------------------------------------------------------------===//
+// CStyleCastOperation
+//===----------------------------------------------------------------------===//
+
+namespace clang {
+namespace cppcast {
+
+/// CStyleCastOperation is responsible for capturing a CStyleCastExpr AST node
+/// and perform some basic semantic analysis on the C style cast's equivalent in
+/// C++ explicit cast expressions.
+///
+/// This class does not have any ownership semantics for the CStyleCastExpr
+/// itself, nor does it have ownership semantics for the underlying Context that
+/// the CStyleCastExpr is from.
+///
+/// Note that although some functions are similar to the Sema library, such as
+/// CastsAwayConstness vs. requireConstCast, it is a much simpler version as we
+/// assume compilation is successful and we are restricted to C++. The reason
+/// Sema was not used here is because the anonymous namespaces and static
+/// visibility of the original functions require leaking existing abstractions
+/// and a large refactoring in the fundamental Clang codebase.
+class CStyleCastOperation {
+  /// The main expression captured by a matcher
+  const CStyleCastExpr &MainExpr;
+
+  /// The subexpression type in the C style cast.
+  /// For example:
+  /// \code
+  /// (int) 3.2f;
+  /// \endcode
+  ///
+  /// the subexpression is 3.2f, and its type is float.
+  const QualType SubExprType;
+
+  /// The cast type in the C style cast.
+  /// For example:
+  /// \code
+  /// (int) 3.2f;
+  /// \endcode
+  ///
+  /// the cast type is int.
+  const QualType CastType;
+
+  /// The context that the CStyleCastExpr belongs to.
+  const ASTContext &Context;
+
+  /// The engine in the context responsible for printing diagnostics
+  const DiagnosticsEngine &DiagEngine;
+
+  /// Whether or not the generated code should comply with -pedantic
+  const bool Pedantic;
+
+public:
+  /// Initializes a new CStyleCastOperation
+  /// \param CastExpr node to perform semantic analysis on
+  /// \param Context the context that CastExpr belongs to
+  /// \param Pedantic whether we should generate code that complies with
+  /// -pedantic and -pedantic-errors
+  ///
+  /// Note that we are canonicalizing the subexpression and cast types. We don't
+  /// yet provide typedef-preserving code generation.
+  CStyleCastOperation(const CStyleCastExpr &CastExpr, const ASTContext &Context,
+                      const bool Pedantic)
+      : MainExpr(CastExpr),
+        SubExprType(
+            CastExpr.getSubExprAsWritten()->getType().getCanonicalType()),
+        CastType(CastExpr.getTypeAsWritten().getCanonicalType()),
+        Context(Context), DiagEngine(Context.getDiagnostics()),
+        Pedantic(Pedantic) {}
+
+  CStyleCastOperation() = delete;
+  CStyleCastOperation(const CStyleCastOperation &) = delete;
+  CStyleCastOperation(CStyleCastOperation &&) = delete;
+  CStyleCastOperation &operator=(const CStyleCastOperation &) = delete;
+  CStyleCastOperation &operator=(CStyleCastOperation &&) = delete;
+  virtual ~CStyleCastOperation() = default;
+
+  /// public accessor methods, some of which return reference handles.
+  QualType getSubExprType() const { return SubExprType; }
+  QualType getCastType() const { return CastType; }
+  const CStyleCastExpr &getCStyleCastExpr() const { return MainExpr; }
+  const Expr &getSubExprAsWritten() const {
+    return *MainExpr.getSubExprAsWritten();
+  }
+  const ASTContext &getContext() const { return Context; }
+
+  /// This method tests to see whether the conversion from SubExprType to
+  /// CastType is "casting away constness", as the standard describes.
+  ///
+  /// The standard states that a conversion is casting away constness when there
+  /// exists a cv-decomposition such that there exists no implicit qualification
+  /// conversion from SubExprType to CastType.
+  ///
+  /// Clang, without -pedantic or -pedantic-error, allows a few exceptional
+  /// cases, such as when two nested pointers diverge in type (pointer vs.
+  /// array, for example), and the rest of the qualifiers are ignored in the
+  /// cast-away-constness consideration. (exception: two member-pointers that
+  /// are pointers to different classes are considered the same "type").
+  ///
+  /// Some other notable exceptions include variable-length arrays (VLA) being
+  /// used in non-pedantic codebases, which we also allow.
+  bool requireConstCast() const;
+
+  /// This converts From(the cast type) into To(subexpression type)'s qualifiers
+  /// to prepare for const_cast.
+  /// \return QualType corresponding to CastType but with minimal qualifiers to
+  /// satisfy for a non-pedantic conversion.
+  ///
+  /// For example:
+  /// \code
+  /// const int* const (* const x) [2];
+  /// (int***) x;
+  /// \endcode
+  ///
+  /// will yield (int* const* const* const) for typical, non-pedantic usage
+  /// because const_cast will only modify the qualifiers where they are
+  /// both locally similar (they stop being similar at array vs. pointer).
+  ///
+  /// We need this to first perform static/reinterpret casts and then const
+  /// cast. In order to do this, we must take the cast type and change its
+  /// qualifiers so that it can be performed by static/reinterpret cast first.
+  ///
+  /// NOTE: There is only one case where we'd need to modify qualifiers and that
+  /// is for function pointers. Const cast cannot change qualifiers on function
+  /// pointers.
+  QualType changeQualifiers() const {
+    return changeQualifierHelper(CastType, SubExprType);
+  }
+
+  /// Given the CStyleCastExpr, determine from its CastKind and other metadata
+  /// to decide what kind of C++ style cast is appropriate, if any. This does
+  /// not take into consideration const_cast and qualifiers. Use
+  /// requireConstCast() to determine whether const_cast should be added.
+  ///
+  /// \return CXXCast corresponding to the type of conversion.
+  CXXCast getCastKindFromCStyleCast() const { return castHelper(&MainExpr); }
+
+private:
+  /// Auxilliary diagnostic methods
+  void warnMemberPointerClass(const QualType &SEType,
+                              const QualType &CType) const;
+  void errorPedanticVLA(const QualType &T) const;
+
+  /// \param SEType, a copy of QualType subexpression
+  /// \param CType, a copy of QualType cast type
+  /// \return true if the const was casted away, false otherwise.
+  ///
+  /// IMPORTANT: We are assuming that reinterpret_cast will have already taken
+  /// care of the downcasting of nested function pointers, and if we have nested
+  /// function pointers, they have the same qualifiers.
+  bool castAwayConst(const QualType &SEType, const QualType &CType) const;
+
+  /// \param From, the CastType, which needs to be modified to not require const
+  /// cast
+  /// \param To, the SubExpression, which has specific qualifiers on it.
+  /// \return QualType mirroring From but with qualifiers on To.
+  QualType changeQualifierHelper(QualType From, const QualType &To) const;
+
+  /// Recurse CastExpr AST nodes until a non-cast expression has been reached.
+  /// \return CXXCast enum corresponding to the lowest power cast required.
+  CXXCast castHelper(const Expr *Expression) const;
+
+  /// Given a CastExpr, determine from its CastKind and other metadata
+  /// the corresponding CXXCast to use (NOTE: this does not include
+  ///     the additional const cast for qualifiers if it is
+  ///     static/interpret. Use isQualifierModified for that.)
+  /// Note that it's a CastExpr, which can be either CStyleCastExpr
+  /// or ImplicitCastExpr or any of its children.
+  ///
+  /// \return CXXCast corresponding to the type of conversion.
+  CXXCast getCastType(const CastExpr *CastExpression) const;
+};
+
+bool CStyleCastOperation::requireConstCast() const {
+  // Case 0 - We just cannot cast function pointers at the very beginning,
+  // regardless of whether it's being downcasted or not.
+  if (details::isFunctionPtr(CastType) || details::isFunctionPtr(SubExprType)) {
+    return false;
+  }
+
+  // We modify the types for references
+  // Note that for our subexpr type, we use the type post-implicit conv.
+  QualType ModifiedImpliedSubExprType =
+      MainExpr.getSubExpr()->getType().getCanonicalType();
+  QualType ModifiedCastType = CastType;
+
+  // Case 1 - reference type:
+  // remove the reference from both the subexpr and cast and add a pointer
+  // level.
+  if (ModifiedCastType->isReferenceType()) {
+    ModifiedCastType = ModifiedCastType.getNonReferenceType();
+    if (ModifiedImpliedSubExprType->isReferenceType()) {
+      ModifiedImpliedSubExprType =
+          ModifiedImpliedSubExprType.getNonReferenceType();
+    }
+    ModifiedCastType = Context.getPointerType(ModifiedCastType);
+    ModifiedImpliedSubExprType =
+        Context.getPointerType(ModifiedImpliedSubExprType);
+  }
+
+  // Case 2, 3 - pointer type & POD type
+  // if the pointer qualifiers are downcasted at any level, then fail.
+  // if the POD qualifiers are downcasted, then fail.
+  return castAwayConst(ModifiedImpliedSubExprType, ModifiedCastType);
+}
+
+void CStyleCastOperation::warnMemberPointerClass(const QualType &SEType,
+                                                 const QualType &CType) const {
+  // Auxilliary: If the member pointers classes are
+  // not the same, issue a warning.
+  if (SEType->isMemberPointerType() && CType->isMemberPointerType()) {
+    const MemberPointerType *MPSEType = dyn_cast<MemberPointerType>(SEType);
+    const Type *SEClass = MPSEType->getClass();
+
+    const MemberPointerType *MPCType = dyn_cast<MemberPointerType>(CType);
+    const Type *CClass = MPCType->getClass();
+
+    if (SEClass->getCanonicalTypeUnqualified() !=
+        CClass->getCanonicalTypeUnqualified()) {
+      reportWithLoc(
+          Context.getDiagnostics(), DiagnosticsEngine::Warning,
+          "C style cast performs a member-to-pointer cast from class %0 to "
+          "%1, which are not equal",
+          MainExpr.getExprLoc(), QualType(SEClass, /*Quals=*/0),
+          QualType(CClass, /*Quals=*/0));
+    }
+  }
+}
+
+void CStyleCastOperation::errorPedanticVLA(const QualType &T) const {
+  if (!Pedantic)
+    return;
+  // Auxilliary: If the type is variable length arrays (VLA)s, it should raise
+  // warnings under --pedantic
+  if (T->isVariableArrayType()) {
+    reportWithLoc(Context.getDiagnostics(), DiagnosticsEngine::Error,
+                  "detected variable length array %0 with --pedantic enabled",
+                  MainExpr.getExprLoc(), T);
+  }
+}
+
+bool CStyleCastOperation::castAwayConst(const QualType &SEType,
+                                        const QualType &CType) const {
+  if (SEType.isMoreQualifiedThan(CType)) {
+    return true;
+  }
+
+  // If we follow clang's extensions, then the moment something is not locally
+  // similar, we consider the source and destination types not equal and so we
+  // can return false for any nested levels.
+  if (!details::isLocallySimilar(SEType, CType) && !Pedantic)
+    return false;
+
+  // Auxiliary warnings during parsing
+  warnMemberPointerClass(SEType, CType);
+  errorPedanticVLA(SEType);
+  errorPedanticVLA(CType);
+
+  if (details::isTerminal(SEType, CType))
+    return false;
+
+  QualType SEStrippedType = details::stripLayer(SEType, Context);
+  QualType CStrippedType = details::stripLayer(CType, Context);
+
+  // Continue recursing for pointer types
+  return castAwayConst(SEStrippedType, CStrippedType);
+}
+
+QualType CStyleCastOperation::changeQualifierHelper(QualType From,
+                                                    const QualType &To) const {
+  // If it's a function pointer, then we don't change the qualifier and we've
+  // reached the end.
+  if (details::isFunctionPtr(From))
+    return From;
+
+  // We're changing qualifiers because it'd be casting away constness.
+  // If we follow the standard, we could be casting away constness down to the
+  // terminal level.
+  // If we follow clang's extensions, we could be casting away constness until
+  // we've reached a non-similar stage.
+  if ((!details::isLocallySimilar(From, To) && !Pedantic) ||
+      details::isTerminal(From, To)) {
+    From.setLocalFastQualifiers(To.getLocalFastQualifiers());
+    return From;
+  }
+
+  auto StrippedTo = details::stripLayer(To, Context);
+  auto StrippedFrom = details::stripLayer(From, Context);
+
+  auto Temp = changeQualifierHelper(StrippedFrom, StrippedTo);
+  // If they are locally similar and non-terminal, we can keep going down
+  if (From->isPointerType()) {
+    // modify the nested types
+    From = Context.getPointerType(Temp);
+  } else if (From->isMemberPointerType()) {
+    const MemberPointerType *MPT = dyn_cast<MemberPointerType>(From);
+    const Type *FromClass = MPT->getClass();
+    // modify the nested types
+    From = Context.getMemberPointerType(Temp, FromClass);
+  } else if (From->isConstantArrayType()) {
+    const ConstantArrayType *AT = dyn_cast<ConstantArrayType>(From);
+    // Don't assign yet because we need to change the qualifiers
+    From = Context.getConstantArrayType(Temp, AT->getSize(), AT->getSizeExpr(),
+                                        AT->getSizeModifier(),
+                                        AT->getIndexTypeCVRQualifiers());
+  } else if (From->isIncompleteArrayType()) {
+    const IncompleteArrayType *IAT = dyn_cast<IncompleteArrayType>(From);
+    From = Context.getIncompleteArrayType(Temp, IAT->getSizeModifier(),
+                                          IAT->getIndexTypeCVRQualifiers());
+  } else if (From->isVariableArrayType()) {
+    // The following can only work if we're not in --pedantic
+    const VariableArrayType *VAT = dyn_cast<VariableArrayType>(From);
+    Context.getVariableArrayType(
+        Temp, VAT->getSizeExpr(), VAT->getSizeModifier(),
+        VAT->getIndexTypeCVRQualifiers(), VAT->getBracketsRange());
+  }
+  // Unwrap the references and reconstruct them
+  // but we don't need to strip To here (lvalue-to-rvalue would've already
+  // happened)
+  else if (From->isLValueReferenceType()) {
+    From = Context.getLValueReferenceType(Temp);
+  } else if (From->isRValueReferenceType()) {
+    From = Context.getRValueReferenceType(Temp);
+  }
+
+  From.setLocalFastQualifiers(To.getLocalFastQualifiers());
+  return From;
+}
+
+CXXCast CStyleCastOperation::castHelper(const Expr *Expression) const {
+  const auto *CastExpression = dyn_cast<CastExpr>(Expression);
+
+  // Base case - we have reached an expression that's not a CastExpr
+  if (!CastExpression) {
+    return CXXCast::CC_NoOpCast;
+  }
+
+  // If it's a cast expression but is not a part of the explicit c-style cast,
+  // we've also gone too far.
+  const auto *ImplicitCastExpression = dyn_cast<ImplicitCastExpr>(Expression);
+
+  if (ImplicitCastExpression &&
+      !ImplicitCastExpression->isPartOfExplicitCast()) {
+    return CXXCast::CC_NoOpCast;
+  }
+
+  return std::max(getCastType(CastExpression),
+                  castHelper(CastExpression->getSubExpr()));
+}
+
+CXXCast CStyleCastOperation::getCastType(const CastExpr *CastExpression) const {
+  switch (CastExpression->getCastKind()) {
+  /// No-op cast type
+  case CastKind::CK_NoOp:
+  case CastKind::CK_ArrayToPointerDecay:
+  case CastKind::CK_LValueToRValue:
+    return CXXCast::CC_NoOpCast;
+
+  // Static cast (with extension) types
+  case CastKind::CK_UncheckedDerivedToBase:
+  case CastKind::CK_DerivedToBase: {
+    /// Special case:
+    /// The base class A is inaccessible (private)
+    /// so we can't static_cast. We can't reinterpret_cast
+    /// either because reinterpret casting to A* would point
+    /// to the data segment owned by Pad. We can't convert to C style
+    /// in this case.
+    ///
+    /// \code
+    /// struct A { int i; };
+    /// struct Pad { int i; };
+    /// class B: Pad, A {};
+    ///
+    /// A *f(B *b) { return (A*)(b); }
+    /// \endcode
+    ///
+    /// We assume that the base class is unambiguous.
+    const CXXRecordDecl *Base = CastType->getPointeeCXXRecordDecl();
+    const CXXRecordDecl *Derived = SubExprType->getPointeeCXXRecordDecl();
+    return details::getBaseDerivedCast(Base, Derived);
+  }
+  case CastKind::CK_BaseToDerived: {
+    const CXXRecordDecl *Base = SubExprType->getPointeeCXXRecordDecl();
+    const CXXRecordDecl *Derived = CastType->getPointeeCXXRecordDecl();
+    return details::getBaseDerivedCast(Base, Derived);
+  }
+  case CastKind::CK_FunctionToPointerDecay:
+  case CastKind::CK_NullToPointer:
+  case CastKind::CK_NullToMemberPointer:
+  case CastKind::CK_BaseToDerivedMemberPointer:
+  case CastKind::CK_DerivedToBaseMemberPointer:
+  case CastKind::CK_MemberPointerToBoolean:
+  case CastKind::CK_UserDefinedConversion:
+  case CastKind::CK_ConstructorConversion:
+  case CastKind::CK_PointerToBoolean:
+  case CastKind::CK_ToVoid:
+  // vector splats are constant size vectors that can be
+  // broadcast assigned a single value.
+  case CastKind::CK_VectorSplat:
+  // Common integral/float cast types
+  case CastKind::CK_IntegralCast:
+  case CastKind::CK_IntegralToBoolean:
+  case CastKind::CK_IntegralToFloating:
+  case CastKind::CK_FixedPointCast:
+  case CastKind::CK_FixedPointToIntegral:
+  case CastKind::CK_IntegralToFixedPoint:
+  case CastKind::CK_FixedPointToBoolean:
+  case CastKind::CK_FloatingToIntegral:
+  case CastKind::CK_FloatingToBoolean:
+  case CastKind::CK_BooleanToSignedIntegral:
+  case CastKind::CK_FloatingCast:
+  // Floating complex cast types
+  case CastKind::CK_FloatingRealToComplex:
+  case CastKind::CK_FloatingComplexToReal:
+  case CastKind::CK_FloatingComplexToBoolean:
+  case CastKind::CK_FloatingComplexCast:
+  case CastKind::CK_FloatingComplexToIntegralComplex:
+  // Integral complex cast types
+  case CastKind::CK_IntegralRealToComplex:
+  case CastKind::CK_IntegralComplexToReal:
+  case CastKind::CK_IntegralComplexToBoolean:
+  case CastKind::CK_IntegralComplexCast:
+  case CastKind::CK_IntegralComplexToFloatingComplex:
+  // Atomic to non-atomic casts
+  case CastKind::CK_AtomicToNonAtomic:
+  case CastKind::CK_NonAtomicToAtomic:
+  // OpenCL casts
+  // https://godbolt.org/z/DEz8Rs
+  case CastKind::CK_ZeroToOCLOpaqueType:
+  case CastKind::CK_AddressSpaceConversion:
+  case CastKind::CK_IntToOCLSampler:
+    return CXXCast::CC_StaticCast;
+
+  // Reinterpret cast types
+  case CastKind::CK_BitCast:
+  case CastKind::CK_LValueBitCast:
+  case CastKind::CK_IntegralToPointer:
+  case CastKind::CK_LValueToRValueBitCast:
+  case CastKind::CK_ReinterpretMemberPointer:
+  case CastKind::CK_PointerToIntegral:
+    return CXXCast::CC_ReinterpretCast;
+
+  // C style cast types
+  // Dependent types are left as they are.
+  case CastKind::CK_Dependent:
+    return CXXCast::CC_CStyleCast;
+
+  // Invalid cast types for C++
+  // Union casts is not available in C++.
+  case CastKind::CK_ToUnion:
+  // Built-in functions must be directly called and don't have
+  // address. This is impossible for C style casts.
+  case CastKind::CK_BuiltinFnToFnPtr:
+  // C++ does not officially support the Objective C extensions.
+  // We mark these as invalid.
+  // - Objective C pointer types &
+  // - Objective C automatic reference counting (ARC)
+  // - Objective C blocks
+  // - etc
+  case CastKind::CK_CPointerToObjCPointerCast:
+  case CastKind::CK_BlockPointerToObjCPointerCast:
+  case CastKind::CK_AnyPointerToBlockPointerCast:
+  case CastKind::CK_ObjCObjectLValueCast:
+  case CastKind::CK_ARCProduceObject:
+  case CastKind::CK_ARCConsumeObject:
+  case CastKind::CK_ARCReclaimReturnedObject:
+  case CastKind::CK_ARCExtendBlockObject:
+  case CastKind::CK_CopyAndAutoreleaseBlockObject:
+  default:
+    return CXXCast::CC_InvalidCast;
+  }
+}
+
+} // namespace cppcast
+} // namespace clang
+#endif
diff --git a/clang-tools-extra/clang-cast/CastOptions.h b/clang-tools-extra/clang-cast/CastOptions.h
new file mode 100644
--- /dev/null
+++ b/clang-tools-extra/clang-cast/CastOptions.h
@@ -0,0 +1,190 @@
+//===--- CastOptions.h - clang-cast -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the enumerations and options that dictate behavior of
+/// the Matcher object defined in Matcher.h.
+///
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_CAST_CAST_OPTIONS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_CAST_CAST_OPTIONS_H
+
+#include "clang/Rewrite/Frontend/FixItRewriter.h"
+#include <string>
+
+namespace clang {
+namespace cppcast {
+
+/// Enumerations for cast types
+/// The ordering of these enums is important.
+///
+/// C style casts in clang are performed incrementally:
+/// - CStyleCastExpr
+///   - ImplicitCastExpr
+///     - ImplicitCastExpr
+///     ...
+///       - DeclRefExpr (for example)
+///
+/// Each one of the cast exprs may require a more "powerful" level of
+/// casting. With the exception of dynamic cast, the rest are ordered
+/// accordingly.
+///
+/// CC_DynamicCast
+/// --------------
+/// \code
+/// dynamic_cast<Base*>(derived_ptr);
+/// \endcode
+///
+/// A conversion from Base to Derived or vice versa that is
+/// performed at RUNTIME. This is not possible to be expressed
+/// in terms of C style casts.
+///
+/// CC_NoOpCast
+/// -----------
+/// This is either a cast to itself or an implicit conversion that
+/// can be done without casting.
+///
+/// CC_ConstCast
+/// ------------
+/// \code
+/// int x = 1;
+/// const int& y = x;
+/// const_cast<int&>(y);
+/// \endcode
+///
+/// A conversion from the same type but with different qualifiers on the
+/// multilevel pointer-array structure.
+///
+/// CC_StaticCast
+/// -------------
+/// \code
+/// static_cast<int>(true);
+/// \endcode
+///
+/// Static cast can perform logical conversions between types,
+/// call explicitly defined conversion functions such as operator(),
+/// and cast up and down an inheritance hierarchy (given access),
+/// and more.
+///
+/// CC_ReinterpretCast
+/// ------------------
+/// \code
+/// int* x;
+/// (bool*) x;
+/// \endcode
+///
+/// The above is a bitcast, and is generally the theme of reinterpret cast.
+/// We reinterpret the bits of the data type into something else. This cast
+/// will only cast A to B if sizeof(A) <= sizeof(B). Out of all the C++ casts,
+/// this is the most "rule-breaking" and dangerous, and should be used
+/// very sparingly.
+///
+/// CC_CStyleCast
+/// -------------
+/// \code
+/// template <typename T>
+/// void foo() {
+///     (T) 0;
+/// }
+/// \endcode
+///
+/// There are some cases where none of the above casts are possible,
+/// or suitable for replacement for C style casts, such as when
+/// static_cast cannot cast DerivedToBase due to insufficient access,
+/// or C style casting dependent (template) types (which can be any type
+/// enumerated above, including the DerivedToBase case). It is generally
+/// good to convert all C style casts to something of lower power, but
+/// sometimes it's not possible without losing power.
+///
+/// CC_InvalidCast
+/// --------------
+/// This maps to the set of CastKind::CK_* that are not possible to
+/// generate in C++. If this enum is encountered, something is wrong.
+///
+/// NOTE: We are using enums instead of enum-classes for the following reasons:
+/// - We can't use convenience functions from llvm CommandLine.h to define
+/// lists.
+/// - we want to make these these values bitmasks for inclusivity testing, and
+///   there is no implicit conversion from enum-class values to integral types.
+enum CXXCast {
+  CC_DynamicCast = 0b1,
+  CC_NoOpCast = 0b10,
+  CC_ConstCast = 0b100,
+  CC_StaticCast = 0b1000,
+  CC_ReinterpretCast = 0b10000,
+  CC_CStyleCast = 0b100000,
+  CC_InvalidCast = 0b1000000,
+};
+
+} // namespace cppcast
+
+namespace cli {
+
+using clang::cppcast::CXXCast;
+
+/// Options for CLI to specify which of the following should raise an error
+/// upon encountering CStyleCast with equivalent power.
+///
+/// NOTE: If a C style cast requires both const and static, having EO_StaticCast
+/// is sufficient to trigger an error.
+enum ErrorOpts {
+  EO_StaticCast = CXXCast::CC_StaticCast,
+  EO_ReinterpretCast = CXXCast::CC_ReinterpretCast,
+  EO_ConstCast = CXXCast::CC_ConstCast,
+  EO_CStyleCast = CXXCast::CC_CStyleCast,
+  EO_NoOpCast = CXXCast::CC_NoOpCast,
+  EO_All = 0xffffffff,
+};
+
+/// Options for CLI to specify which of C style casts should be fixed with
+/// FixItWriters.
+///
+/// NOTE: If a C style cast requires both const and static, it is required to
+/// have the bits of 'FO_StaticCast | FO_ConstCast' in the mask to apply a fix.
+enum FixOpts {
+  FO_StaticCast = CXXCast::CC_StaticCast,
+  FO_ReinterpretCast = CXXCast::CC_ReinterpretCast,
+  FO_ConstCast = CXXCast::CC_ConstCast,
+  // NOTE: There is no "fix_cstyle" because we can't fix them.
+  FO_NoOpCast = CXXCast::CC_NoOpCast,
+  FO_All = 0xffffffff,
+};
+
+} // namespace cli
+
+namespace rewriter {
+
+/// Custom FixItOptions to allow users to emit to files with added suffix.
+class FixItRewriterOptions : public clang::FixItOptions {
+public:
+  FixItRewriterOptions(const std::string &RewriteSuffix)
+      : RewriteSuffix(RewriteSuffix) {
+    if (RewriteSuffix.empty()) {
+      InPlace = true;
+    } else {
+      InPlace = false;
+    }
+    FixWhatYouCan = true;
+  }
+
+  std::string RewriteFilename(const std::string &Filename, int &fd) override {
+    // Set fd to -1 to mean that the file descriptor is not yet opened.
+    fd = -1;
+    const auto NewFilename = Filename + RewriteSuffix;
+    return NewFilename;
+  }
+
+private:
+  std::string RewriteSuffix;
+};
+
+} // namespace rewriter
+
+} // namespace clang
+
+#endif
diff --git a/clang-tools-extra/clang-cast/CastUtils.h b/clang-tools-extra/clang-cast/CastUtils.h
new file mode 100644
--- /dev/null
+++ b/clang-tools-extra/clang-cast/CastUtils.h
@@ -0,0 +1,195 @@
+//===--- CastUtils.h - clang-cast -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains utility functions for semantics and diagnostics.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_CAST_CAST_UTILS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_CAST_CAST_UTILS_H
+
+#include "CastOptions.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclCXX.h"
+
+namespace clang {
+namespace cppcast {
+
+/// Given a cast type enum of the form CXXCasts::CC_{type}, return the
+/// string representation of that respective type.
+std::string cppCastToString(const CXXCast &Cast) {
+  switch (Cast) {
+  case CXXCast::CC_ConstCast:
+    return "const_cast";
+  case CXXCast::CC_StaticCast:
+    return "static_cast";
+  case CXXCast::CC_ReinterpretCast:
+    return "reinterpret_cast";
+    // Below are only used for summary diagnostics
+  case CXXCast::CC_CStyleCast:
+    return "C style cast";
+  case CXXCast::CC_NoOpCast:
+    return "No-op cast";
+  default:
+    llvm_unreachable("The cast should never occur.");
+    return {};
+  }
+}
+
+/// Reports messages with a source location, typically used to address
+/// specific code segments.
+///
+/// \tparam N length of string
+/// \tparam Args variadic types to be accepted by DiagnosticBuilder
+/// \param Engine the diagnostic engine to report with
+/// \param Level diagnostic level
+/// \param FormatString A C string with \p N characters with potential clang
+/// format strings
+/// \param Loc The starting location in the translation unit to
+/// address
+/// \param args data to be formatted into FormatString \return resulting
+/// message object to be emitted.
+template <unsigned N, typename... Args>
+DiagnosticBuilder reportWithLoc(DiagnosticsEngine &Engine,
+                                const DiagnosticsEngine::Level &Level,
+                                const char (&FormatString)[N],
+                                const SourceLocation &Loc, Args &&... args) {
+  unsigned ID = Engine.getCustomDiagID(Level, FormatString);
+  // Binary left fold
+  return (Engine.Report(Loc, ID) << ... << std::forward<Args>(args));
+}
+
+/// Reports messages, typically used to address a translation-unit/file wide
+/// diagnostic. Refer to reportWithLoc for more information.
+template <unsigned N, typename... Args>
+DiagnosticBuilder report(DiagnosticsEngine &Engine,
+                         DiagnosticsEngine::Level Level,
+                         const char (&FormatString)[N], Args &&... args) {
+  unsigned ID = Engine.getCustomDiagID(Level, FormatString);
+  // Binary left fold
+  return (Engine.Report(ID) << ... << std::forward<Args &&>(args));
+}
+
+namespace details {
+
+/// Determines whether Base is accessible from Derived class.
+///
+/// \param Base the base class declaration
+/// \param Derived the derived class declaration
+/// \returns true if \p Base is accessible from \p Derived or are the same
+/// class, and false if \p Base is not accessible from \p Derived or are
+/// unrelated classes
+bool isAccessible(const CXXRecordDecl *Base, const CXXRecordDecl *Derived) {
+  if (!Base || !Derived)
+    return false;
+
+  if (clang::declaresSameEntity(Derived, Base)) {
+    // The class's contents is always accessible to itself.
+    return true;
+  }
+
+  for (const CXXBaseSpecifier &Specifier : Derived->bases()) {
+    // This should already be canonical
+    const QualType &BaseType = Specifier.getType();
+    CXXRecordDecl *BaseClass = (*BaseType).getAsCXXRecordDecl();
+
+    if (Specifier.getAccessSpecifier() == clang::AccessSpecifier::AS_public &&
+        isAccessible(Base, BaseClass))
+      return true;
+  }
+
+  // These two are unrelated classes.
+  return false;
+}
+
+/// Determines the proper cast type for a Base to/from Derived conversion
+/// based off of accessbility.
+///
+/// \param Base the base class declaration
+/// \param Derived the derived class declaration
+/// \return CXXCast enum corresponding to the lowest power cast required.
+CXXCast getBaseDerivedCast(const CXXRecordDecl *Base,
+                           const CXXRecordDecl *Derived) {
+  assert(Base && Derived && "Base and Derived decls cannot be null.");
+  if (!details::isAccessible(Base, Derived))
+    return CXXCast::CC_CStyleCast;
+  else
+    return CXXCast::CC_StaticCast;
+}
+
+/// Removes a layer of pointers, member pointers, arrays.
+///
+/// \param T type to strip, assumed to be one of the above.
+/// \param Context, the ASTContext to create the array type edge case.
+/// \return type corresponding to \p T stripped of one indirection layer.
+QualType stripLayer(const QualType &T, const ASTContext &Context) {
+  if (T->isPointerType()) {
+    const PointerType *PT = dyn_cast<PointerType>(T);
+    return PT->getPointeeType();
+  }
+  if (T->isArrayType()) {
+    const ArrayType *AT = Context.getAsArrayType(T);
+    return AT->getElementType();
+  }
+  if (T->isMemberPointerType()) {
+    const MemberPointerType *MPT = dyn_cast<MemberPointerType>(T);
+    return MPT->getPointeeType();
+  }
+  llvm_unreachable("The type is not a pointer/array/member to pointer type.");
+  return T;
+}
+
+/// \param T some qualified type
+/// \return true if T is a function pointer
+bool isFunctionPtr(const QualType &T) {
+  return T->isMemberFunctionPointerType() || T->isFunctionPointerType();
+}
+
+/// We define the types A and B to be locally similar if they are both
+/// - pointer, i.e. int* is a pointer to an int
+/// - member pointer, i.e. given struct t, int t::* const ptr is a
+/// pointer to an int member of struct t.
+/// - array / array of unknown bound, i.e. int a[2] and int a[], where the
+/// latter is likely a partial 'extern' type.
+bool isLocallySimilar(const QualType &A, const QualType &B) {
+  bool AIsPtr = A->isPointerType();
+  bool BIsPtr = B->isPointerType();
+  bool AIsArr = A->isArrayType();
+  bool BIsArr = B->isArrayType();
+  bool AIsMemberPtr = A->isMemberPointerType();
+  bool BIsMemberPtr = B->isMemberPointerType();
+
+  bool LocallySimilar = (AIsMemberPtr && BIsMemberPtr) ||
+                        (AIsPtr && BIsPtr && !AIsMemberPtr && !BIsMemberPtr) ||
+                        (AIsArr && BIsArr);
+
+  return LocallySimilar;
+}
+
+/// Either types are terminal if either one are not pointer-like types that
+/// can be traversed.
+bool isTerminal(const QualType &A, const QualType &B) {
+  bool AIsPtr = A->isPointerType();
+  bool BIsPtr = B->isPointerType();
+  bool AIsArr = A->isArrayType();
+  bool BIsArr = B->isArrayType();
+  bool AIsMemberPtr = A->isMemberPointerType();
+  bool BIsMemberPtr = B->isMemberPointerType();
+
+  bool IsTerminal = !(AIsPtr || AIsArr || AIsMemberPtr) ||
+                    !(BIsPtr || BIsArr || BIsMemberPtr);
+
+  return IsTerminal;
+}
+
+} // namespace details
+} // namespace cppcast
+} // namespace clang
+
+#endif
diff --git a/clang-tools-extra/clang-cast/ClangCast.cpp b/clang-tools-extra/clang-cast/ClangCast.cpp
new file mode 100644
--- /dev/null
+++ b/clang-tools-extra/clang-cast/ClangCast.cpp
@@ -0,0 +1,147 @@
+//===--- ClangCast.cpp - clang-cast -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file is the main driver for the clang-cast tool.
+///
+//===----------------------------------------------------------------------===//
+
+#include "Consumer.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+
+using namespace clang::ast_matchers;
+using namespace clang;
+using namespace cppcast;
+using namespace llvm;
+
+namespace clang {
+namespace cli {
+
+static llvm::cl::OptionCategory ClangCastCategory("clang-cast options");
+
+llvm::cl::extrahelp ClangCastCategoryHelp(R"(
+clang-cast finds C style casts and attempts to convert them into C++ casts.
+C++ casts are preferred since they are safer and more explicit than C style casts.
+
+For example:
+
+double* p = nullptr;
+(int*) p;
+
+... is converted into:
+
+double* p = nullptr;
+reinterpret_cast<int*>(p);
+
+When running clang-cast on a compilation unit, the tool will emit useful diagnostics.
+The tool can be used to modify a file in-place or into a new file with an added suffix.
+Clang-cast will not modify system headers, nor any file with the suffix .c (C files).
+)");
+
+llvm::cl::opt<bool>
+    PedanticOption("pedantic", llvm::cl::init(false),
+                   llvm::cl::desc("If true, clang-cast will not assume "
+                                  "qualification conversion extensions \n"
+                                  "(this will lead to more false negatives) "
+                                  "that clang adds. This is for projects \n"
+                                  "which use the -pedantic or -pedantic-errors "
+                                  "flag and have extensions turned off."),
+                   llvm::cl::cat(ClangCastCategory));
+
+llvm::cl::list<ErrorOpts> ErrorOptList(
+    llvm::cl::desc("For each flag set, clang-cast will issue an "
+                   "error for any C style casts that are converted "
+                   "to the following types."),
+    llvm::cl::values(
+        clEnumValN(EO_StaticCast, "err-static", "Error on static_cast"),
+        clEnumValN(EO_ReinterpretCast, "err-reinterpret",
+                   "Error on reinterpret_cast"),
+        clEnumValN(EO_ConstCast, "err-const", "Error on const_cast"),
+        clEnumValN(EO_CStyleCast, "err-cstyle",
+                   "Error on non-convertible C style casts"),
+        clEnumValN(EO_NoOpCast, "err-noop",
+                   "Error on unnecessary C style casts"),
+        clEnumValN(EO_All, "err-all", "Error for all of the above")),
+    llvm::cl::cat(ClangCastCategory));
+
+llvm::cl::list<FixOpts> FixOptList(
+    llvm::cl::desc("For each flag set, clang-cast will apply a fix for the C "
+                   "style casts that can be converted to the following types. "
+                   "Note that for casts that require two consecutive C++ "
+                   "casts, both flags need to be specified (or --fix-all)."),
+    llvm::cl::values(
+        clEnumValN(FO_StaticCast, "fix-static", "Apply fixes to static_cast"),
+        clEnumValN(FO_ReinterpretCast, "fix-reinterpret",
+                   "Apply fixes to reinterpret_cast"),
+        clEnumValN(FO_ConstCast, "fix-const", "Apply fixes to const_cast"),
+        clEnumValN(FO_NoOpCast, "fix-noop", "Apply fixes to no-op cast"),
+        clEnumValN(FO_All, "fix-all", "Apply fixes for all of the above")),
+    llvm::cl::cat(ClangCastCategory));
+
+llvm::cl::opt<std::string>
+    SuffixOption("suffix",
+                 llvm::cl::desc("If suffix is set, changes of "
+                                "a file F will be written to F+suffix."),
+                 llvm::cl::cat(ClangCastCategory));
+
+llvm::cl::opt<bool>
+    DontExpandIncludes("no-includes", llvm::cl::init(false),
+                       llvm::cl::desc("Don't modify any include files."),
+                       llvm::cl::cat(ClangCastCategory));
+
+llvm::cl::opt<bool>
+    PublishSummary("summary", llvm::cl::init(false),
+                   llvm::cl::desc("If true, clang-cast gives a small summary "
+                                  "of the statistics of casts through "
+                                  "the entire translation unit."),
+                   llvm::cl::cat(ClangCastCategory));
+
+llvm::cl::extrahelp
+    CommonHelp(clang::tooling::CommonOptionsParser::HelpMessage);
+
+} // namespace cli
+
+namespace cppcast {
+
+class Action : public clang::ASTFrontendAction {
+public:
+  using ASTConsumerPointer = std::unique_ptr<clang::ASTConsumer>;
+  Action() = default;
+  ASTConsumerPointer CreateASTConsumer(CompilerInstance &Compiler,
+                                       StringRef Filename) override {
+    if (!Compiler.getLangOpts().CPlusPlus) {
+      llvm::report_fatal_error("clang-cast is only supported for C++.");
+    }
+    return std::make_unique<Consumer>(
+        cli::SuffixOption, cli::PedanticOption, cli::PublishSummary,
+        cli::DontExpandIncludes, cli::ErrorOptList, cli::FixOptList);
+  }
+};
+
+struct ToolFactory : public clang::tooling::FrontendActionFactory {
+  std::unique_ptr<clang::FrontendAction> create() override {
+    std::unique_ptr<clang::FrontendAction> Ptr;
+    Ptr.reset(new Action());
+    return Ptr;
+  }
+};
+
+} // namespace cppcast
+
+} // namespace clang
+
+int main(int argc, const char **argv) {
+  tooling::CommonOptionsParser op(argc, argv, cli::ClangCastCategory);
+  tooling::ClangTool Tool(op.getCompilations(), op.getSourcePathList());
+  int ExitCode = Tool.run(new clang::cppcast::ToolFactory());
+  return ExitCode;
+}
diff --git a/clang-tools-extra/clang-cast/Consumer.h b/clang-tools-extra/clang-cast/Consumer.h
new file mode 100644
--- /dev/null
+++ b/clang-tools-extra/clang-cast/Consumer.h
@@ -0,0 +1,45 @@
+//===--- Consumer.h - clang-cast --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the ASTConsumer class used in FrontendAction.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_CAST_CONSUMER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_CAST_CONSUMER_H
+
+#include "Matcher.h"
+#include "clang/AST/ASTConsumer.h"
+
+namespace clang {
+namespace cppcast {
+
+class Consumer : public clang::ASTConsumer {
+public:
+  template <typename... Args>
+  Consumer(Args &&... args) : Handler(std::forward<Args>(args)...) {
+    using namespace clang::ast_matchers;
+    // TODO: Make this a constant instead of hardcode "cast"
+    StatementMatcher CStyleCastMatcher = cStyleCastExpr().bind("cast");
+    MatchFinder.addMatcher(CStyleCastMatcher, &Handler);
+  }
+
+  void HandleTranslationUnit(clang::ASTContext &Context) override {
+    MatchFinder.matchAST(Context);
+  }
+
+private:
+  Matcher Handler;
+  clang::ast_matchers::MatchFinder MatchFinder;
+};
+
+} // namespace cppcast
+} // namespace clang
+
+#endif
diff --git a/clang-tools-extra/clang-cast/Matcher.h b/clang-tools-extra/clang-cast/Matcher.h
new file mode 100644
--- /dev/null
+++ b/clang-tools-extra/clang-cast/Matcher.h
@@ -0,0 +1,400 @@
+//===--- Matcher.h - clang-cast ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the ASTMatcher responsible for most of the diagnostics.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_CAST_MATCHER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_CAST_MATCHER_H
+
+#include "Cast.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace cppcast {
+
+/// Matcher is responsible for creating a CStyleCastOperation to perform
+/// semantic analysis on the CStyleCastExpr and decide what potential
+/// replacements are suitable for the expression.
+///
+/// The matcher decides whether to emit a warning or error or to additionally
+/// perform a fix based off of the bit masks created by enums in CastOptions.h.
+///
+/// The Matcher emits summaries if the flags are set, containing some summary
+/// statistics.
+class Matcher : public MatchFinder::MatchCallback {
+  using RewriterPtr = std::unique_ptr<clang::FixItRewriter>;
+  /// The FixItRewriter is actually a composable DiagnosticConsumer which
+  /// contains the original DiagnosticConsumer client, which is a
+  /// TextDiagnosticPrinter.
+  ///
+  /// If we have Rewriter set as the client, FixIt's will be fixed on the file.
+  /// We want to have granular control over which FixIt's to fix, so we do some
+  /// light pointer manipulation with the DiagnosticEngine in setClient to
+  /// switch back and forth between the TextDiagnosticPrinter and FixItRewriter.
+  RewriterPtr Rewriter;
+  rewriter::FixItRewriterOptions FixItOptions;
+  DiagnosticConsumer *OriginalWriter;
+
+  /// Whether or not the Matcher should emit code compliant with -pedantic
+  bool Pedantic;
+
+  /// Whether or not the Matcher should publish a short blurb upon finishing
+  /// visiting a translation unit.
+  bool PublishSummary;
+
+  /// Number of C style casts encountered
+  unsigned TotalCasts;
+
+  /// Whether or not to diagnose & fix includes (Local modifications only)
+  bool DontExpandIncludes;
+
+  /// Bitmask to determine which C style cast powers should give an error.
+  /// For example, if we met a cast that requires static cast,
+  /// and static cast is 0x1, and our mask is 0x10011, since the last bit is a
+  /// 1, we will emit the error.
+  unsigned ErrMask;
+
+  /// Bitmask to determine which C style casts to fix.
+  unsigned FixMask;
+
+  /// Keeps count of how many C++ cast conversion types there are.
+  /// Note that for casts that require multiple C++ casts, multiple types are
+  /// updated here.
+  std::map<CXXCast, unsigned> Statistics;
+
+  /// List of files that were modified from the tool.
+  std::set<StringRef> ChangedFiles;
+
+public:
+  /// Initializes a new Matcher.
+  /// \param FilenameSuffix the suffix to add to the filenames
+  /// \param Pedantic whether to check & emit code compliant with -pedantic
+  /// \param PublishSummary whether to publish a small summary at the end
+  /// \param DontExpandIncludes whether to parse cast exprs in headers
+  /// \param ErrorOptions vector of bitmasks for error/warn types of cast
+  /// \param FixOptions vector of bitmasks for fixing types of cast
+  Matcher(const std::string &FilenameSuffix, const bool Pedantic,
+          bool PublishSummary, bool DontExpandIncludes,
+          std::vector<cli::ErrorOpts> ErrorOptions,
+          std::vector<cli::FixOpts> FixOptions);
+
+  virtual ~Matcher();
+
+  /// There are a few cases for the replacement string:
+  /// 1. No-op cast (remove the cast)
+  /// 2. Only const cast
+  /// 3. Static cast
+  /// 4. Static cast + const cast
+  /// 5. Reinterpret cast
+  /// 6. Reinterpret cast + const cast
+  /// 7. C style cast (keep the cast as is)
+  /// \param Op operation wrapper
+  /// \param CXXCastKind kind of non const cast applied to the expr (can be
+  /// noop)
+  /// \param ConstCastRequired whether const cast should be added to the
+  /// existing casts
+  /// \return A string to replace the C style cast operation char range with.
+  std::string replaceExpression(const CStyleCastOperation &Op,
+                                CXXCast CXXCastKind, bool ConstCastRequired);
+
+  /// Given an ordered list of casts, use the ASTContext to report necessary
+  /// changes to the cast expression. Also performs a FixIt on the source code
+  /// if necessary.
+  /// \param ModifiableContext the context ptr needs to be modifiable in
+  /// order to set the diagnostic client
+  /// \param Op the operation wrapper
+  /// \return true if a FixIt has been applied
+  bool reportDiagnostic(ASTContext *ModifiableContext,
+                        const CStyleCastOperation &Op);
+
+  virtual void run(const MatchFinder::MatchResult &Result);
+
+private:
+  /// Given an expression, gives the character source range of the expr.
+  CharSourceRange getRangeForExpression(const Expr *Expression,
+                                        const ASTContext &Context);
+
+  /// Sets a client to the diagnostic engine depending on Modify:
+  /// if Modify is true, then we change the diagnostics client to a
+  /// FixItRewriter which takes and owns the TextDiagnosticPrinter from the
+  /// engine.
+  ///
+  /// If modify is false, then we can't just destroy the FixItRewriter, as the
+  /// previous FixIt's would be gone. If we already created a rewriter, then the
+  /// TextDiagnosticPrinter is owned by it when previously it was owned by the
+  /// engine. We do nothing if the rewriter hasn't been initialized, but this is
+  /// extremely precarious as it's juggling ownership between two objects as the
+  /// FixIt's are applied to subsets of the program.
+  ///
+  /// This function was born out of a necessity to work with FixItRewriter's
+  /// composition-of-consumers design which makes ownership very complicated.
+  void setClient(clang::ASTContext *Context, bool Modify);
+
+  /// A quick diagnostic warning for when symlinks are being overwritten by an
+  /// actual file.
+  void checkForSymlinks(const SourceManager &Manager, const SourceLocation &Loc,
+                        DiagnosticsEngine &DiagEngine);
+};
+
+// We can't initialize the RewriterPtr until we get an ASTContext.
+Matcher::Matcher(const std::string &FilenameSuffix, const bool Pedantic,
+                 bool PublishSummary, bool DontExpandIncludes,
+                 std::vector<cli::ErrorOpts> ErrorOptions,
+                 std::vector<cli::FixOpts> FixOptions)
+    : Rewriter(nullptr), FixItOptions(FilenameSuffix), Pedantic(Pedantic),
+      PublishSummary(PublishSummary), TotalCasts(0),
+      DontExpandIncludes(DontExpandIncludes),
+      /* modify in ctr */ ErrMask(0),
+      /* modify in ctr */ FixMask(0) {
+  for (unsigned i = 0; i != ErrorOptions.size(); i++) {
+    ErrMask |= ErrorOptions[i];
+  }
+  for (unsigned i = 0; i != FixOptions.size(); i++) {
+    FixMask |= FixOptions[i];
+  }
+}
+
+// TODO: Is this okay to do?
+Matcher::~Matcher() {
+  if (PublishSummary) {
+    for (auto const &[CXXCastKind, Freq] : Statistics) {
+      if (!Freq)
+        continue;
+      llvm::errs() << "The type " << cppCastToString(CXXCastKind)
+                   << " has been issued " << Freq
+                   << " times throughout the translation unit.\n";
+    }
+    if (FixMask) {
+      llvm::errs() << "The following files were modified:\n";
+      for (auto const &File : ChangedFiles) {
+        llvm::errs() << "\t - " << File << "\n";
+      }
+    }
+    llvm::errs() << "In total, there are " << TotalCasts
+                 << " C style casts in the translation unit. Multiple C++ "
+                    "casts may be used to convert a single C style cast.\n";
+  }
+}
+
+CharSourceRange Matcher::getRangeForExpression(const Expr *Expression,
+                                               const ASTContext &Context) {
+  // Also expand on macros:
+  auto &Manager = Context.getSourceManager();
+
+  const ParenExpr *ParenExpression;
+  while ((ParenExpression = dyn_cast<ParenExpr>(Expression))) {
+    Expression = ParenExpression->getSubExpr();
+  }
+  auto ExprStart = Manager.getSpellingLoc(Expression->getBeginLoc());
+  auto ExprEnd = Lexer::getLocForEndOfToken(
+      Manager.getSpellingLoc(Expression->getEndLoc()), /*Offset=*/0,
+      Context.getSourceManager(), Context.getLangOpts());
+
+  return CharSourceRange::getCharRange(SourceRange{ExprStart, ExprEnd});
+}
+
+std::string Matcher::replaceExpression(const CStyleCastOperation &Op,
+                                       CXXCast CXXCastKind,
+                                       bool ConstCastRequired) {
+  assert(CXXCastKind != CXXCast::CC_ConstCast &&
+         "Const cast enum cannot be passed in as CXXCastKind");
+  QualType CastType = Op.getCastType();
+  const Expr &SubExpr = Op.getSubExprAsWritten();
+  const ASTContext &Context = Op.getContext();
+  const auto &LangOpts = Context.getLangOpts();
+
+  std::string SubExpressionStr =
+      Lexer::getSourceText(getRangeForExpression(&SubExpr, Context),
+                           Context.getSourceManager(), Context.getLangOpts())
+          .str();
+
+  switch (CXXCastKind) {
+  case CXXCast::CC_NoOpCast: {
+    if (!ConstCastRequired) {
+      // our replacement is simply the subexpression (no cast needed)
+      return SubExpressionStr;
+    }
+    return cppCastToString(CXXCast::CC_ConstCast) + "<" +
+           CastType.getAsString(LangOpts) + ">(" + SubExpressionStr + ")";
+  }
+  case CXXCast::CC_StaticCast:
+  case CXXCast::CC_ReinterpretCast: {
+    std::string CastTypeStr = cppCastToString(CXXCastKind);
+    if (!ConstCastRequired) {
+      return CastTypeStr + "<" + CastType.getAsString(LangOpts) + ">(" +
+             SubExpressionStr + ")";
+    }
+    QualType IntermediateType = Op.changeQualifiers();
+    return cppCastToString(CXXCast::CC_ConstCast) + "<" +
+           CastType.getAsString(LangOpts) + ">(" + CastTypeStr + "<" +
+           IntermediateType.getAsString(LangOpts) + ">(" + SubExpressionStr +
+           "))";
+  }
+  default: {
+    llvm_unreachable(
+        "The type of cast passed in cannot produce a replacement.");
+    return {};
+  }
+  }
+}
+
+bool Matcher::reportDiagnostic(ASTContext *ModifiableContext,
+                               const CStyleCastOperation &Op) {
+  // No FixItRewriter should be set as consumer until we need to fix anything.
+  setClient(ModifiableContext, /*Modify=*/false);
+  const auto &Context = Op.getContext();
+  auto &DiagEngine = Context.getDiagnostics();
+
+  // Set diagnostics to warning by default, and to INFO for edge cases.
+  const auto &CastExpr = Op.getCStyleCastExpr();
+  auto StartingLoc = CastExpr.getExprLoc();
+  const auto &ExprRange = getRangeForExpression(&CastExpr, Context);
+
+  CXXCast CXXCastKind = Op.getCastKindFromCStyleCast();
+  bool ConstCastRequired = Op.requireConstCast();
+  unsigned CurMask = CXXCastKind | (ConstCastRequired * CXXCast::CC_ConstCast);
+
+  // Invalid cast or dynamic (this should never happen, and would be a bug)
+  if (CurMask & (CXXCast::CC_InvalidCast | CXXCast::CC_DynamicCast)) {
+    reportWithLoc(
+        DiagEngine, DiagnosticsEngine::Error,
+        "clang-casts has encountered an error. Currently does not support "
+        "the following cast",
+        StartingLoc, ExprRange);
+    return false;
+  }
+  if (CurMask & CXXCast::CC_CStyleCast) {
+    reportWithLoc(DiagEngine,
+                  (CurMask & ErrMask) ? DiagnosticsEngine::Error
+                                      : DiagnosticsEngine::Remark,
+                  "C style cast cannot be converted "
+                  "into a C++ style cast",
+                  StartingLoc, ExprRange);
+    return false;
+  }
+
+  const auto Replacement =
+      replaceExpression(Op, CXXCastKind, ConstCastRequired);
+  const auto CastRange = getRangeForExpression(&CastExpr, Context);
+
+  Statistics[CXXCast::CC_ConstCast] += ConstCastRequired;
+  Statistics[CXXCastKind]++;
+
+  // TODO: the location pointer looks funky when the cast expression is
+  // in a macro.
+  auto &Manager = Context.getSourceManager();
+  auto Level = (CurMask & ErrMask) ? DiagnosticsEngine::Error
+                                   : DiagnosticsEngine::Warning;
+  if (Manager.isMacroBodyExpansion(StartingLoc)) {
+    reportWithLoc(DiagEngine, Level,
+                  "C style cast can be replaced by '%0' "
+                  "(won't be fixed in macro)",
+                  StartingLoc, Replacement, ExprRange);
+    return false;
+  }
+
+  // Set the diagnostic consumer accordingly.
+  bool Modify = (CurMask & FixMask) == CurMask;
+
+  // TODO: For clang-tidy, we want to put a logical branch here and not input
+  // FixIt if we don't want to modify it. If we set the consumer as
+  // FixItRewriter permanently, emitting an error (from --err-all, for example)
+  // will cause FixIt to emit "FIXIT: detected an error it cannot fix", which
+  // clutters up the real diagnostics.
+  setClient(ModifiableContext, Modify);
+  const auto FixIt =
+      clang::FixItHint::CreateReplacement(CastRange, Replacement);
+  reportWithLoc(DiagEngine, Level, "C style cast can be replaced by '%0'",
+                StartingLoc, Replacement, FixIt);
+  return Modify;
+}
+
+void Matcher::run(const MatchFinder::MatchResult &Result) {
+  ASTContext *Context = Result.Context;
+  auto &DiagEngine = Context->getDiagnostics();
+
+  const CStyleCastExpr *CastExpression =
+      Result.Nodes.getNodeAs<CStyleCastExpr>("cast");
+  assert(CastExpression && "CastExpr cannot be null");
+
+  const auto &Manager = Context->getSourceManager();
+  const auto Loc = CastExpression->getBeginLoc();
+  // We skip external headers
+  if (Manager.isInExternCSystemHeader(Loc) || Manager.isInSystemHeader(Loc) ||
+      (!Manager.isInMainFile(Loc) && DontExpandIncludes)) {
+    return;
+  }
+
+  TotalCasts++;
+  CStyleCastOperation Op(*CastExpression, *Context, Pedantic);
+
+  // If anything is to be modified
+  if (reportDiagnostic(Context, Op)) {
+    checkForSymlinks(Manager, Loc, DiagEngine);
+    if (Rewriter->WriteFixedFiles()) {
+      llvm::errs() << "Writing the FixItHint was unsuccessful.\n";
+    }
+  }
+}
+
+void Matcher::checkForSymlinks(const SourceManager &Manager,
+                               const SourceLocation &Loc,
+                               DiagnosticsEngine &DiagEngine) {
+  const FileEntry *Entry = Manager.getFileEntryForID(Manager.getFileID(Loc));
+  assert(Entry && "File entry cannot be null");
+  const auto RealFilename = Entry->tryGetRealPathName();
+  const auto Filename = Entry->getName();
+  ChangedFiles.insert(Filename);
+
+  if (RealFilename != Filename) {
+    report(DiagEngine, DiagnosticsEngine::Warning,
+           "The symlink at %0 pointing to %1 is changed to a file during "
+           "modifications.",
+           Filename, RealFilename);
+  }
+}
+
+void Matcher::setClient(clang::ASTContext *Context, bool Modify) {
+  auto &DiagEngine = Context->getDiagnostics();
+  if (Modify) {
+    // First time initializing, means that engine owns TDP.
+    if (!Rewriter) {
+      OriginalWriter = DiagEngine.getClient();
+      Rewriter = std::make_unique<clang::FixItRewriter>(
+          DiagEngine, Context->getSourceManager(), Context->getLangOpts(),
+          &FixItOptions);
+    }
+
+    // If we modify, we don't want the diagnostics engine to own it.
+    // If we don't modify, we want the diagnostics engine to own the original
+    // client.
+    Context->getDiagnostics().setClient(Rewriter.get(),
+                                        /*ShouldOwnClient=*/false);
+  } else {
+    // Rewriter not initialized, therefore do nothing, as the TDP client
+    // is still there and owned by engine.
+    if (!Rewriter)
+      return;
+    // Rewriter is initialized, therefore set TDP (not owned by engine,
+    // owned by FIR)
+    Context->getDiagnostics().setClient(OriginalWriter,
+                                        /*ShouldOwnClient=*/false);
+  }
+}
+
+} // namespace cppcast
+} // namespace clang
+
+#endif
diff --git a/clang-tools-extra/unittests/CMakeLists.txt b/clang-tools-extra/unittests/CMakeLists.txt
--- a/clang-tools-extra/unittests/CMakeLists.txt
+++ b/clang-tools-extra/unittests/CMakeLists.txt
@@ -6,6 +6,7 @@
 endfunction()
 
 add_subdirectory(clang-apply-replacements)
+add_subdirectory(clang-cast)
 add_subdirectory(clang-change-namespace)
 add_subdirectory(clang-doc)
 add_subdirectory(clang-include-fixer)
diff --git a/clang-tools-extra/unittests/clang-cast/CMakeLists.txt b/clang-tools-extra/unittests/clang-cast/CMakeLists.txt
new file mode 100644
--- /dev/null
+++ b/clang-tools-extra/unittests/clang-cast/CMakeLists.txt
@@ -0,0 +1,30 @@
+set(LLVM_LINK_COMPONENTS Support)
+
+include_directories(
+    ${CMAKE_CURRENT_SOURCE_DIR}/../../clang-cast
+)
+
+add_extra_unittest(ClangCastTests
+    ClangCastTests.cpp
+)
+
+clang_target_link_libraries(ClangCastTests
+    PRIVATE
+    clangAST
+    clangASTMatchers
+    clangBasic
+    clangFormat
+    clangFrontend
+    clangRewrite
+    clangSerialization
+    clangTooling
+    clangToolingCore
+)
+
+target_link_libraries(ClangCastTests
+    PRIVATE
+    clangTooling
+    clangBasic
+    clangASTMatchers
+)
+
diff --git a/clang-tools-extra/unittests/clang-cast/ClangCXXCastTestCases.h b/clang-tools-extra/unittests/clang-cast/ClangCXXCastTestCases.h
new file mode 100644
--- /dev/null
+++ b/clang-tools-extra/unittests/clang-cast/ClangCXXCastTestCases.h
@@ -0,0 +1,339 @@
+//===-- ClangCXXCastTestCases.h - clang-cast --------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains test cases for
+/// CStyleCastOperation::getCastKindFromCStyleCast (defined in Cast.h)
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_CAST_CLANGCXXCASTTESTCASES_H
+#define LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_CAST_CLANGCXXCASTTESTCASES_H
+
+namespace testcases {
+
+/// NoOp cast types - these are conversions
+/// that can be done implicitly.
+static const char NoOp[] = R"(
+void f() {
+  (int&&) 0;
+}
+)";
+static const char ArrayToPointerDecay[] = R"(
+void f() {
+  char x[] = "";
+  (char*) x;
+}
+)";
+// NOTE: Unused, as C style casts cannot
+//       perform this. It must be done implicitly.
+//       (int&&) x is a NoOp, and std::move(x) is
+//       FunctionToPointerDecay.
+static const char LValueToRValue[] = R"(
+void f() {
+  int x;
+  int y = x;
+}
+)";
+
+/// Static cast types
+static const char BaseToDerived[] = R"(
+class A {};
+class B: public A {};
+class C: public B {};
+
+void f() {
+  A* a = nullptr;
+  (C*) a;
+}
+)";
+static const char DerivedToBase[] = R"(
+class A {};
+class B: public A {};
+class C: public B {};
+
+void f() {
+  C* c = nullptr;
+  (A*) c;
+}
+)";
+// NOTE: Unused, as C style casts cannot
+//       perform this. It must be done implicitly.
+static const char UncheckedDerivedToBase[] = R"(
+class A { public: void a(){} };
+class B : public A {};
+
+void f() {
+    B *b;
+    b->a();
+}
+)";
+static const char FunctionToPointerDecay[] = R"(
+void g() {}
+
+void f() {
+  (void (*)()) g;
+}
+)";
+static const char NullToPointer[] = R"(
+void f() {
+  (void*) 0;
+}
+)";
+static const char NullToMemberPointer[] = R"(
+struct A {};
+void f() {
+  (int A::*) 0;
+}
+)";
+static const char BaseToDerivedMemberPointer[] = R"(
+struct A { int m; };
+struct B : A {};
+void f() {
+  (int B::*) &A::m;
+}
+)";
+static const char DerivedToBaseMemberPointer[] = R"(
+struct A {};
+struct B : A { int m; };
+void f() {
+  (int A::*) &B::m;
+}
+)";
+static const char MemberPointerToBoolean[] = R"(
+struct A { int m; };
+void f() {
+  (bool) &A::m;
+}
+)";
+static const char UserDefinedConversion[] = R"(
+struct A { operator int(); };
+
+void f() {
+  (int) A();
+}
+)";
+static const char ConstructorConversion[] = R"(
+struct A { A(int); };
+
+void f() {
+  (A) 10;
+}
+)";
+static const char PointerToBoolean[] = R"(
+void f() {
+  (bool) nullptr;
+}
+)";
+static const char ToVoid[] = R"(
+void f() {
+  (void) 0;
+}
+)";
+static const char VectorSplat[] = R"(
+void f() {
+  typedef float float4 __attribute__((ext_vector_type(4)));
+  using type = float4;
+  (type) 0.0f;
+}
+)";
+static const char IntegralCast[] = R"(
+void f() {
+  (long long int) 0u;
+}
+)";
+static const char IntegralToBoolean[] = R"(
+void f() {
+  (bool) 10l;
+}
+)";
+static const char IntegralToFloating[] = R"(
+void f() {
+  (float) 10l;
+}
+)";
+
+// NOTE: Unused, as fixed points are not yet introduced into C++ standard.
+//       nor the clang extensions.
+static const char FixedPointCast[] = R"()";
+// NOTE: Unused, as fixed points are not yet introduced into C++ standard.
+//       nor the clang extensions.
+static const char FixedPointToIntegral[] = R"()";
+// NOTE: Unused, as fixed points are not yet introduced into C++ standard.
+//       nor the clang extensions.
+static const char IntegralToFixedPoint[] = R"()";
+// NOTE: Unused, as fixed points are not yet introduced into C++ standard.
+//       nor the clang extensions.
+static const char FixedPointToBoolean[] = R"()";
+
+static const char FloatingToIntegral[] = R"(
+void f() {
+  (int) 0.0f;
+}
+)";
+static const char FloatingToBoolean[] = R"(
+void f() {
+  (bool) 0.0f;
+}
+)";
+// TODO: Ask question
+// NOTE: Unused, because AFAIK this is not possible, according to
+//       the comments it will cast to -1/0 for true/false.
+static const char BooleanToSignedIntegral[] = R"(
+void f() {
+  (int) true;
+}
+)";
+static const char FloatingCast[] = R"(
+void f() {
+  (double) 0.0f;
+}
+)";
+static const char FloatingRealToComplex[] = R"(
+void f() {
+  (_Complex long double) 1.0;
+}
+)";
+static const char FloatingComplexToReal[] = R"(
+void f() {
+  _Complex long double c;
+  (long double) c;
+}
+)";
+static const char FloatingComplexToBoolean[] = R"(
+void f() {
+  _Complex long double c;
+  (bool) c;
+}
+)";
+static const char FloatingComplexCast[] = R"(
+void f() {
+  _Complex long double c;
+  (_Complex float) c;
+}
+)";
+static const char FloatingComplexToIntegralComplex[] = R"(
+void f() {
+  _Complex long double c;
+  (_Complex int) c;
+}
+)";
+static const char IntegralRealToComplex[] = R"(
+void f() {
+  (_Complex long long) 10l;
+}
+)";
+static const char IntegralComplexToReal[] = R"(
+void f() {
+  _Complex long long c;
+  (int) c;
+}
+)";
+static const char IntegralComplexToBoolean[] = R"(
+void f() {
+  _Complex long long c;
+  (bool) c;
+}
+)";
+static const char IntegralComplexCast[] = R"(
+void f() {
+  _Complex long long c;
+  (_Complex int) c;
+}
+)";
+static const char IntegralComplexToFloatingComplex[] = R"(
+void f() {
+  _Complex long long c;
+  (_Complex float) c;
+}
+)";
+static const char AtomicToNonAtomic[] = R"(
+void f() {
+  _Atomic(int) c;
+  (int) c;
+}
+)";
+static const char NonAtomicToAtomic[] = R"(
+void f() {
+  int c;
+  (_Atomic(int)) c;
+}
+)";
+
+/// Reinterpret cast types
+const char BitCast[] = R"(
+void f() {
+  char* x;
+  (int *) x;
+}
+)";
+const char LValueBitCast[] = R"(
+void f() {
+  char c;
+  (bool&) c;
+}
+)";
+static const char IntegralToPointer[] = R"(
+void f() {
+  (int*) 10l;
+}
+)";
+// NOTE: Unused, as C style casts cannot
+//       perform this. It must be done by bit_cast.
+const char LValueToRValueBitCast[] = R"(
+void f() {
+  int i;
+  std::bit_cast<float>(i);
+}
+)";
+static const char ReinterpretMemberPointer[] = R"(
+struct A { int val; };
+
+void f() {
+  int A::* ptr = &A::val;
+  (bool A::*) ptr;
+}
+)";
+static const char PointerToIntegral[] = R"(
+#include <stdint.h>
+void f() {
+  (intptr_t) nullptr;
+}
+)";
+
+/// C-style cast types
+static const char Dependent[] = R"(
+template <typename T>
+void foo() {
+    (T) 0;
+}
+)";
+
+namespace edgecases {
+
+static const char BaseToDerivedPrivateSpecifier[] = R"(
+struct A { int i; };
+struct Pad { int i; };
+class B: Pad, A {};
+
+B* foo(A *a) { return (B*)(a); }
+)";
+
+static const char DerivedToBasePrivateSpecifier[] = R"(
+struct A { int i; };
+struct Pad { int i; };
+class B: Pad, A {};
+
+A* foo(B *b) { return (A*)(b); }
+)";
+
+} // namespace edgecases
+
+} // namespace testcases
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_CAST_CLANGCXXCASTTESTCASES_H
diff --git a/clang-tools-extra/unittests/clang-cast/ClangCastTests.cpp b/clang-tools-extra/unittests/clang-cast/ClangCastTests.cpp
new file mode 100644
--- /dev/null
+++ b/clang-tools-extra/unittests/clang-cast/ClangCastTests.cpp
@@ -0,0 +1,509 @@
+//===-- ClangCastTests.cpp - clang-cast -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains tests of:
+///   - Getting the correct non-const cast type from a C style cast
+///   - Correctly determing whether an expression is casting
+///     away constness
+///   - Checking edge cases (function ptrs, private access
+///     inheritance, etc)
+///
+//===----------------------------------------------------------------------===//
+
+#include "Cast.h"
+#include "ClangCXXCastTestCases.h"
+#include "ClangChangeQualifierTestCases.h"
+#include "ClangFunctionPtrTestCases.h"
+#include "ClangQualifierTestCases.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+#include <iostream>
+#include <set>
+#include <vector>
+
+#define CLANG_CXX_CAST_CHECK(cast_kind, cxx_cast)                              \
+  {                                                                            \
+    auto res = parse(cast_kind);                                               \
+    ASSERT_GE(res.first.size(), 1ul);                                          \
+    ASSERT_EQ(res.second.size(), 1ul);                                         \
+    ASSERT_TRUE(res.first.find(CastKind::CK_##cast_kind) != res.first.end());  \
+    ASSERT_EQ(res.second[0], CXXCast::CC_##cxx_cast);                          \
+  }
+
+#define CLANG_QUAL_CHECK(test_case, req_const, pedantic)                       \
+  {                                                                            \
+    auto res = parse(test_case, pedantic);                                     \
+    ASSERT_EQ(res, req_const);                                                 \
+  }
+
+#define CLANG_FUNC_PTR_CHECK(test_case, detected)                              \
+  {                                                                            \
+    auto res = parse(test_case);                                               \
+    ASSERT_EQ(res, detected);                                                  \
+  }
+
+using namespace testcases;
+using namespace testcases::constcheck;
+using namespace testcases::funcptr;
+using namespace testcases::changequal;
+using namespace clang;
+using namespace clang::tooling;
+using namespace clang::ast_matchers;
+using namespace clang::cppcast;
+
+static constexpr auto CastVar = "cast";
+static constexpr auto DeclVar = "varDecl";
+
+/// Uses CStyleCastCollector to collect all CXXCast enums obtained
+/// and CastKinds encountered.
+class ClangCXXCastTest : public ::testing::Test {
+  using CastKindSet = std::set<CastKind>;
+  using CXXCastVector = std::vector<CXXCast>;
+  StatementMatcher CStyleCastMatcher;
+
+  struct CStyleCastCollector : MatchFinder::MatchCallback {
+    std::vector<CXXCast> Casts;
+    std::set<CastKind> CastKinds;
+    CStyleCastCollector() = default;
+
+    virtual void run(const MatchFinder::MatchResult &Result) override {
+      ASTContext *Context = Result.Context;
+      const CStyleCastExpr *Expr =
+          Result.Nodes.getNodeAs<CStyleCastExpr>(CastVar);
+      assert(Expr && Context);
+      CStyleCastOperation Op(*Expr, *Context, /*Pedantic*/ false);
+
+      const CastExpr *GenericCastExpr = dyn_cast<CastExpr>(Expr);
+      // traverse the expr tree and set current expr
+      // node to GenericCastExpr.
+      while (GenericCastExpr) {
+        CastKinds.insert(GenericCastExpr->getCastKind());
+        GenericCastExpr = dyn_cast<CastExpr>(GenericCastExpr->getSubExpr());
+      }
+
+      Casts.push_back(Op.getCastKindFromCStyleCast());
+    }
+  };
+
+protected:
+  ClangCXXCastTest() : CStyleCastMatcher(cStyleCastExpr().bind(CastVar)) {}
+
+  std::pair<CastKindSet, CXXCastVector> parse(const StringRef Code) {
+    // Parses a single translation unit (from text)
+    // and returns the CXXCasts in order of traversed.
+    std::unique_ptr<clang::ASTUnit> ast(clang::tooling::buildASTFromCode(Code));
+    CStyleCastCollector Collector;
+    MatchFinder Finder;
+    Finder.addMatcher(CStyleCastMatcher, &Collector);
+    Finder.matchAST(ast->getASTContext());
+    return {Collector.CastKinds, Collector.Casts};
+  }
+};
+
+class ClangQualifierModificationTest : public ::testing::Test {
+  StatementMatcher CStyleCastMatcher;
+
+  struct QualifierChecker : MatchFinder::MatchCallback {
+    bool RequireConstCast;
+    bool Pedantic;
+
+    QualifierChecker(const bool Pedantic) : Pedantic(Pedantic){};
+
+    virtual void run(const MatchFinder::MatchResult &Result) override {
+      ASTContext *Context = Result.Context;
+      const CStyleCastExpr *CastExpression =
+          Result.Nodes.getNodeAs<CStyleCastExpr>(CastVar);
+      assert(CastExpression && Context);
+      CStyleCastOperation Op(*CastExpression, *Context, Pedantic);
+
+      RequireConstCast = Op.requireConstCast();
+    }
+  };
+
+protected:
+  ClangQualifierModificationTest()
+      : CStyleCastMatcher(cStyleCastExpr().bind(CastVar)) {}
+
+  bool parse(const StringRef Code, bool Pedantic) {
+    std::unique_ptr<clang::ASTUnit> ast(clang::tooling::buildASTFromCode(Code));
+    QualifierChecker Checker(Pedantic);
+    MatchFinder Finder;
+    Finder.addMatcher(CStyleCastMatcher, &Checker);
+    Finder.matchAST(ast->getASTContext());
+    return Checker.RequireConstCast;
+  }
+};
+
+class ClangFunctionPtrDetectionTest : public ::testing::Test {
+  DeclarationMatcher VarDeclMatcher;
+
+  struct FunctionPtrDetector : MatchFinder::MatchCallback {
+    bool FoundFunctionPtr;
+    FunctionPtrDetector() = default;
+
+    virtual void run(const MatchFinder::MatchResult &Result) override {
+      const VarDecl *DeclExpr = Result.Nodes.getNodeAs<VarDecl>(DeclVar);
+      assert(DeclExpr);
+      QualType CanonicalDeclType = DeclExpr->getType().getCanonicalType();
+      FoundFunctionPtr = details::isFunctionPtr(CanonicalDeclType);
+    }
+  };
+
+protected:
+  ClangFunctionPtrDetectionTest() : VarDeclMatcher(varDecl().bind(DeclVar)) {}
+
+  bool parse(const StringRef Code) {
+    std::unique_ptr<clang::ASTUnit> ast(clang::tooling::buildASTFromCode(Code));
+    FunctionPtrDetector Detector;
+    MatchFinder Finder;
+    Finder.addMatcher(VarDeclMatcher, &Detector);
+    Finder.matchAST(ast->getASTContext());
+    return Detector.FoundFunctionPtr;
+  }
+};
+
+class ChangeQualifierTest : public ::testing::Test {
+  StatementMatcher CStyleCastMatcher;
+  DeclarationMatcher DeclMatcher;
+
+  struct QualifierChanger : MatchFinder::MatchCallback {
+    QualType ChangedCanonicalType;
+    bool Pedantic;
+    QualifierChanger(const bool Pedantic) : Pedantic(Pedantic) {}
+
+    virtual void run(const MatchFinder::MatchResult &Result) override {
+      ASTContext *Context = Result.Context;
+      const CStyleCastExpr *CastExpression =
+          Result.Nodes.getNodeAs<CStyleCastExpr>(CastVar);
+      assert(CastExpression && Context);
+      CStyleCastOperation Op(*CastExpression, *Context, Pedantic);
+
+      ChangedCanonicalType = Op.changeQualifiers().getCanonicalType();
+    }
+  };
+
+  struct DeclTypeMatcher : MatchFinder::MatchCallback {
+    QualType FoundCanonicalType;
+    DeclTypeMatcher() = default;
+
+    virtual void run(const MatchFinder::MatchResult &Result) override {
+      const VarDecl *DeclExpr = Result.Nodes.getNodeAs<VarDecl>(DeclVar);
+      assert(DeclExpr);
+
+      FoundCanonicalType = DeclExpr->getType().getCanonicalType();
+    }
+  };
+
+protected:
+  ChangeQualifierTest()
+      : CStyleCastMatcher(cStyleCastExpr().bind(CastVar)),
+        DeclMatcher(varDecl().bind(DeclVar)) {}
+
+  bool parse(const StringRef CastCode, bool Pedantic) {
+    std::unique_ptr<clang::ASTUnit> CastAst(
+        clang::tooling::buildASTFromCode(CastCode));
+    std::unique_ptr<clang::ASTUnit> TypeAst(
+        clang::tooling::buildASTFromCode(CastCode));
+    QualifierChanger Changer(Pedantic);
+    DeclTypeMatcher TypeMatcher;
+    {
+      MatchFinder Finder;
+      Finder.addMatcher(CStyleCastMatcher, &Changer);
+      Finder.matchAST(CastAst->getASTContext());
+    }
+    {
+      MatchFinder Finder;
+      Finder.addMatcher(DeclMatcher, &TypeMatcher);
+      Finder.matchAST(TypeAst->getASTContext());
+    }
+    return Changer.ChangedCanonicalType.getAsString() ==
+           TypeMatcher.FoundCanonicalType.getAsString();
+  }
+};
+
+TEST_F(ClangCXXCastTest, TestNoOpCastTypes) {
+  CLANG_CXX_CAST_CHECK(NoOp, NoOpCast);
+  CLANG_CXX_CAST_CHECK(ArrayToPointerDecay, NoOpCast);
+  // Unchecked: CLANG_CXX_CAST_CHECK(LValueToRValue, ConstCast);
+}
+
+TEST_F(ClangCXXCastTest, TestReinterpretCastTypes) {
+  CLANG_CXX_CAST_CHECK(BitCast, ReinterpretCast);
+  CLANG_CXX_CAST_CHECK(LValueBitCast, ReinterpretCast);
+  CLANG_CXX_CAST_CHECK(IntegralToPointer, ReinterpretCast);
+  CLANG_CXX_CAST_CHECK(ReinterpretMemberPointer, ReinterpretCast);
+  CLANG_CXX_CAST_CHECK(PointerToIntegral, ReinterpretCast);
+}
+
+TEST_F(ClangCXXCastTest, TestStaticCastTypes) {
+  CLANG_CXX_CAST_CHECK(BaseToDerived, StaticCast);
+  CLANG_CXX_CAST_CHECK(DerivedToBase, StaticCast);
+  // Unchecked: CLANG_CXX_CAST_CHECK(UncheckedDerivedToBase, StaticCast);
+  CLANG_CXX_CAST_CHECK(FunctionToPointerDecay, StaticCast);
+  CLANG_CXX_CAST_CHECK(NullToPointer, StaticCast);
+  CLANG_CXX_CAST_CHECK(NullToMemberPointer, StaticCast);
+  CLANG_CXX_CAST_CHECK(BaseToDerivedMemberPointer, StaticCast);
+  CLANG_CXX_CAST_CHECK(DerivedToBaseMemberPointer, StaticCast);
+  CLANG_CXX_CAST_CHECK(MemberPointerToBoolean, StaticCast);
+  CLANG_CXX_CAST_CHECK(UserDefinedConversion, StaticCast);
+  CLANG_CXX_CAST_CHECK(ConstructorConversion, StaticCast);
+  CLANG_CXX_CAST_CHECK(PointerToBoolean, StaticCast);
+  CLANG_CXX_CAST_CHECK(ToVoid, StaticCast);
+  CLANG_CXX_CAST_CHECK(VectorSplat, StaticCast);
+  CLANG_CXX_CAST_CHECK(IntegralCast, StaticCast);
+  CLANG_CXX_CAST_CHECK(IntegralToBoolean, StaticCast);
+  CLANG_CXX_CAST_CHECK(IntegralToFloating, StaticCast);
+  CLANG_CXX_CAST_CHECK(FloatingToIntegral, StaticCast);
+  CLANG_CXX_CAST_CHECK(FloatingToBoolean, StaticCast);
+  CLANG_CXX_CAST_CHECK(FloatingCast, StaticCast);
+  CLANG_CXX_CAST_CHECK(FloatingRealToComplex, StaticCast);
+  CLANG_CXX_CAST_CHECK(FloatingComplexToReal, StaticCast);
+  CLANG_CXX_CAST_CHECK(FloatingComplexToBoolean, StaticCast);
+  CLANG_CXX_CAST_CHECK(FloatingComplexCast, StaticCast);
+  CLANG_CXX_CAST_CHECK(FloatingComplexToIntegralComplex, StaticCast);
+  CLANG_CXX_CAST_CHECK(IntegralRealToComplex, StaticCast);
+  CLANG_CXX_CAST_CHECK(IntegralComplexToReal, StaticCast);
+  CLANG_CXX_CAST_CHECK(IntegralComplexToBoolean, StaticCast);
+  CLANG_CXX_CAST_CHECK(IntegralComplexCast, StaticCast);
+  CLANG_CXX_CAST_CHECK(IntegralComplexToFloatingComplex, StaticCast);
+  CLANG_CXX_CAST_CHECK(AtomicToNonAtomic, StaticCast);
+  CLANG_CXX_CAST_CHECK(NonAtomicToAtomic, StaticCast);
+}
+
+TEST_F(ClangCXXCastTest, TestCStyleCastTypes) {
+  CLANG_CXX_CAST_CHECK(Dependent, CStyleCast);
+}
+
+TEST_F(ClangCXXCastTest, TestEdgeCases) {
+  using namespace edgecases;
+  {
+    auto res = parse(DerivedToBasePrivateSpecifier);
+    ASSERT_GE(res.first.size(), 1ul);
+    ASSERT_GE(res.second.size(), 1ul);
+    ASSERT_TRUE(res.first.find(CastKind::CK_DerivedToBase) != res.first.end());
+    ASSERT_EQ(res.second[0], CXXCast::CC_CStyleCast);
+  }
+  {
+    auto res = parse(BaseToDerivedPrivateSpecifier);
+    ASSERT_GE(res.first.size(), 1ul);
+    ASSERT_GE(res.second.size(), 1ul);
+    ASSERT_TRUE(res.first.find(CastKind::CK_BaseToDerived) != res.first.end());
+    ASSERT_EQ(res.second[0], CXXCast::CC_CStyleCast);
+  }
+  CLANG_CXX_CAST_CHECK(Dependent, CStyleCast);
+}
+
+/// These tests mean:
+/// Does the C-style cast in <test case> require a const_cast?
+TEST_F(ClangQualifierModificationTest, TestConstCases) {
+  CLANG_QUAL_CHECK(QualNoOp, false, false);
+  // add
+  // we perform these operations in order to first do a sanity check that
+  // 1. const cast isn't needed for upcasting
+  // 2. there will be no segmentation faults before we run the removal tests
+  CLANG_QUAL_CHECK(QualAddConst, false, false);
+  CLANG_QUAL_CHECK(QualAddPtrToConst, false, false);
+  CLANG_QUAL_CHECK(QualAddConstPtr, false, false);
+  CLANG_QUAL_CHECK(QualAddConstDoublePtr, false, false);
+  CLANG_QUAL_CHECK(QualAddConstDiffLevelPtr, false, false);
+  CLANG_QUAL_CHECK(QualAddMemberPtrToConst, false, false);
+  CLANG_QUAL_CHECK(QualAddConstMemberPtr, false, false);
+  CLANG_QUAL_CHECK(QualAddConstDoubleMemberPtr, false, false);
+  CLANG_QUAL_CHECK(QualAddConstDiffLevelMemberPtr, false, false);
+  CLANG_QUAL_CHECK(QualAddConstRef, false, false);
+  CLANG_QUAL_CHECK(QualAddConstArr, false, false);
+  CLANG_QUAL_CHECK(QualAddConstPtrToArr, false, false);
+  CLANG_QUAL_CHECK(QualAddConstPtrToArrOfConstPtrs, false, false);
+  CLANG_QUAL_CHECK(QualAddArrPtrConstData, false, false);
+  CLANG_QUAL_CHECK(QualAddDiffLevelArrPtrConstData, false, false);
+  CLANG_QUAL_CHECK(QualAddConstMixedPtrTypes, false, false);
+  CLANG_QUAL_CHECK(QualAddConstUnknownArrPtr, false, false);
+  CLANG_QUAL_CHECK(QualAddConstUnknownArrPtrToKnownArrPtr, false, false);
+
+  // remove
+  // we perform these operations in order to check the positive cases, along
+  // with negative edge cases.
+  // does not require const cast - implicit
+  CLANG_QUAL_CHECK(QualRemoveConst, false, false);
+  // does require const cast, base type downcast
+  CLANG_QUAL_CHECK(QualRemovePtrToConst, true, false);
+  // does not - implicit
+  CLANG_QUAL_CHECK(QualRemoveConstPtr, false, false);
+  // does - downcast
+  CLANG_QUAL_CHECK(QualRemoveConstDoublePtr, true, false);
+  // does not - level truncated
+  CLANG_QUAL_CHECK(QualRemoveConstDiffLevelPtr, false, false);
+
+  // Same as the above 4
+  CLANG_QUAL_CHECK(QualRemoveMemberPtrToConst, true, false);
+  CLANG_QUAL_CHECK(QualRemoveConstMemberPtr, false, false);
+  CLANG_QUAL_CHECK(QualRemoveConstDoubleMemberPtr, true, false);
+  CLANG_QUAL_CHECK(QualRemoveConstDiffLevelMemberPtr, false, false);
+
+  // does - downcast
+  CLANG_QUAL_CHECK(QualRemoveConstRef, true, false);
+  // does - downcast
+  CLANG_QUAL_CHECK(QualRemoveConstArr, true, false);
+  // does not - implicit
+  CLANG_QUAL_CHECK(QualRemoveConstPtrToArr, false, false);
+  // does - downcast
+  CLANG_QUAL_CHECK(QualRemoveConstPtrToArrOfConstPtrs, true, false);
+  // does - downcast
+  CLANG_QUAL_CHECK(QualRemoveArrPtrConstData, true, false);
+  // does not - level truncated
+  CLANG_QUAL_CHECK(QualRemoveDiffLevelArrPtrConstData, false, false);
+  // does - similar types going down
+  CLANG_QUAL_CHECK(QualRemoveSimilarPtrsBeyondArrConstData, true, false);
+  // does - All pointer-like types are downcasted
+  CLANG_QUAL_CHECK(QualRemoveConstMixedPtrTypes, true, false);
+  // does - Unknown size array is similar to unknown size array
+  CLANG_QUAL_CHECK(QualRemoveConstUnknownArrPtr, true, false);
+  // does - Unknown size array is similar to known size array
+  CLANG_QUAL_CHECK(QualRemoveConstUnknownArrPtrToKnownArrPtr, true, false);
+
+  // Checking for pedantic changes
+  CLANG_QUAL_CHECK(QualNoOp, false, false);
+  CLANG_QUAL_CHECK(QualAddConst, false, false);
+  CLANG_QUAL_CHECK(QualAddPtrToConst, false, false);
+  CLANG_QUAL_CHECK(QualAddConstPtr, false, false);
+  CLANG_QUAL_CHECK(QualAddConstDoublePtr, false, false);
+  CLANG_QUAL_CHECK(QualAddConstDiffLevelPtr, false, false);
+  CLANG_QUAL_CHECK(QualAddMemberPtrToConst, false, false);
+  CLANG_QUAL_CHECK(QualAddConstMemberPtr, false, false);
+  CLANG_QUAL_CHECK(QualAddConstDoubleMemberPtr, false, false);
+  CLANG_QUAL_CHECK(QualAddConstDiffLevelMemberPtr, false, false);
+  CLANG_QUAL_CHECK(QualAddConstRef, false, false);
+  CLANG_QUAL_CHECK(QualAddConstArr, false, false);
+  CLANG_QUAL_CHECK(QualAddConstPtrToArr, false, false);
+  CLANG_QUAL_CHECK(QualAddConstPtrToArrOfConstPtrs, false, false);
+  CLANG_QUAL_CHECK(QualAddArrPtrConstData, false, false);
+  CLANG_QUAL_CHECK(QualAddDiffLevelArrPtrConstData, false, false);
+  CLANG_QUAL_CHECK(QualAddConstMixedPtrTypes, false, false);
+  CLANG_QUAL_CHECK(QualAddConstUnknownArrPtr, false, false);
+  CLANG_QUAL_CHECK(QualAddConstUnknownArrPtrToKnownArrPtr, false, false);
+
+  CLANG_QUAL_CHECK(QualRemoveConst, false, false);
+  CLANG_QUAL_CHECK(QualRemovePtrToConst, true, false);
+  CLANG_QUAL_CHECK(QualRemoveConstPtr, false, false);
+  CLANG_QUAL_CHECK(QualRemoveConstDoublePtr, true, false);
+  CLANG_QUAL_CHECK(QualRemoveConstDiffLevelPtr, false, false);
+  CLANG_QUAL_CHECK(QualRemoveMemberPtrToConst, true, false);
+  CLANG_QUAL_CHECK(QualRemoveConstMemberPtr, false, false);
+  CLANG_QUAL_CHECK(QualRemoveConstDoubleMemberPtr, true, false);
+  CLANG_QUAL_CHECK(QualRemoveConstDiffLevelMemberPtr, false, false);
+  CLANG_QUAL_CHECK(QualRemoveConstRef, true, false);
+  CLANG_QUAL_CHECK(QualRemoveConstArr, true, false);
+  CLANG_QUAL_CHECK(QualRemoveConstPtrToArr, false, false);
+  CLANG_QUAL_CHECK(QualRemoveConstPtrToArrOfConstPtrs, true, false);
+  CLANG_QUAL_CHECK(QualRemoveArrPtrConstData, true, false);
+  CLANG_QUAL_CHECK(QualRemoveDiffLevelArrPtrConstData, false, false);
+  CLANG_QUAL_CHECK(QualRemoveSimilarPtrsBeyondArrConstData, true, false);
+  CLANG_QUAL_CHECK(QualRemoveConstMixedPtrTypes, true, false);
+  CLANG_QUAL_CHECK(QualRemoveConstUnknownArrPtr, true, false);
+  CLANG_QUAL_CHECK(QualRemoveConstUnknownArrPtrToKnownArrPtr, true, false);
+}
+
+TEST_F(ClangQualifierModificationTest, TestVolatileCases) {
+  // add
+  CLANG_QUAL_CHECK(QualAddVolatile, false, false);
+  CLANG_QUAL_CHECK(QualAddPtrToVolatile, false, false);
+  CLANG_QUAL_CHECK(QualAddVolatilePtr, false, false);
+  CLANG_QUAL_CHECK(QualAddVolatileDoublePtr, false, false);
+  CLANG_QUAL_CHECK(QualAddVolatileDiffLevelPtr, false, false);
+  CLANG_QUAL_CHECK(QualAddVolatileRef, false, false);
+  CLANG_QUAL_CHECK(QualAddVolatileArr, false, false);
+  CLANG_QUAL_CHECK(QualAddVolatilePtrToArr, false, false);
+  CLANG_QUAL_CHECK(QualAddVolatilePtrToArrOfVolatilePtrs, false, false);
+  CLANG_QUAL_CHECK(QualAddArrPtrVolatileData, false, false);
+  CLANG_QUAL_CHECK(QualAddDiffLevelArrPtrVolatileData, false, false);
+  CLANG_QUAL_CHECK(QualAddVolatileMixedPtrTypes, false, false);
+  CLANG_QUAL_CHECK(QualAddVolatileUnknownArrPtr, false, false);
+  CLANG_QUAL_CHECK(QualAddVolatileUnknownArrPtrToKnownArrPtr, false, false);
+
+  // remove
+  CLANG_QUAL_CHECK(QualRemoveVolatile, false, false);
+  CLANG_QUAL_CHECK(QualRemovePtrToVolatile, true, false);
+  CLANG_QUAL_CHECK(QualRemoveVolatilePtr, false, false);
+  CLANG_QUAL_CHECK(QualRemoveVolatileDoublePtr, true, false);
+  CLANG_QUAL_CHECK(QualRemoveVolatileDiffLevelPtr, false, false);
+  CLANG_QUAL_CHECK(QualRemoveVolatileRef, true, false);
+  CLANG_QUAL_CHECK(QualRemoveVolatileArr, true, false);
+  CLANG_QUAL_CHECK(QualRemoveVolatilePtrToArr, false, false);
+  CLANG_QUAL_CHECK(QualRemoveVolatilePtrToArrOfVolatilePtrs, true, false);
+  CLANG_QUAL_CHECK(QualRemoveArrPtrVolatileData, true, false);
+  CLANG_QUAL_CHECK(QualRemoveDiffLevelArrPtrVolatileData, false, false);
+  CLANG_QUAL_CHECK(QualRemoveSimilarPtrsBeyondArrVolatileData, true, false);
+  CLANG_QUAL_CHECK(QualRemoveVolatileMixedPtrTypes, true, false);
+  CLANG_QUAL_CHECK(QualRemoveVolatileUnknownArrPtr, true, false);
+  CLANG_QUAL_CHECK(QualRemoveVolatileUnknownArrPtrToKnownArrPtr, true, false);
+}
+
+TEST_F(ClangQualifierModificationTest, TestRestrictCases) {
+  // add
+  CLANG_QUAL_CHECK(QualAddRestrictPtr, false, false);
+  CLANG_QUAL_CHECK(QualAddRestrictDoublePtr, false, false);
+  CLANG_QUAL_CHECK(QualAddRestrictDiffLevelPtr, false, false);
+  CLANG_QUAL_CHECK(QualAddRestrictArr, false, false);
+  CLANG_QUAL_CHECK(QualAddRestrictPtrToArr, false, false);
+  CLANG_QUAL_CHECK(QualAddRestrictPtrToArrOfRestrictPtrs, false, false);
+  CLANG_QUAL_CHECK(QualAddArrPtrRestrictData, false, false);
+  CLANG_QUAL_CHECK(QualAddDiffLevelArrPtrRestrictData, false, false);
+  CLANG_QUAL_CHECK(QualAddRestrictMixedPtrTypes, false, false);
+  CLANG_QUAL_CHECK(QualAddRestrictUnknownArrPtr, false, false);
+  CLANG_QUAL_CHECK(QualAddRestrictUnknownArrPtrToKnownArrPtr, false, false);
+
+  // remove
+  CLANG_QUAL_CHECK(QualRemoveRestrictPtr, false, false);
+  CLANG_QUAL_CHECK(QualRemoveRestrictDoublePtr, true, false);
+  CLANG_QUAL_CHECK(QualRemoveRestrictDiffLevelPtr, false, false);
+  CLANG_QUAL_CHECK(QualRemoveRestrictArr, true, false);
+  CLANG_QUAL_CHECK(QualRemoveRestrictPtrToArr, false, false);
+  CLANG_QUAL_CHECK(QualRemoveRestrictPtrToArrOfRestrictPtrs, true, false);
+  CLANG_QUAL_CHECK(QualRemoveArrPtrRestrictData, true, false);
+  CLANG_QUAL_CHECK(QualRemoveDiffLevelArrPtrRestrictData, false, false);
+  CLANG_QUAL_CHECK(QualRemoveSimilarPtrsBeyondArrRestrictData, true, false);
+  CLANG_QUAL_CHECK(QualRemoveRestrictMixedPtrTypes, true, false);
+  CLANG_QUAL_CHECK(QualRemoveRestrictUnknownArrPtr, true, false);
+  CLANG_QUAL_CHECK(QualRemoveRestrictUnknownArrPtrToKnownArrPtr, true, false);
+}
+
+TEST_F(ClangFunctionPtrDetectionTest, TestFuncPtrs) {
+  CLANG_FUNC_PTR_CHECK(Scalar, false);
+  CLANG_FUNC_PTR_CHECK(ArrOfFreeFunctionPtr, false);
+  CLANG_FUNC_PTR_CHECK(NestedFreeFunctionPtr, false);
+  CLANG_FUNC_PTR_CHECK(FreeFunction, true);
+  CLANG_FUNC_PTR_CHECK(ArrOfMemberFunction, false);
+  CLANG_FUNC_PTR_CHECK(NestedMemberFunction, false);
+  CLANG_FUNC_PTR_CHECK(MemberFunction, true);
+}
+
+TEST_F(ChangeQualifierTest, TestChangeQualifiers) {
+  ASSERT_TRUE(parse(extension::NoQualifierChange, false));
+  ASSERT_TRUE(parse(extension::NoQualifierChangeReinterpret, false));
+  ASSERT_TRUE(parse(extension::ChangeNestedPointers, false));
+  ASSERT_TRUE(parse(extension::ChangeNestedPointersReinterpret, false));
+  ASSERT_TRUE(parse(extension::ChangeNestedPointersUntilArray, false));
+  ASSERT_TRUE(
+      parse(extension::ChangeNestedPointersUntilArrayReinterpret, false));
+  ASSERT_TRUE(parse(extension::NoModificationToMixedPtrTypes, false));
+  ASSERT_TRUE(parse(extension::DontChangeMemberFuncPtr, false));
+  ASSERT_TRUE(parse(extension::ChangeNestedPointersUntilMemberVsNot, false));
+
+  ASSERT_TRUE(parse(pedantic::NoQualifierChange, true));
+  ASSERT_TRUE(parse(pedantic::NoQualifierChangeReinterpret, true));
+  ASSERT_TRUE(parse(pedantic::ChangeNestedPointers, true));
+  ASSERT_TRUE(parse(pedantic::ChangeNestedPointersReinterpret, true));
+  ASSERT_TRUE(parse(pedantic::ChangeNestedPointersUntilArray, true));
+  ASSERT_TRUE(parse(pedantic::ChangeNestedPointersUntilArrayReinterpret, true));
+  ASSERT_TRUE(parse(pedantic::NoModificationToMixedPtrTypes, true));
+  ASSERT_TRUE(parse(pedantic::DontChangeMemberFuncPtr, true));
+  ASSERT_TRUE(parse(pedantic::ChangeNestedPointersUntilMemberVsNot, true));
+}
diff --git a/clang-tools-extra/unittests/clang-cast/ClangChangeQualifierTestCases.h b/clang-tools-extra/unittests/clang-cast/ClangChangeQualifierTestCases.h
new file mode 100644
--- /dev/null
+++ b/clang-tools-extra/unittests/clang-cast/ClangChangeQualifierTestCases.h
@@ -0,0 +1,199 @@
+//===-- ClangChangeQualifierTestCases.h - clang-cast ------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains test cases for
+/// CStyleCast::changeQualifiers (defined in Cast.h)
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_PROJECT_CLANGCHANGEQUALIFIERTESTCASES_H
+#define LLVM_PROJECT_CLANGCHANGEQUALIFIERTESTCASES_H
+
+/// NOTE: We are not performing an implicit cast conversion, so the types
+/// are exactly decltype(p) when we pass it into changeQualifiers().
+namespace testcases {
+namespace changequal {
+namespace extension {
+static const char NoQualifierChange[] = R"(
+void f() {
+  const int * const p {};
+  (const int * const) p;
+  // Expected type
+  const int * const x {};
+}
+)";
+static const char NoQualifierChangeReinterpret[] = R"(
+void f() {
+  const int * const p {};
+  (const double * const) p;
+  // Expected type
+  const double * const x {};
+}
+)";
+
+// Change all of these pointer qualifiers down
+static const char ChangeNestedPointers[] = R"(
+void f() {
+  const int * const * volatile * __restrict p {};
+  (int***) p;
+  // Expected type
+  const int* const * volatile * __restrict x {};
+}
+)";
+static const char ChangeNestedPointersReinterpret[] = R"(
+void f() {
+  const int * const * volatile * __restrict p {};
+  (double****) p;
+  // Expected type (note how we have 1 more layer of ptr)
+  double* const * const * volatile * __restrict x {};
+}
+)";
+
+// Stop the const cast up to an array type
+static const char ChangeNestedPointersUntilArray[] = R"(
+void f() {
+  int * const * volatile * __restrict (* const * volatile * __restrict p) [2] {};
+  (int*** (***)[2]) p;
+  // Expected type
+  int * const * volatile * __restrict (* const * volatile * __restrict x) [2] {};
+}
+)";
+static const char ChangeNestedPointersUntilArrayReinterpret[] = R"(
+void f() {
+  int * const * volatile * __restrict (* const * volatile * __restrict p) [2] {};
+  (double**** (***)[2]) p;
+  // Expected type
+  double ** const * volatile * __restrict (* const * volatile * __restrict x) [2] {};
+}
+)";
+static const char NoModificationToMixedPtrTypes[] = R"(
+struct t{};
+void f() {
+  double * __restrict t::* __restrict (t::* __restrict * __restrict a) [2];
+  (double * t::* (t::* *) [2]) a;
+  // Expected type
+  double * __restrict t::* __restrict (t::* __restrict * __restrict x) [2];
+}
+)";
+
+// The member function dropped const, but const_cast can't perform this
+// operation. It's up to reinterpret_cast to perform this.
+static const char DontChangeMemberFuncPtr[] = R"(
+struct t{};
+void f() {
+  void (t::* p)(void) const {};
+  (void (t::*)(void)) p;
+  // Expected type
+  void (t::* x)(void) {};
+}
+)";
+
+static const char ChangeNestedPointersUntilMemberVsNot[] = R"(
+struct t{};
+void f() {
+    const int* const t::* const t::* const p {};
+    (int** t::*) p;
+    // Expected type
+    int** const t::* const x {};
+}
+)";
+} // namespace extension
+
+namespace pedantic {
+
+static const char NoQualifierChange[] = R"(
+void f() {
+  const int * const p {};
+  (const int * const) p;
+  // Expected type
+  const int * const x {};
+}
+)";
+static const char NoQualifierChangeReinterpret[] = R"(
+void f() {
+  const int * const p {};
+  (const double * const) p;
+  // Expected type
+  const double * const x {};
+}
+)";
+
+// Change all of these pointer qualifiers down
+static const char ChangeNestedPointers[] = R"(
+void f() {
+  const int * const * volatile * __restrict p {};
+  (int***) p;
+  // Expected type
+  const int* const * volatile * __restrict x {};
+}
+)";
+static const char ChangeNestedPointersReinterpret[] = R"(
+void f() {
+  const int * const * volatile * __restrict p {};
+  (double****) p;
+  // Expected type (note how we have 1 more layer of ptr)
+  double* const * const * volatile * __restrict x {};
+}
+)";
+
+// Stop the const cast up to an array type
+static const char ChangeNestedPointersUntilArray[] = R"(
+void f() {
+  int * const * volatile * __restrict (* const * volatile * __restrict p) [2] {};
+  (int*** (***)[2]) p;
+  // Expected type
+  int * const * volatile * __restrict (* const * volatile * __restrict x) [2] {};
+}
+)";
+static const char ChangeNestedPointersUntilArrayReinterpret[] = R"(
+void f() {
+  int * const * volatile * __restrict (* const * volatile * __restrict p) [2] {};
+  (double**** (***)[2]) p;
+  // Expected type
+  double ** const * volatile * __restrict (* const * volatile * __restrict x) [2] {};
+}
+)";
+static const char NoModificationToMixedPtrTypes[] = R"(
+struct t{};
+void f() {
+  double * __restrict t::* __restrict (t::* __restrict * __restrict a) [2];
+  (double * t::* (t::* *) [2]) a;
+  // Expected type
+  double * __restrict t::* __restrict (t::* __restrict * __restrict x) [2];
+}
+)";
+
+// The member function dropped const, but const_cast can't perform this
+// operation. It's up to reinterpret_cast to perform this.
+static const char DontChangeMemberFuncPtr[] = R"(
+struct t{};
+void f() {
+  void (t::* p)(void) const {};
+  (void (t::*)(void)) p;
+  // Expected type
+  void (t::* x)(void) {};
+}
+)";
+
+static const char ChangeNestedPointersUntilMemberVsNot[] = R"(
+struct t{};
+void f() {
+    const int* const t::* const t::* const p {};
+    (int** t::*) p;
+    // Expected type
+    const int* const* const t::* const x {};
+}
+)";
+
+} // namespace pedantic
+
+} // namespace changequal
+} // namespace testcases
+
+#endif // LLVM_PROJECT_CLANGCHANGEQUALIFIERTESTCASES_H
diff --git a/clang-tools-extra/unittests/clang-cast/ClangFunctionPtrTestCases.h b/clang-tools-extra/unittests/clang-cast/ClangFunctionPtrTestCases.h
new file mode 100644
--- /dev/null
+++ b/clang-tools-extra/unittests/clang-cast/ClangFunctionPtrTestCases.h
@@ -0,0 +1,71 @@
+//===-- ClangFunctionPtrTestCases.h - clang-cast ----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains test cases for
+/// details::isFunctionPtr (defined in CastUtils.h)
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_PROJECT_CLANGFUNCTIONPTRTESTCASES_H
+#define LLVM_PROJECT_CLANGFUNCTIONPTRTESTCASES_H
+
+namespace testcases {
+namespace funcptr {
+
+// Out of all of these tests, we should ideally only identify the cases of
+// FreeFunction and MemberFunction.
+
+static const char Scalar[] = R"(
+void f() {
+  bool a {};
+}
+)";
+
+static const char ArrOfFreeFunctionPtr[] = R"(
+void f() {
+  void (*a[2])(void){};
+}
+)";
+
+static const char NestedFreeFunctionPtr[] = R"(
+void f() {
+  void (**a)(void) {};
+}
+)";
+
+static const char FreeFunction[] = R"(
+void f() {
+  void (*a)(void) {};
+}
+)";
+
+static const char ArrOfMemberFunction[] = R"(
+struct t{};
+void f() {
+  void (t::* a[2])(void) const {};
+}
+)";
+
+static const char NestedMemberFunction[] = R"(
+struct t{};
+void f() {
+  void (t::** a)(void) const {};
+}
+)";
+
+static const char MemberFunction[] = R"(
+struct t{};
+void f() {
+  void (t::* a)(void) const {};
+}
+)";
+} // namespace funcptr
+} // namespace testcases
+
+#endif // LLVM_PROJECT_CLANGFUNCTIONPTRTESTCASES_H
diff --git a/clang-tools-extra/unittests/clang-cast/ClangQualifierTestCases.h b/clang-tools-extra/unittests/clang-cast/ClangQualifierTestCases.h
new file mode 100644
--- /dev/null
+++ b/clang-tools-extra/unittests/clang-cast/ClangQualifierTestCases.h
@@ -0,0 +1,603 @@
+//===-- ClangQualifierTestCases.h - clang-cast ------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains test cases for
+/// CStyleCastOperation::requireConstCast (defined in Cast.h)
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_CAST_CLANGQUALIFIERTESTCASES_H
+#define LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_CAST_CLANGQUALIFIERTESTCASES_H
+
+namespace testcases {
+
+namespace constcheck {
+/// No-op
+static const char QualNoOp[] = R"(
+void f() {
+  (bool) 0;
+}
+)";
+
+/// Const
+/// Adding const doesn't require const_cast
+static const char QualAddConst[] = R"(
+void f() {
+  int x = 0;
+  (const int) x;
+}
+)";
+static const char QualAddPtrToConst[] = R"(
+void f() {
+  int* x = nullptr;
+  const int* y = (const int*) x;
+}
+)";
+static const char QualAddConstPtr[] = R"(
+void f() {
+  int* x = nullptr;
+  int* const y = (int* const) x;
+}
+)";
+static const char QualAddConstDoublePtr[] = R"(
+void f() {
+  int** x = nullptr;
+  int* const * const y = (int* const * const) x;
+}
+)";
+static const char QualAddConstDiffLevelPtr[] = R"(
+void f() {
+  int*** x = nullptr;
+  const int** y = (const int**) x;
+}
+)";
+static const char QualAddMemberPtrToConst[] = R"(
+struct t{};
+void f() {
+  int t::* x = nullptr;
+  const int t::* y = (const int t::*) x;
+}
+)";
+static const char QualAddConstMemberPtr[] = R"(
+struct t{};
+void f() {
+  int t::* x = nullptr;
+  int t::* const y = (int t::* const) x;
+}
+)";
+static const char QualAddConstDoubleMemberPtr[] = R"(
+struct t{};
+void f() {
+  int t::* t::* x = nullptr;
+  int t::* const t::* const y = (int t::* const t::* const) x;
+}
+)";
+static const char QualAddConstDiffLevelMemberPtr[] = R"(
+struct t{};
+void f() {
+  int t::* t::* t::* x = nullptr;
+  const int t::* t::* y = (const int t::* t::*) x;
+}
+)";
+static const char QualAddConstRef[] = R"(
+void f() {
+  int x = 1;
+  int& y = x;
+  const int& z = (const int&) y;
+}
+)";
+static const char QualAddConstArr[] = R"(
+void f() {
+  double a[2] {1, 2};
+  const double* ca = (const double*) a;
+}
+)";
+static const char QualAddConstPtrToArr[] = R"(
+void f() {
+  double (*a)[2] {};
+  (double(* const)[2]) a;
+}
+)";
+static const char QualAddConstPtrToArrOfConstPtrs[] = R"(
+void f() {
+  double* (*a)[2] {};
+  (double * const (* const)[2]) a;
+}
+)";
+static const char QualAddArrPtrConstData[] = R"(
+void f() {
+  double (*a)[2] {};
+  (const double(*)[2]) a;
+}
+)";
+static const char QualAddDiffLevelArrPtrConstData[] = R"(
+void f() {
+  double (*a)[2] {};
+  (const double* (*)[2]) a;
+}
+)";
+static const char QualAddConstMixedPtrTypes[] = R"(
+struct t {};
+void f() {
+  double * t::* (t::* *a) [2];
+  (const double * const t::* const (t::* const * const) [2]) a;
+}
+)";
+static const char QualAddConstUnknownArrPtr[] = R"(
+void f() {
+  int* (*x) [] {};
+  (const int* const (*)[]) x;
+}
+)";
+static const char QualAddConstUnknownArrPtrToKnownArrPtr[] = R"(
+void f() {
+  int* (*x) [] {};
+  (const int* const (*)[2]) x;
+}
+)";
+
+/// Removing const MIGHT require const_cast
+static const char QualRemoveConst[] = R"(
+void f() {
+  const int x = 0;
+  (int) x;
+}
+)";
+static const char QualRemovePtrToConst[] = R"(
+void f() {
+  const int* x = nullptr;
+  int* y = (int*) x;
+}
+)";
+static const char QualRemoveConstPtr[] = R"(
+void f() {
+  int* const x = nullptr;
+  int* y = (int*) x;
+}
+)";
+static const char QualRemoveConstDoublePtr[] = R"(
+void f() {
+  int* const * const x = nullptr;
+  int** y = (int**) x;
+}
+)";
+static const char QualRemoveConstDiffLevelPtr[] = R"(
+void f() {
+  const int*** x = nullptr;
+  int** y = (int**) x;
+}
+)";
+static const char QualRemoveMemberPtrToConst[] = R"(
+struct t{};
+void f() {
+  const int t::* x = nullptr;
+  int t::* y = (int t::*) x;
+}
+)";
+static const char QualRemoveConstMemberPtr[] = R"(
+struct t{};
+void f() {
+  int t::* const x = nullptr;
+  int t::* y = (int t::*) x;
+}
+)";
+static const char QualRemoveConstDoubleMemberPtr[] = R"(
+struct t{};
+void f() {
+  int t::* const  t::* const x = nullptr;
+  int t::* t::* y = (int t::* t::*) x;
+}
+)";
+static const char QualRemoveConstDiffLevelMemberPtr[] = R"(
+struct t{};
+void f() {
+  const int t::* t::* t::* x = nullptr;
+  int t::* t::* y = (int t::* t::*) x;
+}
+)";
+static const char QualRemoveConstRef[] = R"(
+void f() {
+  int x = 1;
+  const int& y = x;
+  int& z = (int&) y;
+}
+)";
+static const char QualRemoveConstArr[] = R"(
+void f() {
+  const double a[2] {1, 2};
+  double* ca = (double*) a;
+}
+)";
+static const char QualRemoveConstPtrToArr[] = R"(
+void f() {
+  double (* const a)[2] {};
+  (double(*)[2]) a;
+}
+)";
+static const char QualRemoveConstPtrToArrOfConstPtrs[] = R"(
+void f() {
+  double* const (* const a)[2] {};
+  (double* (*)[2]) a;
+}
+)";
+static const char QualRemoveArrPtrConstData[] = R"(
+void f() {
+  const double (*a)[2] {};
+  (double(*)[2]) a;
+}
+)";
+static const char QualRemoveDiffLevelArrPtrConstData[] = R"(
+void f() {
+  const double* (*a)[2] {};
+  (double (*)[2]) a;
+}
+)";
+static const char QualRemoveSimilarPtrsBeyondArrConstData[] = R"(
+void f() {
+  const double* const (* const a)[2] {};
+  (double* const (* const)[2]) a;
+}
+)";
+static const char QualRemoveConstMixedPtrTypes[] = R"(
+struct t {};
+void f() {
+  const double * const t::* const (t::* const * const a) [2] {};
+  (double * t::* (t::* *) [2]) a;
+}
+)";
+static const char QualRemoveConstUnknownArrPtr[] = R"(
+void f() {
+  const int* const (*x) [] {};
+  (int* (*)[]) x;
+}
+)";
+static const char QualRemoveConstUnknownArrPtrToKnownArrPtr[] = R"(
+void f() {
+  const int* const (*x) [] {};
+  (int* (*)[2]) x;
+}
+)";
+
+/// Volatile
+/// add
+static const char QualAddVolatile[] = R"(
+void f() {
+  int x = 0;
+  (volatile int) x;
+}
+)";
+static const char QualAddPtrToVolatile[] = R"(
+void f() {
+  int* x = nullptr;
+  volatile int* y = (volatile int*) x;
+}
+)";
+static const char QualAddVolatilePtr[] = R"(
+void f() {
+  int* x = nullptr;
+  int* volatile y = (int* volatile) x;
+}
+)";
+static const char QualAddVolatileDoublePtr[] = R"(
+void f() {
+  int** x = nullptr;
+  int* volatile * volatile y = (int* volatile * volatile) x;
+}
+)";
+static const char QualAddVolatileDiffLevelPtr[] = R"(
+void f() {
+  int*** x = nullptr;
+  volatile int** y = (volatile int**) x;
+}
+)";
+static const char QualAddVolatileRef[] = R"(
+void f() {
+  int x = 1;
+  int& y = x;
+  volatile int& z = (volatile int&) y;
+}
+)";
+static const char QualAddVolatileArr[] = R"(
+void f() {
+  double a[2] {1, 2};
+  volatile double* ca = (volatile double*) a;
+}
+)";
+static const char QualAddVolatilePtrToArr[] = R"(
+void f() {
+  double (*a)[2] {};
+  (double(* volatile)[2]) a;
+}
+)";
+static const char QualAddVolatilePtrToArrOfVolatilePtrs[] = R"(
+void f() {
+  double* (*a)[2] {};
+  (double * volatile (* volatile)[2]) a;
+}
+)";
+static const char QualAddArrPtrVolatileData[] = R"(
+void f() {
+  double (*a)[2] {};
+  (volatile double(*)[2]) a;
+}
+)";
+static const char QualAddDiffLevelArrPtrVolatileData[] = R"(
+void f() {
+  double (*a)[2] {};
+  (volatile double* (*)[2]) a;
+}
+)";
+static const char QualRemoveSimilarPtrsBeyondArrVolatileData[] = R"(
+void f() {
+  volatile double* volatile (* volatile a)[2] {};
+  (double* volatile (* volatile)[2]) a;
+}
+)";
+static const char QualAddVolatileMixedPtrTypes[] = R"(
+struct t {};
+void f() {
+  double * t::* (t::* *a) [2];
+  (volatile double * volatile t::* volatile (t::* volatile * volatile) [2]) a;
+}
+)";
+static const char QualAddVolatileUnknownArrPtr[] = R"(
+void f() {
+  int* (*x) [] {};
+  (volatile int* volatile (*)[]) x;
+}
+)";
+static const char QualAddVolatileUnknownArrPtrToKnownArrPtr[] = R"(
+void f() {
+  int* (*x) [] {};
+  (volatile int* volatile (*)[2]) x;
+}
+)";
+
+/// remove
+static const char QualRemoveVolatile[] = R"(
+void f() {
+  volatile int x = 0;
+  (int) x;
+}
+)";
+static const char QualRemovePtrToVolatile[] = R"(
+void f() {
+  volatile int* x = nullptr;
+  int* y = (int*) x;
+}
+)";
+static const char QualRemoveVolatilePtr[] = R"(
+void f() {
+  int* volatile x = nullptr;
+  int* y = (int*) x;
+}
+)";
+static const char QualRemoveVolatileDoublePtr[] = R"(
+void f() {
+  int* volatile * volatile x = nullptr;
+  int** y = (int**) x;
+}
+)";
+static const char QualRemoveVolatileDiffLevelPtr[] = R"(
+void f() {
+  volatile int*** x = nullptr;
+  int** y = (int**) x;
+}
+)";
+static const char QualRemoveVolatileRef[] = R"(
+void f() {
+  int x = 1;
+  volatile int& y = x;
+  int& z = (int&) y;
+}
+)";
+static const char QualRemoveVolatileArr[] = R"(
+void f() {
+  volatile double a[2] {1, 2};
+  double* ca = (double*) a;
+}
+)";
+static const char QualRemoveVolatilePtrToArr[] = R"(
+void f() {
+  double (* volatile a)[2] {};
+  (double(*)[2]) a;
+}
+)";
+static const char QualRemoveVolatilePtrToArrOfVolatilePtrs[] = R"(
+void f() {
+  double* volatile (* volatile a)[2] {};
+  (double* (*)[2]) a;
+}
+)";
+static const char QualRemoveArrPtrVolatileData[] = R"(
+void f() {
+  volatile double (*a)[2] {};
+  (double(*)[2]) a;
+}
+)";
+static const char QualRemoveDiffLevelArrPtrVolatileData[] = R"(
+void f() {
+  volatile double* (*a)[2] {};
+  (double (*)[2]) a;
+}
+)";
+static const char QualRemoveVolatileMixedPtrTypes[] = R"(
+struct t {};
+void f() {
+  volatile double * volatile t::* volatile (t::* volatile * volatile a) [2] {};
+  (double * t::* (t::* *) [2]) a;
+}
+)";
+static const char QualRemoveVolatileUnknownArrPtr[] = R"(
+void f() {
+  volatile int* volatile (*x) [] {};
+  (int* (*)[]) x;
+}
+)";
+static const char QualRemoveVolatileUnknownArrPtrToKnownArrPtr[] = R"(
+void f() {
+  volatile int* volatile (*x) [] {};
+  (int* (*)[2]) x;
+}
+)";
+
+/// Restricted
+/// add
+static const char QualAddRestrictPtr[] = R"(
+void f() {
+  int* x = nullptr;
+  int* __restrict y = (int* __restrict) x;
+}
+)";
+static const char QualAddRestrictDoublePtr[] = R"(
+void f() {
+  int** x = nullptr;
+  int* __restrict * __restrict y = (int* __restrict * __restrict) x;
+}
+)";
+// Add another layer of pointers to decorate __restrict on
+static const char QualAddRestrictDiffLevelPtr[] = R"(
+void f() {
+  int** x = nullptr;
+  int* __restrict *** y = (int* __restrict ***) x;
+}
+)";
+// Add another layers of pointers to decorate __restrict on
+static const char QualAddRestrictArr[] = R"(
+void f() {
+  double* a[2] {};
+  double* __restrict * ca = (double* __restrict *) a;
+}
+)";
+static const char QualAddRestrictPtrToArr[] = R"(
+void f() {
+  double (*a)[2] {};
+  (double(* __restrict)[2]) a;
+}
+)";
+static const char QualAddRestrictPtrToArrOfRestrictPtrs[] = R"(
+void f() {
+  double* (*a)[2] {};
+  (double* __restrict (* __restrict)[2]) a;
+}
+)";
+// Add another layer of pointers to decorate __restrict on
+static const char QualAddArrPtrRestrictData[] = R"(
+void f() {
+  double* (*a)[2] {};
+  (double* __restrict (*)[2]) a;
+}
+)";
+// Add another layer of pointers to decorate __restrict on
+static const char QualAddDiffLevelArrPtrRestrictData[] = R"(
+void f() {
+  double (*a)[2] {};
+  (double* __restrict *(*)[2]) a;
+}
+)";
+static const char QualAddRestrictMixedPtrTypes[] = R"(
+struct t {};
+void f() {
+  double * t::* (t::* *a) [2];
+  (double * __restrict t::* __restrict (t::* __restrict * __restrict) [2]) a;
+}
+)";
+static const char QualAddRestrictUnknownArrPtr[] = R"(
+void f() {
+  int* (*x) [] {};
+  (int* __restrict (*)[]) x;
+}
+)";
+static const char QualAddRestrictUnknownArrPtrToKnownArrPtr[] = R"(
+void f() {
+  int* (*x) [] {};
+  (int* __restrict (*)[2]) x;
+}
+)";
+
+/// remove
+static const char QualRemoveRestrictPtr[] = R"(
+void f() {
+  int* __restrict x = nullptr;
+  int* y = (int*) x;
+}
+)";
+static const char QualRemoveRestrictDoublePtr[] = R"(
+void f() {
+  int* __restrict * __restrict x = nullptr;
+  int** y = (int**) x;
+}
+)";
+static const char QualRemoveRestrictDiffLevelPtr[] = R"(
+void f() {
+  int* __restrict *** x = nullptr;
+  int** y = (int**) x;
+}
+)";
+static const char QualRemoveRestrictArr[] = R"(
+void f() {
+  double* __restrict a[2] {};
+  double** ca = (double**) a;
+}
+)";
+static const char QualRemoveRestrictPtrToArr[] = R"(
+void f() {
+  double (* __restrict a)[2] {};
+  (double(*)[2]) a;
+}
+)";
+static const char QualRemoveRestrictPtrToArrOfRestrictPtrs[] = R"(
+void f() {
+  double* __restrict (* __restrict a)[2] {};
+  (double* (*)[2]) a;
+}
+)";
+static const char QualRemoveArrPtrRestrictData[] = R"(
+void f() {
+  double* __restrict (*a)[2] {};
+  (double* (*)[2]) a;
+}
+)";
+static const char QualRemoveDiffLevelArrPtrRestrictData[] = R"(
+void f() {
+  double* __restrict *(*a)[2] {};
+  (double (*)[2]) a;
+}
+)";
+static const char QualRemoveSimilarPtrsBeyondArrRestrictData[] = R"(
+void f() {
+  double* __restrict * __restrict (* __restrict a)[2] {};
+  (double* * __restrict (* __restrict)[2]) a;
+}
+)";
+static const char QualRemoveRestrictMixedPtrTypes[] = R"(
+struct t {};
+void f() {
+  double * __restrict t::* __restrict (t::* __restrict * __restrict a) [2] {};
+  (double * t::* (t::* *) [2]) a;
+}
+)";
+static const char QualRemoveRestrictUnknownArrPtr[] = R"(
+void f() {
+  int* __restrict (*x) [] {};
+  (int* (*)[]) x;
+}
+)";
+static const char QualRemoveRestrictUnknownArrPtrToKnownArrPtr[] = R"(
+void f() {
+  int* __restrict (*x) [] {};
+  (int* (*)[2]) x;
+}
+)";
+
+} // namespace constcheck
+
+} // namespace testcases
+
+#endif // LLVM_PROJECT_CLANGQUALIFIERTESTCASES_H