diff options
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/rust/checks/errors/privacy/rust-privacy-check.cc | 63 | ||||
-rw-r--r-- | gcc/rust/checks/errors/privacy/rust-privacy-check.h | 44 | ||||
-rw-r--r-- | gcc/rust/checks/errors/privacy/rust-privacy-common.h | 67 | ||||
-rw-r--r-- | gcc/rust/checks/errors/privacy/rust-privacy-ctx.cc | 93 | ||||
-rw-r--r-- | gcc/rust/checks/errors/privacy/rust-privacy-ctx.h | 79 | ||||
-rw-r--r-- | gcc/rust/checks/errors/privacy/rust-privacy-reporter.cc | 771 | ||||
-rw-r--r-- | gcc/rust/checks/errors/privacy/rust-privacy-reporter.h | 173 | ||||
-rw-r--r-- | gcc/rust/checks/errors/privacy/rust-pub-restricted-visitor.cc | 182 | ||||
-rw-r--r-- | gcc/rust/checks/errors/privacy/rust-pub-restricted-visitor.h | 120 | ||||
-rw-r--r-- | gcc/rust/checks/errors/privacy/rust-reachability.cc | 236 | ||||
-rw-r--r-- | gcc/rust/checks/errors/privacy/rust-reachability.h | 87 | ||||
-rw-r--r-- | gcc/rust/checks/errors/privacy/rust-visibility-resolver.cc | 245 | ||||
-rw-r--r-- | gcc/rust/checks/errors/privacy/rust-visibility-resolver.h | 103 |
13 files changed, 2263 insertions, 0 deletions
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 0000000..9664d62 --- /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 0000000..290b5ea --- /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 0000000..ceafe91 --- /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 0000000..9ebc869 --- /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 0000000..52d790e --- /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 0000000..3c9380a --- /dev/null +++ b/gcc/rust/checks/errors/privacy/rust-privacy-reporter.cc @@ -0,0 +1,771 @@ +// 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-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 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 0000000..546b108 --- /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 0000000..e391653 --- /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 0000000..2685f3d --- /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 0000000..b322e29 --- /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 0000000..e0bc4f5 --- /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 0000000..3011827 --- /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 0000000..20a581c --- /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 |