new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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 ¶m : 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 ¶m :
+ 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 ¶m : expr.get_arguments ())
+ param->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::MethodCallExpr &expr)
+{
+ expr.get_receiver ()->accept_vis (*this);
+
+ for (auto ¶m : 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 ¶m : 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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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