diff --git a/gcc/rust/checks/errors/privacy/rust-privacy-check.cc b/gcc/rust/checks/errors/privacy/rust-privacy-check.cc
new file mode 100644
index 00000000000..9664d62f65c
--- /dev/null
+++ b/gcc/rust/checks/errors/privacy/rust-privacy-check.cc
@@ -0,0 +1,63 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "rust-privacy-check.h"
+#include "rust-reachability.h"
+#include "rust-hir-type-check.h"
+#include "rust-hir-map.h"
+#include "rust-name-resolver.h"
+#include "rust-visibility-resolver.h"
+#include "rust-pub-restricted-visitor.h"
+#include "rust-privacy-reporter.h"
+
+extern bool
+saw_errors (void);
+
+namespace Rust {
+namespace Privacy {
+
+void
+Resolver::resolve (HIR::Crate &crate)
+{
+  PrivacyContext ctx;
+  auto mappings = Analysis::Mappings::get ();
+  auto resolver = Rust::Resolver::Resolver::get ();
+  auto ty_ctx = ::Rust::Resolver::TypeCheckContext::get ();
+
+  VisibilityResolver (*mappings, *resolver).go (crate);
+  PubRestrictedVisitor (*mappings).go (crate);
+  PrivacyReporter (*mappings, *resolver, *ty_ctx).go (crate);
+
+  auto visitor = ReachabilityVisitor (ctx, *ty_ctx);
+
+  const auto &items = crate.items;
+
+  for (auto &item : items)
+    {
+      if (item->get_hir_kind () == HIR::Node::VIS_ITEM)
+	{
+	  auto vis_item = static_cast<HIR::VisItem *> (item.get ());
+	  vis_item->accept_vis (visitor);
+	}
+    }
+
+  if (saw_errors ())
+    return;
+}
+} // namespace Privacy
+} // namespace Rust
diff --git a/gcc/rust/checks/errors/privacy/rust-privacy-check.h b/gcc/rust/checks/errors/privacy/rust-privacy-check.h
new file mode 100644
index 00000000000..290b5eacb6c
--- /dev/null
+++ b/gcc/rust/checks/errors/privacy/rust-privacy-check.h
@@ -0,0 +1,44 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_PRIVACY_CHECK_H
+#define RUST_PRIVACY_CHECK_H
+
+#include "rust-hir.h"
+#include "rust-hir-expr.h"
+#include "rust-hir-stmt.h"
+#include "rust-hir-item.h"
+#include "rust-hir-type-check.h"
+
+namespace Rust {
+namespace Privacy {
+class Resolver
+{
+public:
+  /**
+   * Perform the full privacy resolving pass on a crate.
+   *
+   * This resolver first computes the reachability of all items in a crate,
+   * before checking for privacy violations.
+   */
+  static void resolve (HIR::Crate &crate);
+};
+} // namespace Privacy
+} // namespace Rust
+
+#endif // !RUST_PRIVACY_CHECK_H
diff --git a/gcc/rust/checks/errors/privacy/rust-privacy-common.h b/gcc/rust/checks/errors/privacy/rust-privacy-common.h
new file mode 100644
index 00000000000..ceafe91d886
--- /dev/null
+++ b/gcc/rust/checks/errors/privacy/rust-privacy-common.h
@@ -0,0 +1,67 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "rust-mapping-common.h"
+
+namespace Rust {
+namespace Privacy {
+
+/**
+ * Visibility class related specifically to DefIds. This class allows defining
+ * the visibility of an item with regard to a specific module.
+ *
+ * Items are either public throughout a crate, or restricted to a specific
+ * module. Private items are simply restricted to the current module.
+ */
+class ModuleVisibility
+{
+public:
+  enum Type
+  {
+    Unknown,
+    Public,
+    Restricted,
+  };
+
+  ModuleVisibility () : kind (Unknown), module_id (UNKNOWN_DEFID) {}
+
+  static ModuleVisibility create_restricted (DefId module_id)
+  {
+    return ModuleVisibility (Type::Restricted, module_id);
+  }
+
+  static ModuleVisibility create_public ()
+  {
+    return ModuleVisibility (Type::Public, UNKNOWN_DEFID);
+  }
+
+  Type get_kind () const { return kind; }
+
+  const DefId &get_module_id () const { return module_id; }
+  DefId &get_module_id () { return module_id; }
+
+private:
+  ModuleVisibility (Type kind, DefId module_id)
+    : kind (kind), module_id (module_id)
+  {}
+
+  Type kind;
+  DefId module_id;
+};
+} // namespace Privacy
+} // namespace Rust
diff --git a/gcc/rust/checks/errors/privacy/rust-privacy-ctx.cc b/gcc/rust/checks/errors/privacy/rust-privacy-ctx.cc
new file mode 100644
index 00000000000..9ebc86988e9
--- /dev/null
+++ b/gcc/rust/checks/errors/privacy/rust-privacy-ctx.cc
@@ -0,0 +1,93 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "rust-privacy-ctx.h"
+#include "selftest.h"
+
+namespace Rust {
+namespace Privacy {
+
+static ReachLevel
+insert_if_higher (ReachLevel new_level,
+		  std::unordered_map<DefId, ReachLevel>::iterator &existing)
+{
+  if (new_level > existing->second)
+    existing->second = new_level;
+
+  return existing->second;
+}
+
+ReachLevel
+PrivacyContext::update_reachability (const Analysis::NodeMapping &mapping,
+				     ReachLevel reach)
+{
+  auto def_id = mapping.get_defid ();
+  auto existing_reach = reachability_map.find (def_id);
+  if (existing_reach != reachability_map.end ())
+    return insert_if_higher (reach, existing_reach);
+
+  reachability_map.insert ({def_id, reach});
+  return reach;
+}
+
+const ReachLevel *
+PrivacyContext::lookup_reachability (const Analysis::NodeMapping &mapping)
+{
+  auto existing_reach = reachability_map.find (mapping.get_defid ());
+  if (existing_reach == reachability_map.end ())
+    return nullptr;
+
+  return &existing_reach->second;
+}
+} // namespace Privacy
+} // namespace Rust
+
+#if CHECKING_P
+namespace selftest {
+static void
+update_reachability_test (void)
+{
+  auto ctx = Rust::Privacy::PrivacyContext ();
+  // Bogus values for the mappings
+  auto mapping = Rust::Analysis::NodeMapping (15, 15, 15, 15);
+
+  auto new_level
+    = ctx.update_reachability (mapping, Rust::Privacy::ReachLevel::Unreachable);
+
+  ASSERT_EQ (new_level, Rust::Privacy::ReachLevel::Unreachable);
+
+  ASSERT_TRUE (ctx.lookup_reachability (mapping));
+  ASSERT_EQ (*ctx.lookup_reachability (mapping),
+	     Rust::Privacy::ReachLevel::Unreachable);
+
+  new_level
+    = ctx.update_reachability (mapping, Rust::Privacy::ReachLevel::Reachable);
+
+  ASSERT_EQ (new_level, Rust::Privacy::ReachLevel::Reachable);
+  ASSERT_TRUE (ctx.lookup_reachability (mapping));
+  ASSERT_EQ (*ctx.lookup_reachability (mapping),
+	     Rust::Privacy::ReachLevel::Reachable);
+}
+
+void
+rust_privacy_ctx_test (void)
+{
+  update_reachability_test ();
+}
+} // namespace selftest
+#endif // !CHECKING_P
diff --git a/gcc/rust/checks/errors/privacy/rust-privacy-ctx.h b/gcc/rust/checks/errors/privacy/rust-privacy-ctx.h
new file mode 100644
index 00000000000..52d790edf63
--- /dev/null
+++ b/gcc/rust/checks/errors/privacy/rust-privacy-ctx.h
@@ -0,0 +1,79 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_PRIVACY_CTX_H
+#define RUST_PRIVACY_CTX_H
+
+#include "rust-hir-map.h"
+#include "rust-privacy-check.h"
+
+namespace Rust {
+namespace Privacy {
+
+/**
+ * Reachability levels of HIR nodes. These levels are computed through the
+ * `ReachabilityVisitor` visitor.
+ */
+enum ReachLevel
+{
+  Unreachable,
+  Reachable,
+};
+
+class PrivacyContext
+{
+public:
+  /**
+   * Insert a new resolved visibility for a given node. If the node is already
+   * present in the reachability map, then its visibility will only be updated
+   * if the given visibility is higher.
+   *
+   * @param mappings Mappings of the node to store the reach level for
+   * @param reach Level of reachability for the given node
+   *
+   * @return The new reachability level for this node. If this was the first
+   * time inserting this node, then return `reach`. Otherwise, return `reach` or
+   * the existing reach level if it was higher.
+   */
+  ReachLevel update_reachability (const Analysis::NodeMapping &mapping,
+				  ReachLevel reach);
+
+  /**
+   * Lookup the visibility of an already declared Node
+   *
+   * @param mapping Mappings of the node to fetch the reach level of
+   *
+   * @return `nullptr` if the reach level for the current node has not been
+   * added, a valid pointer otherwise
+   */
+  const ReachLevel *lookup_reachability (const Analysis::NodeMapping &mapping);
+
+private:
+  std::unordered_map<DefId, ReachLevel> reachability_map;
+};
+} // namespace Privacy
+} // namespace Rust
+
+#if CHECKING_P
+namespace selftest {
+void
+rust_privacy_ctx_test (void);
+}
+#endif // !CHECKING_P
+
+#endif // !RUST_PRIVACY_CTX_H
diff --git a/gcc/rust/checks/errors/privacy/rust-privacy-reporter.cc b/gcc/rust/checks/errors/privacy/rust-privacy-reporter.cc
new file mode 100644
index 00000000000..35fde40782e
--- /dev/null
+++ b/gcc/rust/checks/errors/privacy/rust-privacy-reporter.cc
@@ -0,0 +1,753 @@
+#include "rust-privacy-reporter.h"
+#include "rust-hir-expr.h"
+#include "rust-hir-stmt.h"
+#include "rust-hir-item.h"
+
+namespace Rust {
+namespace Privacy {
+
+PrivacyReporter::PrivacyReporter (
+  Analysis::Mappings &mappings, Resolver::Resolver &resolver,
+  const Rust::Resolver::TypeCheckContext &ty_ctx)
+  : mappings (mappings), resolver (resolver), ty_ctx (ty_ctx),
+    current_module (Optional<NodeId>::none ())
+{}
+
+void
+PrivacyReporter::go (HIR::Crate &crate)
+{
+  for (auto &item : crate.items)
+    item->accept_vis (*this);
+}
+
+static bool
+is_child_module (Analysis::Mappings &mappings, NodeId parent,
+		 NodeId possible_child)
+{
+  auto children = mappings.lookup_module_children (parent);
+
+  if (!children)
+    return false;
+
+  // Visit all toplevel children
+  for (auto &child : *children)
+    if (child == possible_child)
+      return true;
+
+  // Now descend recursively in the child module tree
+  for (auto &child : *children)
+    if (is_child_module (mappings, child, possible_child))
+      return true;
+
+  return false;
+}
+
+// FIXME: This function needs a lot of refactoring
+void
+PrivacyReporter::check_for_privacy_violation (const NodeId &use_id,
+					      const Location &locus)
+{
+  NodeId ref_node_id = UNKNOWN_NODEID;
+
+  // FIXME: Don't assert here - we might be dealing with a type
+  if (!resolver.lookup_resolved_name (use_id, &ref_node_id))
+    resolver.lookup_resolved_type (use_id, &ref_node_id);
+
+  // FIXME: Assert here. For now, we return since this causes issues when
+  // checking inferred types (#1260)
+  // rust_assert (ref_node_id != UNKNOWN_NODEID);
+  if (ref_node_id == UNKNOWN_NODEID)
+    return;
+
+  ModuleVisibility vis;
+
+  // FIXME: Can we really return here if the item has no visibility?
+  if (!mappings.lookup_visibility (ref_node_id, vis))
+    return;
+
+  auto valid = true;
+
+  switch (vis.get_kind ())
+    {
+    case ModuleVisibility::Public:
+      break;
+      case ModuleVisibility::Restricted: {
+	// If we are in the crate, everything is restricted correctly, but we
+	// can't get a module for it
+	if (current_module.is_none ())
+	  return;
+
+	auto module = mappings.lookup_defid (vis.get_module_id ());
+	rust_assert (module != nullptr);
+
+	auto mod_node_id = module->get_mappings ().get_nodeid ();
+
+	// We are in the module referenced by the pub(restricted) visibility.
+	// This is valid
+	if (mod_node_id == current_module.get ())
+	  break;
+
+	// FIXME: This needs a LOT of TLC: hinting about the definition, a
+	// string to say if it's a module, function, type, etc...
+	if (!is_child_module (mappings, mod_node_id, current_module.get ()))
+	  valid = false;
+      }
+      break;
+    case ModuleVisibility::Unknown:
+      rust_unreachable ();
+      break;
+    }
+
+  if (!valid)
+    rust_error_at (locus, "definition is private in this context");
+}
+
+void
+PrivacyReporter::check_base_type_privacy (Analysis::NodeMapping &node_mappings,
+					  const TyTy::BaseType *ty,
+					  const Location &locus)
+{
+  // Avoids repeating commong argument such as `use_id` or `locus` since we're
+  // doing a lot of recursive calls here
+  auto recursive_check
+    = [this, &node_mappings, &locus] (const TyTy::BaseType *ty) {
+	return check_base_type_privacy (node_mappings, ty, locus);
+      };
+
+  switch (ty->get_kind ())
+    {
+      // These "simple" types are our stop condition
+    case TyTy::BOOL:
+    case TyTy::CHAR:
+    case TyTy::INT:
+    case TyTy::UINT:
+    case TyTy::FLOAT:
+    case TyTy::USIZE:
+    case TyTy::ISIZE:
+    case TyTy::ADT:
+      case TyTy::STR: {
+	auto ref_id = ty->get_ref ();
+	NodeId lookup_id;
+
+	bool ok = mappings.lookup_hir_to_node (ref_id, &lookup_id);
+	rust_assert (ok);
+
+	return check_for_privacy_violation (lookup_id, locus);
+      }
+    case TyTy::REF:
+      return recursive_check (
+	static_cast<const TyTy::ReferenceType *> (ty)->get_base ());
+    case TyTy::POINTER:
+      return recursive_check (
+	static_cast<const TyTy::PointerType *> (ty)->get_base ());
+    case TyTy::ARRAY:
+      return recursive_check (
+	static_cast<const TyTy::ArrayType *> (ty)->get_element_type ());
+    case TyTy::SLICE:
+      return recursive_check (
+	static_cast<const TyTy::SliceType *> (ty)->get_element_type ());
+    case TyTy::FNPTR:
+      for (auto &param : static_cast<const TyTy::FnPtr *> (ty)->get_params ())
+	recursive_check (param.get_tyty ());
+      return recursive_check (
+	static_cast<const TyTy::FnPtr *> (ty)->get_return_type ());
+    case TyTy::TUPLE:
+      for (auto &param :
+	   static_cast<const TyTy::TupleType *> (ty)->get_fields ())
+	recursive_check (param.get_tyty ());
+      return;
+    case TyTy::PLACEHOLDER:
+      return recursive_check (
+	// FIXME: Can we use `resolve` here? Is that what we should do?
+	static_cast<const TyTy::PlaceholderType *> (ty)->resolve ());
+    case TyTy::PROJECTION:
+      return recursive_check (
+	static_cast<const TyTy::ProjectionType *> (ty)->get ());
+    case TyTy::CLOSURE:
+      rust_sorry_at (locus, "privacy pass for closures is not handled yet");
+      break;
+
+      // If we're dealing with a generic param, there's nothing we should be
+      // doing here
+    case TyTy::PARAM:
+      // We are dealing with a function definition that has been assigned
+      // somewhere else. Nothing to resolve privacy-wise other than the actual
+      // function, which is resolved as an expression
+    case TyTy::FNDEF:
+      // FIXME: Can we really not resolve Dynamic types here? Shouldn't we have
+      // a look at the path and perform proper privacy analysis?
+    case TyTy::DYNAMIC:
+      // The never type is builtin and always available
+    case TyTy::NEVER:
+      // We shouldn't have inference types here, ever
+    case TyTy::INFER:
+      return;
+    case TyTy::ERROR:
+      rust_unreachable ();
+    }
+}
+
+void
+PrivacyReporter::check_type_privacy (const HIR::Type *type)
+{
+  rust_assert (type);
+
+  TyTy::BaseType *lookup = nullptr;
+  rust_assert (
+    ty_ctx.lookup_type (type->get_mappings ().get_hirid (), &lookup));
+
+  auto node_mappings = type->get_mappings ();
+  return check_base_type_privacy (node_mappings, lookup, type->get_locus ());
+}
+
+void
+PrivacyReporter::visit (HIR::PathInExpression &path)
+{
+  check_for_privacy_violation (path.get_mappings ().get_nodeid (),
+			       path.get_locus ());
+}
+
+void
+PrivacyReporter::visit (HIR::TypePathSegmentFunction &segment)
+{
+  // FIXME: Do we need to do anything for this?
+}
+
+void
+PrivacyReporter::visit (HIR::TypePath &path)
+{
+  check_for_privacy_violation (path.get_mappings ().get_nodeid (),
+			       path.get_locus ());
+}
+
+void
+PrivacyReporter::visit (HIR::QualifiedPathInExpression &path)
+{
+  check_for_privacy_violation (path.get_mappings ().get_nodeid (),
+			       path.get_locus ());
+}
+
+void
+PrivacyReporter::visit (HIR::QualifiedPathInType &path)
+{
+  check_for_privacy_violation (path.get_mappings ().get_nodeid (),
+			       path.get_locus ());
+}
+
+void
+PrivacyReporter::visit (HIR::LiteralExpr &expr)
+{
+  // Literals cannot contain any sort of privacy violation
+}
+
+void
+PrivacyReporter::visit (HIR::BorrowExpr &expr)
+{
+  expr.get_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::DereferenceExpr &expr)
+{
+  expr.get_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::ErrorPropagationExpr &expr)
+{
+  expr.get_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::NegationExpr &expr)
+{
+  expr.get_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::ArithmeticOrLogicalExpr &expr)
+{
+  expr.get_lhs ()->accept_vis (*this);
+  expr.get_rhs ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::ComparisonExpr &expr)
+{
+  expr.get_lhs ()->accept_vis (*this);
+  expr.get_rhs ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::LazyBooleanExpr &expr)
+{
+  expr.get_lhs ()->accept_vis (*this);
+  expr.get_rhs ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::TypeCastExpr &expr)
+{
+  expr.get_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::AssignmentExpr &expr)
+{
+  expr.get_lhs ()->accept_vis (*this);
+  expr.get_rhs ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::CompoundAssignmentExpr &expr)
+{
+  expr.get_left_expr ()->accept_vis (*this);
+  expr.get_right_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::GroupedExpr &expr)
+{
+  expr.get_expr_in_parens ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::ArrayExpr &expr)
+{
+  HIR::ArrayElems &elements = *expr.get_internal_elements ();
+  switch (elements.get_array_expr_type ())
+    {
+      case HIR::ArrayElems::ArrayExprType::VALUES: {
+	HIR::ArrayElemsValues &elems
+	  = static_cast<HIR::ArrayElemsValues &> (elements);
+	for (auto &value : elems.get_values ())
+	  value->accept_vis (*this);
+      }
+      return;
+
+    case HIR::ArrayElems::ArrayExprType::COPIED:
+      HIR::ArrayElemsCopied &elems
+	= static_cast<HIR::ArrayElemsCopied &> (elements);
+      elems.get_elem_to_copy ()->accept_vis (*this);
+    }
+}
+
+void
+PrivacyReporter::visit (HIR::ArrayIndexExpr &expr)
+{
+  expr.get_array_expr ()->accept_vis (*this);
+  expr.get_index_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::TupleExpr &expr)
+{
+  for (auto &value : expr.get_tuple_elems ())
+    value->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::TupleIndexExpr &expr)
+{
+  expr.get_tuple_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::StructExprStruct &expr)
+{
+  // FIXME: We need to check the visibility of the type it refers to here
+}
+
+void
+PrivacyReporter::visit (HIR::StructExprFieldIdentifier &field)
+{}
+
+void
+PrivacyReporter::visit (HIR::StructExprFieldIdentifierValue &field)
+{
+  field.get_value ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::StructExprFieldIndexValue &field)
+{
+  field.get_value ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::StructExprStructFields &expr)
+{
+  for (auto &field : expr.get_fields ())
+    field->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::CallExpr &expr)
+{
+  expr.get_fnexpr ()->accept_vis (*this);
+
+  for (auto &param : expr.get_arguments ())
+    param->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::MethodCallExpr &expr)
+{
+  expr.get_receiver ()->accept_vis (*this);
+
+  for (auto &param : expr.get_arguments ())
+    param->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::FieldAccessExpr &expr)
+{
+  expr.get_receiver_expr ()->accept_vis (*this);
+
+  // FIXME: We should also check if the field is public?
+}
+
+void
+PrivacyReporter::visit (HIR::ClosureExprInner &expr)
+{
+  // Not handled yet
+}
+
+void
+PrivacyReporter::visit (HIR::BlockExpr &expr)
+{
+  for (auto &stmt : expr.get_statements ())
+    stmt->accept_vis (*this);
+
+  auto &last_expr = expr.get_final_expr ();
+  if (last_expr)
+    last_expr->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::ClosureExprInnerTyped &expr)
+{
+  // Not handled yet
+}
+
+void
+PrivacyReporter::visit (HIR::ContinueExpr &expr)
+{}
+
+void
+PrivacyReporter::visit (HIR::BreakExpr &expr)
+{
+  auto &break_expr = expr.get_expr ();
+  if (break_expr)
+    break_expr->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::RangeFromToExpr &expr)
+{
+  expr.get_from_expr ()->accept_vis (*this);
+  expr.get_to_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::RangeFromExpr &expr)
+{
+  expr.get_from_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::RangeToExpr &expr)
+{
+  expr.get_to_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::RangeFullExpr &expr)
+{}
+
+void
+PrivacyReporter::visit (HIR::RangeFromToInclExpr &expr)
+{
+  expr.get_from_expr ()->accept_vis (*this);
+  expr.get_to_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::RangeToInclExpr &expr)
+{
+  // Not handled yet
+}
+
+void
+PrivacyReporter::visit (HIR::ReturnExpr &expr)
+{
+  auto return_expr = expr.get_expr ();
+  if (return_expr)
+    return_expr->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::UnsafeBlockExpr &expr)
+{
+  expr.get_block_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::LoopExpr &expr)
+{
+  expr.get_loop_block ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::WhileLoopExpr &expr)
+{
+  expr.get_predicate_expr ()->accept_vis (*this);
+  expr.get_loop_block ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::WhileLetLoopExpr &expr)
+{
+  expr.get_cond ()->accept_vis (*this);
+  expr.get_loop_block ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::ForLoopExpr &expr)
+{
+  expr.get_iterator_expr ()->accept_vis (*this);
+  expr.get_loop_block ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::IfExpr &expr)
+{
+  expr.get_if_condition ()->accept_vis (*this);
+  expr.get_if_block ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::IfExprConseqElse &expr)
+{
+  expr.get_if_condition ()->accept_vis (*this);
+  expr.get_if_block ()->accept_vis (*this);
+  expr.get_else_block ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::IfExprConseqIf &expr)
+{
+  expr.get_if_condition ()->accept_vis (*this);
+  expr.get_if_block ()->accept_vis (*this);
+  expr.get_conseq_if_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::IfExprConseqIfLet &expr)
+{
+  expr.get_if_condition ()->accept_vis (*this);
+  expr.get_if_block ()->accept_vis (*this);
+
+  // TODO: We need to visit the if_let_expr as well
+}
+
+void
+PrivacyReporter::visit (HIR::IfLetExpr &expr)
+{
+  // TODO: We need to visit the if_let_expr
+  // TODO: We need to visit the block as well
+}
+
+void
+PrivacyReporter::visit (HIR::IfLetExprConseqElse &expr)
+{
+  // TODO: We need to visit the if_let_expr
+  // TODO: We need to visit the if_block as well
+  // TODO: We need to visit the else_block as well
+}
+
+void
+PrivacyReporter::visit (HIR::IfLetExprConseqIf &expr)
+{
+  // TODO: We need to visit the if_let_expr
+  // TODO: We need to visit the if_block as well
+  // TODO: We need to visit the else_block as well
+}
+
+void
+PrivacyReporter::visit (HIR::IfLetExprConseqIfLet &expr)
+{
+  // TODO: We need to visit the if_let_expr
+  // TODO: We need to visit the if_block as well
+  // TODO: We need to visit the else_block as well
+}
+
+void
+PrivacyReporter::visit (HIR::MatchExpr &expr)
+{
+  expr.get_scrutinee_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::AwaitExpr &expr)
+{
+  // Not handled yet
+}
+
+void
+PrivacyReporter::visit (HIR::AsyncBlockExpr &expr)
+{
+  // Not handled yet
+}
+
+void
+PrivacyReporter::visit (HIR::Module &module)
+{
+  // FIXME: We also need to think about module privacy
+
+  auto old_module = current_module;
+  current_module
+    = Optional<NodeId>::some (module.get_mappings ().get_nodeid ());
+
+  for (auto &item : module.get_items ())
+    item->accept_vis (*this);
+
+  current_module = old_module;
+}
+
+void
+PrivacyReporter::visit (HIR::ExternCrate &crate)
+{}
+
+void
+PrivacyReporter::visit (HIR::UseDeclaration &use_decl)
+{
+  // FIXME: Is there anything we need to do here?
+}
+
+void
+PrivacyReporter::visit (HIR::Function &function)
+{
+  for (auto &param : function.get_function_params ())
+    check_type_privacy (param.get_type ());
+
+  function.get_definition ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::TypeAlias &type_alias)
+{
+  // TODO: Check the type here
+}
+
+void
+PrivacyReporter::visit (HIR::StructStruct &struct_item)
+{
+  // TODO: Check the type of all fields
+}
+
+void
+PrivacyReporter::visit (HIR::TupleStruct &tuple_struct)
+{
+  // TODO: Check the type of all fields
+}
+
+void
+PrivacyReporter::visit (HIR::EnumItem &item)
+{
+  // TODO: Check the type of all variants
+}
+
+void
+PrivacyReporter::visit (HIR::EnumItemTuple &item)
+{
+  // TODO: Check the type
+}
+
+void
+PrivacyReporter::visit (HIR::EnumItemStruct &item)
+{
+  // TODO: Check the type
+}
+
+void
+PrivacyReporter::visit (HIR::EnumItemDiscriminant &item)
+{}
+
+void
+PrivacyReporter::visit (HIR::Enum &enum_item)
+{}
+
+void
+PrivacyReporter::visit (HIR::Union &union_item)
+{
+  // TODO: Check the type
+}
+
+void
+PrivacyReporter::visit (HIR::ConstantItem &const_item)
+{
+  // TODO: We need to visit the type
+  const_item.get_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::StaticItem &static_item)
+{
+  // TODO: We need to visit the type
+  static_item.get_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::Trait &trait)
+{
+  // FIXME: We need to be an ItemVisitor as well
+  // for (auto &item : trait.get_trait_items ())
+  //   item->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::ImplBlock &impl)
+{
+  for (auto &item : impl.get_impl_items ())
+    item->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::ExternBlock &block)
+{
+  // FIXME: We need to be an ItemVisitor as well
+  // for (auto &item : block.get_extern_items ())
+  //   item->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::EmptyStmt &stmt)
+{}
+
+void
+PrivacyReporter::visit (HIR::LetStmt &stmt)
+{
+  auto type = stmt.get_type ();
+  if (type)
+    check_type_privacy (type);
+
+  auto init_expr = stmt.get_init_expr ();
+  if (init_expr)
+    init_expr->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::ExprStmtWithoutBlock &stmt)
+{
+  stmt.get_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::ExprStmtWithBlock &stmt)
+{
+  stmt.get_expr ()->accept_vis (*this);
+}
+
+} // namespace Privacy
+} // namespace Rust
diff --git a/gcc/rust/checks/errors/privacy/rust-privacy-reporter.h b/gcc/rust/checks/errors/privacy/rust-privacy-reporter.h
new file mode 100644
index 00000000000..546b108f36d
--- /dev/null
+++ b/gcc/rust/checks/errors/privacy/rust-privacy-reporter.h
@@ -0,0 +1,173 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_PRIVACY_REPORTER_H
+#define RUST_PRIVACY_REPORTER_H
+
+#include "rust-hir-map.h"
+#include "rust-hir-visitor.h"
+#include "rust-mapping-common.h"
+#include "rust-name-resolver.h"
+
+namespace Rust {
+namespace Privacy {
+
+/**
+ * This visitor visits all items and expressions of a crate and reports privacy
+ * violations. It should be started after using the `VisibilityResolver` visitor
+ * which resolves the visibilities of all items of a crate.
+ */
+class PrivacyReporter : public HIR::HIRExpressionVisitor,
+			public HIR::HIRStmtVisitor
+{
+public:
+  PrivacyReporter (Analysis::Mappings &mappings,
+		   Rust::Resolver::Resolver &resolver,
+		   const Rust::Resolver::TypeCheckContext &ty_ctx);
+
+  /**
+   * Perform privacy error reporting on an entire crate
+   */
+  void go (HIR::Crate &crate);
+
+private:
+  /**
+   * Check if a given item's visibility is accessible from the current module.
+   *
+   * This function reports the errors it finds.
+   *
+   * @param use_id NodeId of the expression/statement referencing an item with
+   * 		a visibility
+   * @param locus Location of said expression/statement
+   */
+  void check_for_privacy_violation (const NodeId &use_id,
+				    const Location &locus);
+
+  /**
+   * Internal function used by `check_type_privacy` when dealing with complex
+types
+   * such as references or arrays
+   */
+  void check_base_type_privacy (Analysis::NodeMapping &node_mappings,
+				const TyTy::BaseType *ty,
+				const Location &locus);
+
+  /**
+   * Check the privacy of an explicit type.
+   *
+   * This function reports the errors it finds.
+   *
+   * @param type Reference to an explicit type used in a statement, expression
+   * 		or parameter
+   */
+  void check_type_privacy (const HIR::Type *type);
+
+  virtual void visit (HIR::StructExprFieldIdentifier &field);
+  virtual void visit (HIR::StructExprFieldIdentifierValue &field);
+  virtual void visit (HIR::StructExprFieldIndexValue &field);
+
+  virtual void visit (HIR::QualifiedPathInExpression &expr);
+  virtual void visit (HIR::PathInExpression &expr);
+  virtual void visit (HIR::ClosureExprInnerTyped &);
+  virtual void visit (HIR::ClosureExprInner &expr);
+  virtual void visit (HIR::StructExprStructFields &);
+  virtual void visit (HIR::StructExprStruct &);
+  virtual void visit (HIR::LiteralExpr &expr);
+  virtual void visit (HIR::BorrowExpr &expr);
+  virtual void visit (HIR::DereferenceExpr &expr);
+  virtual void visit (HIR::ErrorPropagationExpr &expr);
+  virtual void visit (HIR::NegationExpr &expr);
+  virtual void visit (HIR::ArithmeticOrLogicalExpr &expr);
+  virtual void visit (HIR::ComparisonExpr &expr);
+  virtual void visit (HIR::LazyBooleanExpr &expr);
+  virtual void visit (HIR::TypeCastExpr &expr);
+  virtual void visit (HIR::AssignmentExpr &expr);
+  virtual void visit (HIR::CompoundAssignmentExpr &expr);
+  virtual void visit (HIR::GroupedExpr &expr);
+  virtual void visit (HIR::ArrayExpr &expr);
+  virtual void visit (HIR::ArrayIndexExpr &expr);
+  virtual void visit (HIR::TupleExpr &expr);
+  virtual void visit (HIR::TupleIndexExpr &expr);
+  virtual void visit (HIR::CallExpr &expr);
+  virtual void visit (HIR::MethodCallExpr &expr);
+  virtual void visit (HIR::FieldAccessExpr &expr);
+  virtual void visit (HIR::BlockExpr &expr);
+  virtual void visit (HIR::ContinueExpr &expr);
+  virtual void visit (HIR::BreakExpr &expr);
+  virtual void visit (HIR::RangeFromToExpr &expr);
+  virtual void visit (HIR::RangeFromExpr &expr);
+  virtual void visit (HIR::RangeToExpr &expr);
+  virtual void visit (HIR::RangeFullExpr &expr);
+  virtual void visit (HIR::RangeFromToInclExpr &expr);
+  virtual void visit (HIR::RangeToInclExpr &expr);
+  virtual void visit (HIR::ReturnExpr &expr);
+  virtual void visit (HIR::UnsafeBlockExpr &expr);
+  virtual void visit (HIR::LoopExpr &expr);
+  virtual void visit (HIR::WhileLoopExpr &expr);
+  virtual void visit (HIR::WhileLetLoopExpr &expr);
+  virtual void visit (HIR::ForLoopExpr &expr);
+  virtual void visit (HIR::IfExpr &expr);
+  virtual void visit (HIR::IfExprConseqElse &expr);
+  virtual void visit (HIR::IfExprConseqIf &expr);
+  virtual void visit (HIR::IfExprConseqIfLet &expr);
+  virtual void visit (HIR::IfLetExpr &expr);
+  virtual void visit (HIR::IfLetExprConseqElse &expr);
+  virtual void visit (HIR::IfLetExprConseqIf &expr);
+  virtual void visit (HIR::IfLetExprConseqIfLet &expr);
+  virtual void visit (HIR::MatchExpr &expr);
+  virtual void visit (HIR::AwaitExpr &expr);
+  virtual void visit (HIR::AsyncBlockExpr &expr);
+
+  virtual void visit (HIR::EnumItemTuple &);
+  virtual void visit (HIR::EnumItemStruct &);
+  virtual void visit (HIR::EnumItem &item);
+  virtual void visit (HIR::TupleStruct &tuple_struct);
+  virtual void visit (HIR::EnumItemDiscriminant &);
+  virtual void visit (HIR::TypePathSegmentFunction &segment);
+  virtual void visit (HIR::TypePath &path);
+  virtual void visit (HIR::QualifiedPathInType &path);
+  virtual void visit (HIR::Module &module);
+  virtual void visit (HIR::ExternCrate &crate);
+  virtual void visit (HIR::UseDeclaration &use_decl);
+  virtual void visit (HIR::Function &function);
+  virtual void visit (HIR::TypeAlias &type_alias);
+  virtual void visit (HIR::StructStruct &struct_item);
+  virtual void visit (HIR::Enum &enum_item);
+  virtual void visit (HIR::Union &union_item);
+  virtual void visit (HIR::ConstantItem &const_item);
+  virtual void visit (HIR::StaticItem &static_item);
+  virtual void visit (HIR::Trait &trait);
+  virtual void visit (HIR::ImplBlock &impl);
+  virtual void visit (HIR::ExternBlock &block);
+  virtual void visit (HIR::EmptyStmt &stmt);
+  virtual void visit (HIR::LetStmt &stmt);
+  virtual void visit (HIR::ExprStmtWithoutBlock &stmt);
+  virtual void visit (HIR::ExprStmtWithBlock &stmt);
+
+  Analysis::Mappings &mappings;
+  Rust::Resolver::Resolver &resolver;
+  const Rust::Resolver::TypeCheckContext &ty_ctx;
+
+  // `None` means we're in the root module - the crate
+  Optional<NodeId> current_module;
+};
+
+} // namespace Privacy
+} // namespace Rust
+
+#endif // !RUST_PRIVACY_REPORTER_H
diff --git a/gcc/rust/checks/errors/privacy/rust-pub-restricted-visitor.cc b/gcc/rust/checks/errors/privacy/rust-pub-restricted-visitor.cc
new file mode 100644
index 00000000000..e391653ea26
--- /dev/null
+++ b/gcc/rust/checks/errors/privacy/rust-pub-restricted-visitor.cc
@@ -0,0 +1,182 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "rust-pub-restricted-visitor.h"
+#include "rust-hir.h"
+#include "rust-hir-item.h"
+
+namespace Rust {
+namespace Privacy {
+
+bool
+PubRestrictedVisitor::is_restriction_valid (NodeId item_id,
+					    const Location &locus)
+{
+  ModuleVisibility visibility;
+
+  // If there is no visibility in the mappings, then the item is private and
+  // does not contain any restriction
+  // FIXME: Is that correct?
+  if (!mappings.lookup_visibility (item_id, visibility))
+    return true;
+
+  for (auto mod = module_stack.rbegin (); mod != module_stack.rend (); mod++)
+    if (*mod == visibility.get_module_id ())
+      return true;
+
+  rust_error_at (locus, "restricted path is not an ancestor of the "
+			"current module");
+  return false;
+}
+
+PubRestrictedVisitor::PubRestrictedVisitor (Analysis::Mappings &mappings)
+  : mappings (mappings)
+{}
+
+void
+PubRestrictedVisitor::go (HIR::Crate &crate)
+{
+  // The `crate` module will always be present
+  module_stack.emplace_back (crate.get_mappings ().get_defid ());
+
+  // FIXME: When do we insert `super`? `self`?
+  // We need wrapper function for these
+
+  for (auto &item : crate.items)
+    {
+      if (item->get_hir_kind () == HIR::Node::VIS_ITEM)
+	{
+	  auto vis_item = static_cast<HIR::VisItem *> (item.get ());
+	  vis_item->accept_vis (*this);
+	}
+    }
+}
+
+void
+PubRestrictedVisitor::visit (HIR::Module &mod)
+{
+  // FIXME: We need to update `super` and `self` here
+  module_stack.push_back (mod.get_mappings ().get_defid ());
+
+  is_restriction_valid (mod.get_mappings ().get_nodeid (), mod.get_locus ());
+
+  for (auto &item : mod.get_items ())
+    {
+      if (item->get_hir_kind () == HIR::Node::VIS_ITEM)
+	{
+	  auto vis_item = static_cast<HIR::VisItem *> (item.get ());
+	  vis_item->accept_vis (*this);
+	}
+    }
+
+  module_stack.pop_back ();
+}
+
+void
+PubRestrictedVisitor::visit (HIR::ExternCrate &crate)
+{
+  is_restriction_valid (crate.get_mappings ().get_nodeid (),
+			crate.get_locus ());
+}
+
+void
+PubRestrictedVisitor::visit (HIR::UseDeclaration &use_decl)
+{
+  is_restriction_valid (use_decl.get_mappings ().get_nodeid (),
+			use_decl.get_locus ());
+}
+
+void
+PubRestrictedVisitor::visit (HIR::Function &func)
+{
+  is_restriction_valid (func.get_mappings ().get_nodeid (), func.get_locus ());
+}
+
+void
+PubRestrictedVisitor::visit (HIR::TypeAlias &type_alias)
+{
+  is_restriction_valid (type_alias.get_mappings ().get_nodeid (),
+			type_alias.get_locus ());
+}
+
+void
+PubRestrictedVisitor::visit (HIR::StructStruct &struct_item)
+{
+  is_restriction_valid (struct_item.get_mappings ().get_nodeid (),
+			struct_item.get_locus ());
+  // FIXME: Check fields here as well
+}
+
+void
+PubRestrictedVisitor::visit (HIR::TupleStruct &tuple_struct)
+{
+  is_restriction_valid (tuple_struct.get_mappings ().get_nodeid (),
+			tuple_struct.get_locus ());
+  // FIXME: Check fields here as well
+}
+
+void
+PubRestrictedVisitor::visit (HIR::Enum &enum_item)
+{
+  is_restriction_valid (enum_item.get_mappings ().get_nodeid (),
+			enum_item.get_locus ());
+}
+
+void
+PubRestrictedVisitor::visit (HIR::Union &union_item)
+{
+  is_restriction_valid (union_item.get_mappings ().get_nodeid (),
+			union_item.get_locus ());
+}
+
+void
+PubRestrictedVisitor::visit (HIR::ConstantItem &const_item)
+{
+  is_restriction_valid (const_item.get_mappings ().get_nodeid (),
+			const_item.get_locus ());
+}
+
+void
+PubRestrictedVisitor::visit (HIR::StaticItem &static_item)
+{
+  is_restriction_valid (static_item.get_mappings ().get_nodeid (),
+			static_item.get_locus ());
+}
+
+void
+PubRestrictedVisitor::visit (HIR::Trait &trait)
+{
+  is_restriction_valid (trait.get_mappings ().get_nodeid (),
+			trait.get_locus ());
+}
+
+void
+PubRestrictedVisitor::visit (HIR::ImplBlock &impl)
+{
+  is_restriction_valid (impl.get_mappings ().get_nodeid (), impl.get_locus ());
+}
+
+void
+PubRestrictedVisitor::visit (HIR::ExternBlock &block)
+{
+  is_restriction_valid (block.get_mappings ().get_nodeid (),
+			block.get_locus ());
+}
+
+} // namespace Privacy
+} // namespace Rust
diff --git a/gcc/rust/checks/errors/privacy/rust-pub-restricted-visitor.h b/gcc/rust/checks/errors/privacy/rust-pub-restricted-visitor.h
new file mode 100644
index 00000000000..2685f3d1488
--- /dev/null
+++ b/gcc/rust/checks/errors/privacy/rust-pub-restricted-visitor.h
@@ -0,0 +1,120 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_PUB_RESTRICTED_VISITOR_H
+#define RUST_PUB_RESTRICTED_VISITOR_H
+
+#include "rust-hir-visitor.h"
+#include "rust-hir.h"
+#include "rust-hir-expr.h"
+#include "rust-hir-stmt.h"
+#include "rust-hir-item.h"
+#include "rust-hir-map.h"
+
+namespace Rust {
+namespace Privacy {
+
+/**
+ * This visitor takes care of reporting `pub(restricted)` violations:
+ * A `pub(restricted)` violation is defined as the usage of a path restriction
+ * on an item which does not restrict the item's visibility to one of its parent
+ * modules. What this means is that an user is allowed to specify that an item
+ * should be public for any of its parent modules, going all the way to the
+ * `crate` module, but not for any of its children module.
+ *
+ * ```rust
+ * mod a {
+ * 	mod b {
+ * 		pub (in a) struct A0;
+ *
+ * 		mod c {
+ * 			mod d {
+ * 				pub (in a) struct A1;
+ * 			}
+ * 		}
+ *
+ * 		pub (in c::d) struct A2;
+ * 	}
+ * }
+ * ```
+ *
+ * The above `A0`'s visibility is valid: It is restricted to a path, `a`,
+ * which is a parent of the current module, `b`.
+ * Likewise, `A1` is also defined properly: `a` is a parent of `d`, albeit
+ * a great-great-great-grandparant of it.
+ *
+ * `A2` visibility, however, is invalid: Where the struct is defined, the
+ * current module is `b`. `c::d` (which refers to the `d` module) is a child of
+ * `b`, and not one of its ancestors.
+ *
+ * Note that these resolution rules are also the ones of the 2015 rust edition:
+ * All the `pub(restricted)` visibilities above would be invalid in the 2018
+ * edition, as the paths there must be absolute and not relative (`c::d` would
+ * become `crate::a::b::c::d` etc). Nonetheless, the logic stays the same.
+ */
+class PubRestrictedVisitor : public HIR::HIRVisItemVisitor
+{
+public:
+  PubRestrictedVisitor (Analysis::Mappings &mappings);
+
+  void go (HIR::Crate &crate);
+
+  /**
+   * Check if an item's restricted visibility (`pub (crate)`, `pub (self)`,
+   * `pub(super)`, `pub (in <path>)`) is valid in the current context.
+   * `pub restricted` visibilities are only allowed when the restriction path
+   * is a parent module of the item being visited.
+   *
+   * In case of error, this function will emit the errors and return.
+   *
+   * @param item_id NodeId of the item to check the restriction of
+   * @param locus Location of the item being checked
+   *
+   * @return true if the visibility restriction is valid, false otherwise.
+   */
+  bool is_restriction_valid (NodeId item_id, const Location &locus);
+
+  virtual void visit (HIR::Module &mod);
+  virtual void visit (HIR::ExternCrate &crate);
+  virtual void visit (HIR::UseDeclaration &use_decl);
+  virtual void visit (HIR::Function &func);
+  virtual void visit (HIR::TypeAlias &type_alias);
+  virtual void visit (HIR::StructStruct &struct_item);
+  virtual void visit (HIR::TupleStruct &tuple_struct);
+  virtual void visit (HIR::Enum &enum_item);
+  virtual void visit (HIR::Union &union_item);
+  virtual void visit (HIR::ConstantItem &const_item);
+  virtual void visit (HIR::StaticItem &static_item);
+  virtual void visit (HIR::Trait &trait);
+  virtual void visit (HIR::ImplBlock &impl);
+  virtual void visit (HIR::ExternBlock &block);
+
+private:
+  /* Stack of ancestor modules visited by this visitor */
+  std::vector<DefId> module_stack;
+
+  // FIXME: Do we have to handle `self` and `super` as part of the name
+  // resolution pass?
+
+  Analysis::Mappings &mappings;
+};
+
+} // namespace Privacy
+} // namespace Rust
+
+#endif // !RUST_PUB_RESTRICTED_VISITOR_H
diff --git a/gcc/rust/checks/errors/privacy/rust-reachability.cc b/gcc/rust/checks/errors/privacy/rust-reachability.cc
new file mode 100644
index 00000000000..b322e29bfc3
--- /dev/null
+++ b/gcc/rust/checks/errors/privacy/rust-reachability.cc
@@ -0,0 +1,236 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "rust-reachability.h"
+#include "rust-tyty.h"
+
+namespace Rust {
+namespace Privacy {
+
+static HIR::VisItem *
+maybe_get_vis_item (std::unique_ptr<HIR::Item> &item)
+{
+  if (item->get_hir_kind () != HIR::Node::VIS_ITEM)
+    return nullptr;
+
+  return static_cast<HIR::VisItem *> (item.get ());
+}
+
+ReachLevel
+ReachabilityVisitor::get_reachability_level (
+  const HIR::Visibility &item_visibility)
+{
+  return item_visibility.is_public () ? current_level : ReachLevel::Unreachable;
+}
+
+void
+ReachabilityVisitor::visit_generic_predicates (
+  const std::vector<std::unique_ptr<HIR::GenericParam>> &generics,
+  ReachLevel item_reach)
+{
+  if (item_reach == ReachLevel::Unreachable)
+    return;
+
+  for (const auto &generic : generics)
+    {
+      if (generic->get_kind () == HIR::GenericParam::GenericKind::TYPE)
+	{
+	  TyTy::BaseType *generic_ty = nullptr;
+	  auto ok = ty_ctx.lookup_type (generic->get_mappings ().get_hirid (),
+					&generic_ty);
+	  rust_assert (ok);
+	  rust_assert (generic_ty->get_kind () == TyTy::PARAM);
+
+	  auto generic_param = static_cast<TyTy::ParamType *> (generic_ty);
+	  for (const auto &bound : generic_param->get_specified_bounds ())
+	    {
+	      const auto trait = bound.get ()->get_hir_trait_ref ();
+	      ctx.update_reachability (trait->get_mappings (), item_reach);
+	    }
+	}
+    }
+}
+
+void
+ReachabilityVisitor::visit (HIR::Module &mod)
+{
+  auto reach = get_reachability_level (mod.get_visibility ());
+  reach = ctx.update_reachability (mod.get_mappings (), reach);
+
+  for (auto &item : mod.get_items ())
+    {
+      // FIXME: Is that what we want to do? Yes? Only visit the items with
+      // visibility?
+      //
+      // Imagine if we had `maybe_get_vis_item(item)?->accept_vis(*this)` ;)
+      auto vis_item = maybe_get_vis_item (item);
+      if (vis_item)
+	vis_item->accept_vis (*this);
+    }
+}
+
+void
+ReachabilityVisitor::visit (HIR::ExternCrate &crate)
+{
+  auto reach = get_reachability_level (crate.get_visibility ());
+  reach = ctx.update_reachability (crate.get_mappings (), reach);
+}
+
+void
+ReachabilityVisitor::visit (HIR::UseDeclaration &use_decl)
+{
+  auto reach = get_reachability_level (use_decl.get_visibility ());
+  reach = ctx.update_reachability (use_decl.get_mappings (), reach);
+}
+
+void
+ReachabilityVisitor::visit (HIR::Function &func)
+{
+  auto fn_reach = get_reachability_level (func.get_visibility ());
+
+  fn_reach = ctx.update_reachability (func.get_mappings (), fn_reach);
+  visit_generic_predicates (func.get_generic_params (), fn_reach);
+}
+
+void
+ReachabilityVisitor::visit (HIR::TypeAlias &type_alias)
+{
+  auto type_reach = get_reachability_level (type_alias.get_visibility ());
+
+  visit_generic_predicates (type_alias.get_generic_params (), type_reach);
+}
+
+void
+ReachabilityVisitor::visit (HIR::StructStruct &struct_item)
+{
+  auto struct_reach = get_reachability_level (struct_item.get_visibility ());
+
+  struct_reach
+    = ctx.update_reachability (struct_item.get_mappings (), struct_reach);
+
+  auto old_level = current_level;
+  current_level = struct_reach;
+
+  visit_generic_predicates (struct_item.get_generic_params (), struct_reach);
+
+  if (struct_reach != ReachLevel::Unreachable)
+    {
+      for (auto &field : struct_item.get_fields ())
+	if (field.get_visibility ().is_public ())
+	  ctx.update_reachability (field.get_field_type ()->get_mappings (),
+				   struct_reach);
+    }
+
+  current_level = old_level;
+}
+
+void
+ReachabilityVisitor::visit (HIR::TupleStruct &tuple_struct)
+{}
+
+void
+ReachabilityVisitor::visit (HIR::Enum &enum_item)
+{
+  auto enum_reach = get_reachability_level (enum_item.get_visibility ());
+
+  enum_reach = ctx.update_reachability (enum_item.get_mappings (), enum_reach);
+  visit_generic_predicates (enum_item.get_generic_params (), enum_reach);
+
+  for (const auto &variant : enum_item.get_variants ())
+    {
+      auto variant_reach
+	= ctx.update_reachability (variant->get_mappings (), enum_reach);
+
+      switch (variant->get_enum_item_kind ())
+	{
+	  case HIR::EnumItem::Tuple: {
+	    // Should we update the fields only if they are public? Similarly to
+	    // what we do in the ReachabilityVisitor for HIR::TupleStruct?
+	    auto tuple_variant
+	      = static_cast<HIR::EnumItemTuple *> (variant.get ());
+	    for (const auto &field : tuple_variant->get_tuple_fields ())
+	      ctx.update_reachability (field.get_mappings (), variant_reach);
+	    break;
+	  }
+	  case HIR::EnumItem::Struct: {
+	    // Should we update the fields only if they are public? Similarly to
+	    // what we do in the ReachabilityVisitor for HIR::StructStruct?
+	    auto struct_variant
+	      = static_cast<HIR::EnumItemStruct *> (variant.get ());
+	    for (const auto &field : struct_variant->get_struct_fields ())
+	      ctx.update_reachability (field.get_mappings (), variant_reach);
+	    break;
+	  }
+	// Nothing nested to visit in that case
+	case HIR::EnumItem::Named:
+	case HIR::EnumItem::Discriminant:
+	  break;
+	}
+    }
+}
+
+void
+ReachabilityVisitor::visit (HIR::Union &union_item)
+{
+  auto union_reach = get_reachability_level (union_item.get_visibility ());
+
+  union_reach
+    = ctx.update_reachability (union_item.get_mappings (), union_reach);
+  visit_generic_predicates (union_item.get_generic_params (), union_reach);
+}
+
+void
+ReachabilityVisitor::visit (HIR::ConstantItem &const_item)
+{
+  auto reach = get_reachability_level (const_item.get_visibility ());
+  reach = ctx.update_reachability (const_item.get_mappings (), reach);
+}
+
+void
+ReachabilityVisitor::visit (HIR::StaticItem &static_item)
+{
+  auto reach = get_reachability_level (static_item.get_visibility ());
+  reach = ctx.update_reachability (static_item.get_mappings (), reach);
+}
+
+void
+ReachabilityVisitor::visit (HIR::Trait &trait)
+{
+  auto trait_reach = get_reachability_level (trait.get_visibility ());
+
+  trait_reach = ctx.update_reachability (trait.get_mappings (), trait_reach);
+  visit_generic_predicates (trait.get_generic_params (), trait_reach);
+}
+
+void
+ReachabilityVisitor::visit (HIR::ImplBlock &impl)
+{
+  auto impl_reach = get_reachability_level (impl.get_visibility ());
+
+  impl_reach = ctx.update_reachability (impl.get_mappings (), impl_reach);
+  visit_generic_predicates (impl.get_generic_params (), impl_reach);
+}
+
+void
+ReachabilityVisitor::visit (HIR::ExternBlock &block)
+{}
+
+// FIXME: How can we visit Blocks in the current configuration? Have a full
+// visitor?
+} // namespace Privacy
+} // namespace Rust
diff --git a/gcc/rust/checks/errors/privacy/rust-reachability.h b/gcc/rust/checks/errors/privacy/rust-reachability.h
new file mode 100644
index 00000000000..e0bc4f5f0b8
--- /dev/null
+++ b/gcc/rust/checks/errors/privacy/rust-reachability.h
@@ -0,0 +1,87 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_REACHABILITY_H
+#define RUST_REACHABILITY_H
+
+#include "rust-privacy-ctx.h"
+#include "rust-hir-visitor.h"
+#include "rust-hir.h"
+#include "rust-hir-expr.h"
+#include "rust-hir-stmt.h"
+#include "rust-hir-item.h"
+#include "rust-hir-type-check.h"
+
+namespace Rust {
+namespace Privacy {
+
+// FIXME: The EmbargoVisitor from rustc is a fixed-point visitor which tries
+// to reach more and more nodes until nothing has changed anymore.
+// Do we need to reproduce this behavior? How long does it take to do this?
+
+/**
+ * The ReachabilityVisitor tries to reach all items possible in the crate,
+ * according to their privacy level.
+ */
+class ReachabilityVisitor : public HIR::HIRVisItemVisitor
+{
+public:
+  ReachabilityVisitor (PrivacyContext &ctx,
+		       const ::Rust::Resolver::TypeCheckContext &ty_ctx)
+    : current_level (ReachLevel::Reachable), ctx (ctx), ty_ctx (ty_ctx)
+  {}
+
+  // FIXME: Add `go` method which takes an `HIR::Crate &` as argument
+
+  /**
+   * Visit all the predicates of all the generic types of a given item, marking
+   * them as reachable or not.
+   */
+  void visit_generic_predicates (
+    const std::vector<std::unique_ptr<HIR::GenericParam>> &generics,
+    ReachLevel item_reach);
+
+  /**
+   * Get the initial reach level for an item based on its visibility.
+   */
+  ReachLevel get_reachability_level (const HIR::Visibility &item_visibility);
+
+  virtual void visit (HIR::Module &mod);
+  virtual void visit (HIR::ExternCrate &crate);
+  virtual void visit (HIR::UseDeclaration &use_decl);
+  virtual void visit (HIR::Function &func);
+  virtual void visit (HIR::TypeAlias &type_alias);
+  virtual void visit (HIR::StructStruct &struct_item);
+  virtual void visit (HIR::TupleStruct &tuple_struct);
+  virtual void visit (HIR::Enum &enum_item);
+  virtual void visit (HIR::Union &union_item);
+  virtual void visit (HIR::ConstantItem &const_item);
+  virtual void visit (HIR::StaticItem &static_item);
+  virtual void visit (HIR::Trait &trait);
+  virtual void visit (HIR::ImplBlock &impl);
+  virtual void visit (HIR::ExternBlock &block);
+
+private:
+  ReachLevel current_level;
+  PrivacyContext &ctx;
+  const ::Rust::Resolver::TypeCheckContext &ty_ctx;
+};
+} // namespace Privacy
+} // namespace Rust
+
+#endif // !RUST_REACHABILITY_H
diff --git a/gcc/rust/checks/errors/privacy/rust-visibility-resolver.cc b/gcc/rust/checks/errors/privacy/rust-visibility-resolver.cc
new file mode 100644
index 00000000000..301182754a4
--- /dev/null
+++ b/gcc/rust/checks/errors/privacy/rust-visibility-resolver.cc
@@ -0,0 +1,245 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "rust-visibility-resolver.h"
+#include "rust-ast.h"
+#include "rust-hir.h"
+#include "rust-hir-item.h"
+
+namespace Rust {
+namespace Privacy {
+
+VisibilityResolver::VisibilityResolver (Analysis::Mappings &mappings,
+					Resolver::Resolver &resolver)
+  : mappings (mappings), resolver (resolver)
+{}
+
+void
+VisibilityResolver::go (HIR::Crate &crate)
+{
+  mappings.insert_visibility (crate.get_mappings ().get_nodeid (),
+			      ModuleVisibility::create_public ());
+
+  current_module = crate.get_mappings ().get_defid ();
+
+  for (auto &item : crate.items)
+    {
+      if (item->get_hir_kind () == HIR::Node::VIS_ITEM)
+	{
+	  auto vis_item = static_cast<HIR::VisItem *> (item.get ());
+	  vis_item->accept_vis (*this);
+	}
+    }
+}
+
+bool
+VisibilityResolver::resolve_module_path (const HIR::SimplePath &restriction,
+					 DefId &id)
+{
+  // We need, from the restriction, to figure out the actual Module it
+  // belongs to.
+
+  NodeId ast_node_id = restriction.get_mappings ().get_nodeid ();
+
+  auto invalid_path
+    = Error (restriction.get_locus (),
+	     "cannot use non-module path as privacy restrictor");
+
+  NodeId ref_node_id = UNKNOWN_NODEID;
+  if (!resolver.lookup_resolved_name (ast_node_id, &ref_node_id))
+    {
+      invalid_path.emit_error ();
+      return false;
+    }
+  // FIXME: Add a hint here if we can find the path in another scope, such as
+  // a type or something else
+  // TODO: For the hint, can we point to the original item's definition if
+  // present?
+
+  HirId ref;
+  rust_assert (mappings.lookup_node_to_hir (ref_node_id, &ref));
+
+  auto module = mappings.lookup_module (ref);
+  if (!module)
+    {
+      invalid_path.emit_error ();
+      return false;
+    }
+
+  // Fill in the resolved `DefId`
+  id = module->get_mappings ().get_defid ();
+
+  return true;
+}
+
+bool
+VisibilityResolver::resolve_visibility (const HIR::Visibility &visibility,
+					ModuleVisibility &to_resolve)
+{
+  switch (visibility.get_vis_type ())
+    {
+    case HIR::Visibility::PRIVATE:
+      to_resolve = ModuleVisibility::create_restricted (current_module);
+      return true;
+    case HIR::Visibility::PUBLIC:
+      to_resolve = ModuleVisibility::create_public ();
+      return true;
+      case HIR::Visibility::RESTRICTED: {
+	// FIXME: We also need to handle 2015 vs 2018 edition conflicts
+	auto id = UNKNOWN_DEFID;
+	auto result = resolve_module_path (visibility.get_path (), id);
+	to_resolve = ModuleVisibility::create_restricted (id);
+	return result;
+      }
+    default:
+      gcc_unreachable ();
+      return false;
+    }
+}
+
+void
+VisibilityResolver::resolve_and_update (const HIR::VisItem *item)
+{
+  ModuleVisibility module_vis;
+  if (!resolve_visibility (item->get_visibility (), module_vis))
+    return; // we will already have emitted errors
+
+  mappings.insert_visibility (item->get_mappings ().get_nodeid (), module_vis);
+}
+
+void
+VisibilityResolver::visit (HIR::Module &mod)
+{
+  auto old_module = current_module;
+  current_module = mod.get_mappings ().get_defid ();
+
+  for (auto &item : mod.get_items ())
+    {
+      if (item->get_hir_kind () == HIR::Node::VIS_ITEM)
+	{
+	  auto vis_item = static_cast<HIR::VisItem *> (item.get ());
+	  vis_item->accept_vis (*this);
+	}
+    }
+
+  current_module = old_module;
+}
+
+void
+VisibilityResolver::visit (HIR::ExternCrate &crate)
+{}
+
+void
+VisibilityResolver::visit (HIR::UseDeclaration &use_decl)
+{}
+
+void
+VisibilityResolver::visit (HIR::Function &func)
+{
+  resolve_and_update (&func);
+}
+
+void
+VisibilityResolver::visit (HIR::TypeAlias &type_alias)
+{
+  resolve_and_update (&type_alias);
+}
+
+void
+VisibilityResolver::visit (HIR::StructStruct &struct_item)
+{
+  resolve_and_update (&struct_item);
+}
+
+void
+VisibilityResolver::visit (HIR::TupleStruct &tuple_struct)
+{
+  resolve_and_update (&tuple_struct);
+}
+
+void
+VisibilityResolver::visit (HIR::Enum &enum_item)
+{
+  ModuleVisibility vis;
+  if (!resolve_visibility (enum_item.get_visibility (), vis))
+    return;
+
+  mappings.insert_visibility (enum_item.get_mappings ().get_nodeid (), vis);
+  for (auto &variant : enum_item.get_variants ())
+    mappings.insert_visibility (variant->get_mappings ().get_nodeid (), vis);
+}
+
+void
+VisibilityResolver::visit (HIR::Union &union_item)
+{}
+
+void
+VisibilityResolver::visit (HIR::ConstantItem &const_item)
+{
+  resolve_and_update (&const_item);
+}
+
+void
+VisibilityResolver::visit (HIR::StaticItem &static_item)
+{
+  resolve_and_update (&static_item);
+}
+
+void
+VisibilityResolver::visit (HIR::Trait &trait)
+{
+  ModuleVisibility vis;
+  if (!resolve_visibility (trait.get_visibility (), vis))
+    return;
+
+  mappings.insert_visibility (trait.get_mappings ().get_nodeid (), vis);
+  for (auto &item : trait.get_trait_items ())
+    mappings.insert_visibility (item->get_mappings ().get_nodeid (), vis);
+}
+
+void
+VisibilityResolver::visit (HIR::ImplBlock &impl)
+{
+  for (auto &item : impl.get_impl_items ())
+    {
+      HIR::VisItem *vis_item;
+      switch (item->get_impl_item_type ())
+	{
+	case HIR::ImplItem::FUNCTION:
+	  vis_item = static_cast<HIR::Function *> (item.get ());
+	  break;
+	case HIR::ImplItem::TYPE_ALIAS:
+	  vis_item = static_cast<HIR::TypeAlias *> (item.get ());
+	  break;
+	case HIR::ImplItem::CONSTANT:
+	  vis_item = static_cast<HIR::ConstantItem *> (item.get ());
+	  break;
+	default:
+	  gcc_unreachable ();
+	  return;
+	}
+      vis_item->accept_vis (*this);
+    }
+}
+
+void
+VisibilityResolver::visit (HIR::ExternBlock &block)
+{}
+
+} // namespace Privacy
+} // namespace Rust
diff --git a/gcc/rust/checks/errors/privacy/rust-visibility-resolver.h b/gcc/rust/checks/errors/privacy/rust-visibility-resolver.h
new file mode 100644
index 00000000000..20a581c16d4
--- /dev/null
+++ b/gcc/rust/checks/errors/privacy/rust-visibility-resolver.h
@@ -0,0 +1,103 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_VISIBILITY_H
+#define RUST_VISIBILITY_H
+
+#include "rust-hir.h"
+#include "rust-hir-expr.h"
+#include "rust-hir-stmt.h"
+#include "rust-hir-item.h"
+#include "rust-hir-map.h"
+#include "rust-name-resolver.h"
+#include "rust-hir-visitor.h"
+
+namespace Rust {
+namespace Privacy {
+
+class VisibilityResolver : public HIR::HIRVisItemVisitor
+{
+public:
+  VisibilityResolver (Analysis::Mappings &mappings,
+		      Rust::Resolver::Resolver &resolver);
+
+  /**
+   * Perform visibility resolving on an entire crate
+   */
+  void go (HIR::Crate &crate);
+
+  /**
+   * Resolve a path to the module it refers
+   */
+  bool resolve_module_path (const HIR::SimplePath &restriction,
+			    DefId &to_resolve);
+
+  /**
+   * Resolve the visibility of an item to its ModuleVisibility. This function
+   * emits errors if necessary. The contents of the to_resolve parameter will be
+   * overwritten on success.
+   *
+   * @param visibility Visibility of the item to resolve
+   * @param to_resolve ModuleVisibility reference to fill on success.
+   *
+   * @return false on error, true if the resolving was successful.
+   */
+  bool resolve_visibility (const HIR::Visibility &visibility,
+			   ModuleVisibility &to_resolve);
+
+  /**
+   * Resolve the visibility of an item and updates it. This is useful for
+   * vis-items who need to be resolved but do not care about their module
+   * visibility - const items, static items, etc. For items with an impact on
+   * their children (enums, traits), this cannot be used
+   */
+  void resolve_and_update (const HIR::VisItem *item);
+
+  /**
+   * Get the DefId of the parent module we are currently visiting.
+   *
+   * @return UNKNOWN_DEFID if the module stack is empty, a valid `DefId`
+   * otherwise
+   */
+  DefId peek_module ();
+
+  virtual void visit (HIR::Module &mod);
+  virtual void visit (HIR::ExternCrate &crate);
+  virtual void visit (HIR::UseDeclaration &use_decl);
+  virtual void visit (HIR::Function &func);
+  virtual void visit (HIR::TypeAlias &type_alias);
+  virtual void visit (HIR::StructStruct &struct_item);
+  virtual void visit (HIR::TupleStruct &tuple_struct);
+  virtual void visit (HIR::Enum &enum_item);
+  virtual void visit (HIR::Union &union_item);
+  virtual void visit (HIR::ConstantItem &const_item);
+  virtual void visit (HIR::StaticItem &static_item);
+  virtual void visit (HIR::Trait &trait);
+  virtual void visit (HIR::ImplBlock &impl);
+  virtual void visit (HIR::ExternBlock &block);
+
+private:
+  Analysis::Mappings &mappings;
+  Rust::Resolver::Resolver &resolver;
+  DefId current_module;
+};
+
+} // namespace Privacy
+} // namespace Rust
+
+#endif // !RUST_VISIBILITY_H
