gccrs: Add privacy checks

This pass is responsible for resolving the privacy of items and verifying
that access to these items is performed within the limits of that privacy.
By default, items in Rust are private and only public to the current
module and its submodules. However, the user can annotate an item with
various qualifiers such as `pub` to publicly expose an item. Furthermore,
a module path can be given to `pub` to restrict an item's privacy to a
certain module: These paths need to be resolved and later on checked by
the privacy error reporter.

	gcc/rust/
	* checks/errors/privacy/rust-privacy-check.cc: New.
	* checks/errors/privacy/rust-privacy-check.h: New.
	* checks/errors/privacy/rust-privacy-common.h: New.
	* checks/errors/privacy/rust-privacy-ctx.cc: New.
	* checks/errors/privacy/rust-privacy-ctx.h: New.
	* checks/errors/privacy/rust-privacy-reporter.cc: New.
	* checks/errors/privacy/rust-privacy-reporter.h: New.
	* checks/errors/privacy/rust-pub-restricted-visitor.cc: New.
	* checks/errors/privacy/rust-pub-restricted-visitor.h: New.
	* checks/errors/privacy/rust-reachability.cc: New.
	* checks/errors/privacy/rust-reachability.h: New.
	* checks/errors/privacy/rust-visibility-resolver.cc: New.
	* checks/errors/privacy/rust-visibility-resolver.h: New.
This commit is contained in:
Arthur Cohen 2022-08-23 16:32:26 +01:00
parent 5215235f01
commit ca246e573f
13 changed files with 2263 additions and 0 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 &param : static_cast<const TyTy::FnPtr *> (ty)->get_params ())
recursive_check (param.get_tyty ());
return recursive_check (
static_cast<const TyTy::FnPtr *> (ty)->get_return_type ());
case TyTy::TUPLE:
for (auto &param :
static_cast<const TyTy::TupleType *> (ty)->get_fields ())
recursive_check (param.get_tyty ());
return;
case TyTy::PLACEHOLDER:
return recursive_check (
// FIXME: Can we use `resolve` here? Is that what we should do?
static_cast<const TyTy::PlaceholderType *> (ty)->resolve ());
case TyTy::PROJECTION:
return recursive_check (
static_cast<const TyTy::ProjectionType *> (ty)->get ());
case TyTy::CLOSURE:
rust_sorry_at (locus, "privacy pass for closures is not handled yet");
break;
// If we're dealing with a generic param, there's nothing we should be
// doing here
case TyTy::PARAM:
// We are dealing with a function definition that has been assigned
// somewhere else. Nothing to resolve privacy-wise other than the actual
// function, which is resolved as an expression
case TyTy::FNDEF:
// FIXME: Can we really not resolve Dynamic types here? Shouldn't we have
// a look at the path and perform proper privacy analysis?
case TyTy::DYNAMIC:
// The never type is builtin and always available
case TyTy::NEVER:
// We shouldn't have inference types here, ever
case TyTy::INFER:
return;
case TyTy::ERROR:
rust_unreachable ();
}
}
void
PrivacyReporter::check_type_privacy (const HIR::Type *type)
{
rust_assert (type);
TyTy::BaseType *lookup = nullptr;
rust_assert (
ty_ctx.lookup_type (type->get_mappings ().get_hirid (), &lookup));
auto node_mappings = type->get_mappings ();
return check_base_type_privacy (node_mappings, lookup, type->get_locus ());
}
void
PrivacyReporter::visit (HIR::PathInExpression &path)
{
check_for_privacy_violation (path.get_mappings ().get_nodeid (),
path.get_locus ());
}
void
PrivacyReporter::visit (HIR::TypePathSegmentFunction &segment)
{
// FIXME: Do we need to do anything for this?
}
void
PrivacyReporter::visit (HIR::TypePath &path)
{
check_for_privacy_violation (path.get_mappings ().get_nodeid (),
path.get_locus ());
}
void
PrivacyReporter::visit (HIR::QualifiedPathInExpression &path)
{
check_for_privacy_violation (path.get_mappings ().get_nodeid (),
path.get_locus ());
}
void
PrivacyReporter::visit (HIR::QualifiedPathInType &path)
{
check_for_privacy_violation (path.get_mappings ().get_nodeid (),
path.get_locus ());
}
void
PrivacyReporter::visit (HIR::LiteralExpr &expr)
{
// Literals cannot contain any sort of privacy violation
}
void
PrivacyReporter::visit (HIR::BorrowExpr &expr)
{
expr.get_expr ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::DereferenceExpr &expr)
{
expr.get_expr ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::ErrorPropagationExpr &expr)
{
expr.get_expr ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::NegationExpr &expr)
{
expr.get_expr ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::ArithmeticOrLogicalExpr &expr)
{
expr.get_lhs ()->accept_vis (*this);
expr.get_rhs ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::ComparisonExpr &expr)
{
expr.get_lhs ()->accept_vis (*this);
expr.get_rhs ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::LazyBooleanExpr &expr)
{
expr.get_lhs ()->accept_vis (*this);
expr.get_rhs ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::TypeCastExpr &expr)
{
expr.get_expr ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::AssignmentExpr &expr)
{
expr.get_lhs ()->accept_vis (*this);
expr.get_rhs ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::CompoundAssignmentExpr &expr)
{
expr.get_left_expr ()->accept_vis (*this);
expr.get_right_expr ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::GroupedExpr &expr)
{
expr.get_expr_in_parens ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::ArrayExpr &expr)
{
HIR::ArrayElems &elements = *expr.get_internal_elements ();
switch (elements.get_array_expr_type ())
{
case HIR::ArrayElems::ArrayExprType::VALUES: {
HIR::ArrayElemsValues &elems
= static_cast<HIR::ArrayElemsValues &> (elements);
for (auto &value : elems.get_values ())
value->accept_vis (*this);
}
return;
case HIR::ArrayElems::ArrayExprType::COPIED:
HIR::ArrayElemsCopied &elems
= static_cast<HIR::ArrayElemsCopied &> (elements);
elems.get_elem_to_copy ()->accept_vis (*this);
}
}
void
PrivacyReporter::visit (HIR::ArrayIndexExpr &expr)
{
expr.get_array_expr ()->accept_vis (*this);
expr.get_index_expr ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::TupleExpr &expr)
{
for (auto &value : expr.get_tuple_elems ())
value->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::TupleIndexExpr &expr)
{
expr.get_tuple_expr ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::StructExprStruct &expr)
{
// FIXME: We need to check the visibility of the type it refers to here
}
void
PrivacyReporter::visit (HIR::StructExprFieldIdentifier &field)
{}
void
PrivacyReporter::visit (HIR::StructExprFieldIdentifierValue &field)
{
field.get_value ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::StructExprFieldIndexValue &field)
{
field.get_value ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::StructExprStructFields &expr)
{
for (auto &field : expr.get_fields ())
field->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::CallExpr &expr)
{
expr.get_fnexpr ()->accept_vis (*this);
for (auto &param : expr.get_arguments ())
param->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::MethodCallExpr &expr)
{
expr.get_receiver ()->accept_vis (*this);
for (auto &param : expr.get_arguments ())
param->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::FieldAccessExpr &expr)
{
expr.get_receiver_expr ()->accept_vis (*this);
// FIXME: We should also check if the field is public?
}
void
PrivacyReporter::visit (HIR::ClosureExprInner &expr)
{
// Not handled yet
}
void
PrivacyReporter::visit (HIR::BlockExpr &expr)
{
for (auto &stmt : expr.get_statements ())
stmt->accept_vis (*this);
auto &last_expr = expr.get_final_expr ();
if (last_expr)
last_expr->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::ClosureExprInnerTyped &expr)
{
// Not handled yet
}
void
PrivacyReporter::visit (HIR::ContinueExpr &expr)
{}
void
PrivacyReporter::visit (HIR::BreakExpr &expr)
{
auto &break_expr = expr.get_expr ();
if (break_expr)
break_expr->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::RangeFromToExpr &expr)
{
expr.get_from_expr ()->accept_vis (*this);
expr.get_to_expr ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::RangeFromExpr &expr)
{
expr.get_from_expr ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::RangeToExpr &expr)
{
expr.get_to_expr ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::RangeFullExpr &expr)
{}
void
PrivacyReporter::visit (HIR::RangeFromToInclExpr &expr)
{
expr.get_from_expr ()->accept_vis (*this);
expr.get_to_expr ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::RangeToInclExpr &expr)
{
// Not handled yet
}
void
PrivacyReporter::visit (HIR::ReturnExpr &expr)
{
auto return_expr = expr.get_expr ();
if (return_expr)
return_expr->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::UnsafeBlockExpr &expr)
{
expr.get_block_expr ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::LoopExpr &expr)
{
expr.get_loop_block ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::WhileLoopExpr &expr)
{
expr.get_predicate_expr ()->accept_vis (*this);
expr.get_loop_block ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::WhileLetLoopExpr &expr)
{
expr.get_cond ()->accept_vis (*this);
expr.get_loop_block ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::ForLoopExpr &expr)
{
expr.get_iterator_expr ()->accept_vis (*this);
expr.get_loop_block ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::IfExpr &expr)
{
expr.get_if_condition ()->accept_vis (*this);
expr.get_if_block ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::IfExprConseqElse &expr)
{
expr.get_if_condition ()->accept_vis (*this);
expr.get_if_block ()->accept_vis (*this);
expr.get_else_block ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::IfExprConseqIf &expr)
{
expr.get_if_condition ()->accept_vis (*this);
expr.get_if_block ()->accept_vis (*this);
expr.get_conseq_if_expr ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::IfExprConseqIfLet &expr)
{
expr.get_if_condition ()->accept_vis (*this);
expr.get_if_block ()->accept_vis (*this);
// TODO: We need to visit the if_let_expr as well
}
void
PrivacyReporter::visit (HIR::IfLetExpr &expr)
{
// TODO: We need to visit the if_let_expr
// TODO: We need to visit the block as well
}
void
PrivacyReporter::visit (HIR::IfLetExprConseqElse &expr)
{
// TODO: We need to visit the if_let_expr
// TODO: We need to visit the if_block as well
// TODO: We need to visit the else_block as well
}
void
PrivacyReporter::visit (HIR::IfLetExprConseqIf &expr)
{
// TODO: We need to visit the if_let_expr
// TODO: We need to visit the if_block as well
// TODO: We need to visit the else_block as well
}
void
PrivacyReporter::visit (HIR::IfLetExprConseqIfLet &expr)
{
// TODO: We need to visit the if_let_expr
// TODO: We need to visit the if_block as well
// TODO: We need to visit the else_block as well
}
void
PrivacyReporter::visit (HIR::MatchExpr &expr)
{
expr.get_scrutinee_expr ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::AwaitExpr &expr)
{
// Not handled yet
}
void
PrivacyReporter::visit (HIR::AsyncBlockExpr &expr)
{
// Not handled yet
}
void
PrivacyReporter::visit (HIR::Module &module)
{
// FIXME: We also need to think about module privacy
auto old_module = current_module;
current_module
= Optional<NodeId>::some (module.get_mappings ().get_nodeid ());
for (auto &item : module.get_items ())
item->accept_vis (*this);
current_module = old_module;
}
void
PrivacyReporter::visit (HIR::ExternCrate &crate)
{}
void
PrivacyReporter::visit (HIR::UseDeclaration &use_decl)
{
// FIXME: Is there anything we need to do here?
}
void
PrivacyReporter::visit (HIR::Function &function)
{
for (auto &param : function.get_function_params ())
check_type_privacy (param.get_type ());
function.get_definition ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::TypeAlias &type_alias)
{
// TODO: Check the type here
}
void
PrivacyReporter::visit (HIR::StructStruct &struct_item)
{
// TODO: Check the type of all fields
}
void
PrivacyReporter::visit (HIR::TupleStruct &tuple_struct)
{
// TODO: Check the type of all fields
}
void
PrivacyReporter::visit (HIR::EnumItem &item)
{
// TODO: Check the type of all variants
}
void
PrivacyReporter::visit (HIR::EnumItemTuple &item)
{
// TODO: Check the type
}
void
PrivacyReporter::visit (HIR::EnumItemStruct &item)
{
// TODO: Check the type
}
void
PrivacyReporter::visit (HIR::EnumItemDiscriminant &item)
{}
void
PrivacyReporter::visit (HIR::Enum &enum_item)
{}
void
PrivacyReporter::visit (HIR::Union &union_item)
{
// TODO: Check the type
}
void
PrivacyReporter::visit (HIR::ConstantItem &const_item)
{
// TODO: We need to visit the type
const_item.get_expr ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::StaticItem &static_item)
{
// TODO: We need to visit the type
static_item.get_expr ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::Trait &trait)
{
// FIXME: We need to be an ItemVisitor as well
// for (auto &item : trait.get_trait_items ())
// item->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::ImplBlock &impl)
{
for (auto &item : impl.get_impl_items ())
item->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::ExternBlock &block)
{
// FIXME: We need to be an ItemVisitor as well
// for (auto &item : block.get_extern_items ())
// item->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::EmptyStmt &stmt)
{}
void
PrivacyReporter::visit (HIR::LetStmt &stmt)
{
auto type = stmt.get_type ();
if (type)
check_type_privacy (type);
auto init_expr = stmt.get_init_expr ();
if (init_expr)
init_expr->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::ExprStmtWithoutBlock &stmt)
{
stmt.get_expr ()->accept_vis (*this);
}
void
PrivacyReporter::visit (HIR::ExprStmtWithBlock &stmt)
{
stmt.get_expr ()->accept_vis (*this);
}
} // namespace Privacy
} // namespace Rust

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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