From patchwork Wed Aug 24 11:59:44 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: herron.philip@googlemail.com X-Patchwork-Id: 711 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:adf:ecc5:0:0:0:0:0 with SMTP id s5csp1387025wro; Wed, 24 Aug 2022 05:11:10 -0700 (PDT) X-Google-Smtp-Source: AA6agR4f2KccVRn63vr+fdNmIMIBUvXuM20LUnxKC53YEabB3nyaQT9XQKTO2GY9t8aG3Nk5q0kM X-Received: by 2002:a05:6402:1943:b0:443:5ffb:b04e with SMTP id f3-20020a056402194300b004435ffbb04emr7398297edz.230.1661343070283; Wed, 24 Aug 2022 05:11:10 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1661343070; cv=none; d=google.com; s=arc-20160816; b=SfspPyLkBubnIJM5NimdUHTlVjdcND/YqwQ2ipTr1r0ki5LD/ol0iMFyouH4R0q17n Z8VKfUMtF+yRA5suvQObzvvLEJ7tlUFub8zsOEL/iJqwb8Fp5SJl4zaEBJ1FljCX3ckh Kev861RkP6bEWBWq1HQKeB1LGpmYWqzw6GHFo0be0lY8zUGGfW7sCAmhsdrRa5ZPxo2F kvHfkPvwDAO20jCQdgTT/wGzYjXjFD3nE2DIA0OPHRpWZGdANyWTIuVMOJt+Vop5pFu9 mRJ5AAGdkJdUchR6v6Uv3hdkOdjPByiEFCcyWFE7Y1zaogYVOtzaXxmrUOaD9vngMpub f4EQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:cc:reply-to:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence :content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:dkim-signature:dmarc-filter :delivered-to; bh=g+XAU5zJCKM/WtsbNuZ9oIAGDyQ4juA7YKh51gc2H7o=; b=oZxOVbFTPgRguRwv7hmesTZ0v365AtYr90+vO9mIdtoT+qzPmy0kaV7lqJMafJs5fh AZAw74O2ua0UUIlp0VbmIp6JDNQLjrneAgscCl6WauyHOqiGrcauGR8ZIapmSpOmzb0M nnEDSqMQHto/VNGDybg33nv9ltUX9MTQKgf8CXYMTakMHPiS+QzemQf1GrSajP+oQuoK Fe/aL4bXaDIidxGP9vguxZwmvJ4Tt0Snyp3PATkZK2dXoXrNOF+V88evADKqbEN7tEsf Y4PAIpLGf9GeoLp4AP7zTNcb+9vWjZdLwsXgHhh6uFJ9ioOGRyRaQodBgzwBJLPiemxo c8mQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=fail header.i=@googlemail.com header.s=20210112 header.b=iA9E9Uaw; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=fail (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=googlemail.com Received: from sourceware.org (ip-8-43-85-97.sourceware.org. [8.43.85.97]) by mx.google.com with ESMTPS id bx15-20020a0564020b4f00b0044791991d0fsi417385edb.439.2022.08.24.05.11.09 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 24 Aug 2022 05:11:10 -0700 (PDT) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) client-ip=8.43.85.97; Authentication-Results: mx.google.com; dkim=fail header.i=@googlemail.com header.s=20210112 header.b=iA9E9Uaw; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=fail (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=googlemail.com Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id E59D0395BC48 for ; Wed, 24 Aug 2022 12:04:25 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-wm1-x32b.google.com (mail-wm1-x32b.google.com [IPv6:2a00:1450:4864:20::32b]) by sourceware.org (Postfix) with ESMTPS id 23DDD385022F; Wed, 24 Aug 2022 12:01:09 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 23DDD385022F Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=googlemail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=googlemail.com Received: by mail-wm1-x32b.google.com with SMTP id k18-20020a05600c0b5200b003a5dab49d0bso711115wmr.3; Wed, 24 Aug 2022 05:01:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlemail.com; s=20210112; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc; bh=g+XAU5zJCKM/WtsbNuZ9oIAGDyQ4juA7YKh51gc2H7o=; b=iA9E9Uawk0l7YRpjaBMUw5ANIy0E2n6QNdOChlxjXlD1FJE7WXA+P5DXeyYXj+SNNA QsG+16JRVwa0vvsLo39jGFwvcViX115QMPj1oa75DgrS/LEjWWY08VD7iu7CyG7aMsK5 PU5aLkm64Ygv6l9x7C86w9JeISUcrB2efzsmTYLoYw/ceZiwRlQeXJbbLwOy2E1CzaLl aO6oSe5jn2pI6fAQhAjAbk78WqcCO9CnNW08zrgqQo6anUCy/uzwFyApnlMLa8uNFrmD ysKNAXZ2PeKlXW4p6yFLBUFQ6DcaheIlNrSQ/IZ01Yqu3p67f+5Vv4Hc55Mr13l3JxMg yRQg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-message-state :from:to:cc; bh=g+XAU5zJCKM/WtsbNuZ9oIAGDyQ4juA7YKh51gc2H7o=; b=FQgHtmigEVqcK+f3W4Jbo8AIjxfuMGUS1jvpCWCULGQLYPP+Ne3dFm6CGiXly2B82m 3TooRquIWHVTg++cddD7BM21LS6+qaiq0DTe8hCmkc6uYPKdE48wtP075FnKvwqWsQyQ IAutzHsRb8vAfl5ayocozWUw9ZOCKdyB2ifmooqm2gNx6PljD3MBWGI4XibeEaSUm8fF jZMeDVVBIS3JHzbemxS2coD0u+bxh8gtputLHcalWp6Sk/0tCvIpRYYjjMoNa9xx3OUq 5Xy5frtoGg1ipv8GV2MMAo1KtxI9fUpzZ/o6pNj8b2fusf3lakKgdKDyGPo0i0JMMli8 ABOQ== X-Gm-Message-State: ACgBeo1rxZDSA8zu2g5NWKuBHUKGNOr7LwwHsWi6pUiNwttEQsQHMQLf WJjBmhc9PvQg/stAZ7HXzF7r1+hNlSc= X-Received: by 2002:a7b:ce05:0:b0:3a5:c069:25b3 with SMTP id m5-20020a7bce05000000b003a5c06925b3mr5077226wmc.87.1661342466477; Wed, 24 Aug 2022 05:01:06 -0700 (PDT) Received: from localhost.localdomain ([86.14.124.218]) by smtp.gmail.com with ESMTPSA id cc19-20020a5d5c13000000b0022571d43d32sm1697676wrb.21.2022.08.24.05.01.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 24 Aug 2022 05:01:05 -0700 (PDT) From: herron.philip@googlemail.com X-Google-Original-From: philip.herron@embecosm.com To: gcc-patches@gcc.gnu.org Subject: [PATCH Rust front-end v2 25/37] gccrs: Add privacy checks Date: Wed, 24 Aug 2022 12:59:44 +0100 Message-Id: <20220824115956.737931-26-philip.herron@embecosm.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220824115956.737931-1-philip.herron@embecosm.com> References: <20220824115956.737931-1-philip.herron@embecosm.com> MIME-Version: 1.0 X-Spam-Status: No, score=-11.9 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: philip.herron@embecosm.com Cc: Arthur Cohen , gcc-rust@gcc.gnu.org Errors-To: gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org Sender: "Gcc-patches" X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1742044471411950496?= X-GMAIL-MSGID: =?utf-8?q?1742044471411950496?= From: Arthur Cohen 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. --- .../errors/privacy/rust-privacy-check.cc | 63 ++ .../errors/privacy/rust-privacy-check.h | 44 + .../errors/privacy/rust-privacy-common.h | 67 ++ .../checks/errors/privacy/rust-privacy-ctx.cc | 93 +++ .../checks/errors/privacy/rust-privacy-ctx.h | 79 ++ .../errors/privacy/rust-privacy-reporter.cc | 753 ++++++++++++++++++ .../errors/privacy/rust-privacy-reporter.h | 173 ++++ .../privacy/rust-pub-restricted-visitor.cc | 182 +++++ .../privacy/rust-pub-restricted-visitor.h | 120 +++ .../errors/privacy/rust-reachability.cc | 236 ++++++ .../checks/errors/privacy/rust-reachability.h | 87 ++ .../privacy/rust-visibility-resolver.cc | 245 ++++++ .../errors/privacy/rust-visibility-resolver.h | 103 +++ 13 files changed, 2245 insertions(+) create mode 100644 gcc/rust/checks/errors/privacy/rust-privacy-check.cc create mode 100644 gcc/rust/checks/errors/privacy/rust-privacy-check.h create mode 100644 gcc/rust/checks/errors/privacy/rust-privacy-common.h create mode 100644 gcc/rust/checks/errors/privacy/rust-privacy-ctx.cc create mode 100644 gcc/rust/checks/errors/privacy/rust-privacy-ctx.h create mode 100644 gcc/rust/checks/errors/privacy/rust-privacy-reporter.cc create mode 100644 gcc/rust/checks/errors/privacy/rust-privacy-reporter.h create mode 100644 gcc/rust/checks/errors/privacy/rust-pub-restricted-visitor.cc create mode 100644 gcc/rust/checks/errors/privacy/rust-pub-restricted-visitor.h create mode 100644 gcc/rust/checks/errors/privacy/rust-reachability.cc create mode 100644 gcc/rust/checks/errors/privacy/rust-reachability.h create mode 100644 gcc/rust/checks/errors/privacy/rust-visibility-resolver.cc create mode 100644 gcc/rust/checks/errors/privacy/rust-visibility-resolver.h diff --git a/gcc/rust/checks/errors/privacy/rust-privacy-check.cc b/gcc/rust/checks/errors/privacy/rust-privacy-check.cc new file mode 100644 index 00000000000..9664d62f65c --- /dev/null +++ b/gcc/rust/checks/errors/privacy/rust-privacy-check.cc @@ -0,0 +1,63 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#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 (item.get ()); + vis_item->accept_vis (visitor); + } + } + + if (saw_errors ()) + return; +} +} // namespace Privacy +} // namespace Rust diff --git a/gcc/rust/checks/errors/privacy/rust-privacy-check.h b/gcc/rust/checks/errors/privacy/rust-privacy-check.h new file mode 100644 index 00000000000..290b5eacb6c --- /dev/null +++ b/gcc/rust/checks/errors/privacy/rust-privacy-check.h @@ -0,0 +1,44 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_PRIVACY_CHECK_H +#define RUST_PRIVACY_CHECK_H + +#include "rust-hir.h" +#include "rust-hir-expr.h" +#include "rust-hir-stmt.h" +#include "rust-hir-item.h" +#include "rust-hir-type-check.h" + +namespace Rust { +namespace Privacy { +class Resolver +{ +public: + /** + * Perform the full privacy resolving pass on a crate. + * + * This resolver first computes the reachability of all items in a crate, + * before checking for privacy violations. + */ + static void resolve (HIR::Crate &crate); +}; +} // namespace Privacy +} // namespace Rust + +#endif // !RUST_PRIVACY_CHECK_H diff --git a/gcc/rust/checks/errors/privacy/rust-privacy-common.h b/gcc/rust/checks/errors/privacy/rust-privacy-common.h new file mode 100644 index 00000000000..ceafe91d886 --- /dev/null +++ b/gcc/rust/checks/errors/privacy/rust-privacy-common.h @@ -0,0 +1,67 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-mapping-common.h" + +namespace Rust { +namespace Privacy { + +/** + * Visibility class related specifically to DefIds. This class allows defining + * the visibility of an item with regard to a specific module. + * + * Items are either public throughout a crate, or restricted to a specific + * module. Private items are simply restricted to the current module. + */ +class ModuleVisibility +{ +public: + enum Type + { + Unknown, + Public, + Restricted, + }; + + ModuleVisibility () : kind (Unknown), module_id (UNKNOWN_DEFID) {} + + static ModuleVisibility create_restricted (DefId module_id) + { + return ModuleVisibility (Type::Restricted, module_id); + } + + static ModuleVisibility create_public () + { + return ModuleVisibility (Type::Public, UNKNOWN_DEFID); + } + + Type get_kind () const { return kind; } + + const DefId &get_module_id () const { return module_id; } + DefId &get_module_id () { return module_id; } + +private: + ModuleVisibility (Type kind, DefId module_id) + : kind (kind), module_id (module_id) + {} + + Type kind; + DefId module_id; +}; +} // namespace Privacy +} // namespace Rust diff --git a/gcc/rust/checks/errors/privacy/rust-privacy-ctx.cc b/gcc/rust/checks/errors/privacy/rust-privacy-ctx.cc new file mode 100644 index 00000000000..9ebc86988e9 --- /dev/null +++ b/gcc/rust/checks/errors/privacy/rust-privacy-ctx.cc @@ -0,0 +1,93 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-privacy-ctx.h" +#include "selftest.h" + +namespace Rust { +namespace Privacy { + +static ReachLevel +insert_if_higher (ReachLevel new_level, + std::unordered_map::iterator &existing) +{ + if (new_level > existing->second) + existing->second = new_level; + + return existing->second; +} + +ReachLevel +PrivacyContext::update_reachability (const Analysis::NodeMapping &mapping, + ReachLevel reach) +{ + auto def_id = mapping.get_defid (); + auto existing_reach = reachability_map.find (def_id); + if (existing_reach != reachability_map.end ()) + return insert_if_higher (reach, existing_reach); + + reachability_map.insert ({def_id, reach}); + return reach; +} + +const ReachLevel * +PrivacyContext::lookup_reachability (const Analysis::NodeMapping &mapping) +{ + auto existing_reach = reachability_map.find (mapping.get_defid ()); + if (existing_reach == reachability_map.end ()) + return nullptr; + + return &existing_reach->second; +} +} // namespace Privacy +} // namespace Rust + +#if CHECKING_P +namespace selftest { +static void +update_reachability_test (void) +{ + auto ctx = Rust::Privacy::PrivacyContext (); + // Bogus values for the mappings + auto mapping = Rust::Analysis::NodeMapping (15, 15, 15, 15); + + auto new_level + = ctx.update_reachability (mapping, Rust::Privacy::ReachLevel::Unreachable); + + ASSERT_EQ (new_level, Rust::Privacy::ReachLevel::Unreachable); + + ASSERT_TRUE (ctx.lookup_reachability (mapping)); + ASSERT_EQ (*ctx.lookup_reachability (mapping), + Rust::Privacy::ReachLevel::Unreachable); + + new_level + = ctx.update_reachability (mapping, Rust::Privacy::ReachLevel::Reachable); + + ASSERT_EQ (new_level, Rust::Privacy::ReachLevel::Reachable); + ASSERT_TRUE (ctx.lookup_reachability (mapping)); + ASSERT_EQ (*ctx.lookup_reachability (mapping), + Rust::Privacy::ReachLevel::Reachable); +} + +void +rust_privacy_ctx_test (void) +{ + update_reachability_test (); +} +} // namespace selftest +#endif // !CHECKING_P diff --git a/gcc/rust/checks/errors/privacy/rust-privacy-ctx.h b/gcc/rust/checks/errors/privacy/rust-privacy-ctx.h new file mode 100644 index 00000000000..52d790edf63 --- /dev/null +++ b/gcc/rust/checks/errors/privacy/rust-privacy-ctx.h @@ -0,0 +1,79 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#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 reachability_map; +}; +} // namespace Privacy +} // namespace Rust + +#if CHECKING_P +namespace selftest { +void +rust_privacy_ctx_test (void); +} +#endif // !CHECKING_P + +#endif // !RUST_PRIVACY_CTX_H diff --git a/gcc/rust/checks/errors/privacy/rust-privacy-reporter.cc b/gcc/rust/checks/errors/privacy/rust-privacy-reporter.cc new file mode 100644 index 00000000000..35fde40782e --- /dev/null +++ b/gcc/rust/checks/errors/privacy/rust-privacy-reporter.cc @@ -0,0 +1,753 @@ +#include "rust-privacy-reporter.h" +#include "rust-hir-expr.h" +#include "rust-hir-stmt.h" +#include "rust-hir-item.h" + +namespace Rust { +namespace Privacy { + +PrivacyReporter::PrivacyReporter ( + Analysis::Mappings &mappings, Resolver::Resolver &resolver, + const Rust::Resolver::TypeCheckContext &ty_ctx) + : mappings (mappings), resolver (resolver), ty_ctx (ty_ctx), + current_module (Optional::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 (ty)->get_base ()); + case TyTy::POINTER: + return recursive_check ( + static_cast (ty)->get_base ()); + case TyTy::ARRAY: + return recursive_check ( + static_cast (ty)->get_element_type ()); + case TyTy::SLICE: + return recursive_check ( + static_cast (ty)->get_element_type ()); + case TyTy::FNPTR: + for (auto ¶m : static_cast (ty)->get_params ()) + recursive_check (param.get_tyty ()); + return recursive_check ( + static_cast (ty)->get_return_type ()); + case TyTy::TUPLE: + for (auto ¶m : + static_cast (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 (ty)->resolve ()); + case TyTy::PROJECTION: + return recursive_check ( + static_cast (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 (elements); + for (auto &value : elems.get_values ()) + value->accept_vis (*this); + } + return; + + case HIR::ArrayElems::ArrayExprType::COPIED: + HIR::ArrayElemsCopied &elems + = static_cast (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::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 00000000000..546b108f36d --- /dev/null +++ b/gcc/rust/checks/errors/privacy/rust-privacy-reporter.h @@ -0,0 +1,173 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#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 current_module; +}; + +} // namespace Privacy +} // namespace Rust + +#endif // !RUST_PRIVACY_REPORTER_H diff --git a/gcc/rust/checks/errors/privacy/rust-pub-restricted-visitor.cc b/gcc/rust/checks/errors/privacy/rust-pub-restricted-visitor.cc new file mode 100644 index 00000000000..e391653ea26 --- /dev/null +++ b/gcc/rust/checks/errors/privacy/rust-pub-restricted-visitor.cc @@ -0,0 +1,182 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#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 (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 (item.get ()); + vis_item->accept_vis (*this); + } + } + + module_stack.pop_back (); +} + +void +PubRestrictedVisitor::visit (HIR::ExternCrate &crate) +{ + is_restriction_valid (crate.get_mappings ().get_nodeid (), + crate.get_locus ()); +} + +void +PubRestrictedVisitor::visit (HIR::UseDeclaration &use_decl) +{ + is_restriction_valid (use_decl.get_mappings ().get_nodeid (), + use_decl.get_locus ()); +} + +void +PubRestrictedVisitor::visit (HIR::Function &func) +{ + is_restriction_valid (func.get_mappings ().get_nodeid (), func.get_locus ()); +} + +void +PubRestrictedVisitor::visit (HIR::TypeAlias &type_alias) +{ + is_restriction_valid (type_alias.get_mappings ().get_nodeid (), + type_alias.get_locus ()); +} + +void +PubRestrictedVisitor::visit (HIR::StructStruct &struct_item) +{ + is_restriction_valid (struct_item.get_mappings ().get_nodeid (), + struct_item.get_locus ()); + // FIXME: Check fields here as well +} + +void +PubRestrictedVisitor::visit (HIR::TupleStruct &tuple_struct) +{ + is_restriction_valid (tuple_struct.get_mappings ().get_nodeid (), + tuple_struct.get_locus ()); + // FIXME: Check fields here as well +} + +void +PubRestrictedVisitor::visit (HIR::Enum &enum_item) +{ + is_restriction_valid (enum_item.get_mappings ().get_nodeid (), + enum_item.get_locus ()); +} + +void +PubRestrictedVisitor::visit (HIR::Union &union_item) +{ + is_restriction_valid (union_item.get_mappings ().get_nodeid (), + union_item.get_locus ()); +} + +void +PubRestrictedVisitor::visit (HIR::ConstantItem &const_item) +{ + is_restriction_valid (const_item.get_mappings ().get_nodeid (), + const_item.get_locus ()); +} + +void +PubRestrictedVisitor::visit (HIR::StaticItem &static_item) +{ + is_restriction_valid (static_item.get_mappings ().get_nodeid (), + static_item.get_locus ()); +} + +void +PubRestrictedVisitor::visit (HIR::Trait &trait) +{ + is_restriction_valid (trait.get_mappings ().get_nodeid (), + trait.get_locus ()); +} + +void +PubRestrictedVisitor::visit (HIR::ImplBlock &impl) +{ + is_restriction_valid (impl.get_mappings ().get_nodeid (), impl.get_locus ()); +} + +void +PubRestrictedVisitor::visit (HIR::ExternBlock &block) +{ + is_restriction_valid (block.get_mappings ().get_nodeid (), + block.get_locus ()); +} + +} // namespace Privacy +} // namespace Rust diff --git a/gcc/rust/checks/errors/privacy/rust-pub-restricted-visitor.h b/gcc/rust/checks/errors/privacy/rust-pub-restricted-visitor.h new file mode 100644 index 00000000000..2685f3d1488 --- /dev/null +++ b/gcc/rust/checks/errors/privacy/rust-pub-restricted-visitor.h @@ -0,0 +1,120 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#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 )`) 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 module_stack; + + // FIXME: Do we have to handle `self` and `super` as part of the name + // resolution pass? + + Analysis::Mappings &mappings; +}; + +} // namespace Privacy +} // namespace Rust + +#endif // !RUST_PUB_RESTRICTED_VISITOR_H diff --git a/gcc/rust/checks/errors/privacy/rust-reachability.cc b/gcc/rust/checks/errors/privacy/rust-reachability.cc new file mode 100644 index 00000000000..b322e29bfc3 --- /dev/null +++ b/gcc/rust/checks/errors/privacy/rust-reachability.cc @@ -0,0 +1,236 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-reachability.h" +#include "rust-tyty.h" + +namespace Rust { +namespace Privacy { + +static HIR::VisItem * +maybe_get_vis_item (std::unique_ptr &item) +{ + if (item->get_hir_kind () != HIR::Node::VIS_ITEM) + return nullptr; + + return static_cast (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> &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 (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 (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 (variant.get ()); + for (const auto &field : struct_variant->get_struct_fields ()) + ctx.update_reachability (field.get_mappings (), variant_reach); + break; + } + // Nothing nested to visit in that case + case HIR::EnumItem::Named: + case HIR::EnumItem::Discriminant: + break; + } + } +} + +void +ReachabilityVisitor::visit (HIR::Union &union_item) +{ + auto union_reach = get_reachability_level (union_item.get_visibility ()); + + union_reach + = ctx.update_reachability (union_item.get_mappings (), union_reach); + visit_generic_predicates (union_item.get_generic_params (), union_reach); +} + +void +ReachabilityVisitor::visit (HIR::ConstantItem &const_item) +{ + auto reach = get_reachability_level (const_item.get_visibility ()); + reach = ctx.update_reachability (const_item.get_mappings (), reach); +} + +void +ReachabilityVisitor::visit (HIR::StaticItem &static_item) +{ + auto reach = get_reachability_level (static_item.get_visibility ()); + reach = ctx.update_reachability (static_item.get_mappings (), reach); +} + +void +ReachabilityVisitor::visit (HIR::Trait &trait) +{ + auto trait_reach = get_reachability_level (trait.get_visibility ()); + + trait_reach = ctx.update_reachability (trait.get_mappings (), trait_reach); + visit_generic_predicates (trait.get_generic_params (), trait_reach); +} + +void +ReachabilityVisitor::visit (HIR::ImplBlock &impl) +{ + auto impl_reach = get_reachability_level (impl.get_visibility ()); + + impl_reach = ctx.update_reachability (impl.get_mappings (), impl_reach); + visit_generic_predicates (impl.get_generic_params (), impl_reach); +} + +void +ReachabilityVisitor::visit (HIR::ExternBlock &block) +{} + +// FIXME: How can we visit Blocks in the current configuration? Have a full +// visitor? +} // namespace Privacy +} // namespace Rust diff --git a/gcc/rust/checks/errors/privacy/rust-reachability.h b/gcc/rust/checks/errors/privacy/rust-reachability.h new file mode 100644 index 00000000000..e0bc4f5f0b8 --- /dev/null +++ b/gcc/rust/checks/errors/privacy/rust-reachability.h @@ -0,0 +1,87 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#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> &generics, + ReachLevel item_reach); + + /** + * Get the initial reach level for an item based on its visibility. + */ + ReachLevel get_reachability_level (const HIR::Visibility &item_visibility); + + virtual void visit (HIR::Module &mod); + virtual void visit (HIR::ExternCrate &crate); + virtual void visit (HIR::UseDeclaration &use_decl); + virtual void visit (HIR::Function &func); + virtual void visit (HIR::TypeAlias &type_alias); + virtual void visit (HIR::StructStruct &struct_item); + virtual void visit (HIR::TupleStruct &tuple_struct); + virtual void visit (HIR::Enum &enum_item); + virtual void visit (HIR::Union &union_item); + virtual void visit (HIR::ConstantItem &const_item); + virtual void visit (HIR::StaticItem &static_item); + virtual void visit (HIR::Trait &trait); + virtual void visit (HIR::ImplBlock &impl); + virtual void visit (HIR::ExternBlock &block); + +private: + ReachLevel current_level; + PrivacyContext &ctx; + const ::Rust::Resolver::TypeCheckContext &ty_ctx; +}; +} // namespace Privacy +} // namespace Rust + +#endif // !RUST_REACHABILITY_H diff --git a/gcc/rust/checks/errors/privacy/rust-visibility-resolver.cc b/gcc/rust/checks/errors/privacy/rust-visibility-resolver.cc new file mode 100644 index 00000000000..301182754a4 --- /dev/null +++ b/gcc/rust/checks/errors/privacy/rust-visibility-resolver.cc @@ -0,0 +1,245 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#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 (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 (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 (item.get ()); + break; + case HIR::ImplItem::TYPE_ALIAS: + vis_item = static_cast (item.get ()); + break; + case HIR::ImplItem::CONSTANT: + vis_item = static_cast (item.get ()); + break; + default: + gcc_unreachable (); + return; + } + vis_item->accept_vis (*this); + } +} + +void +VisibilityResolver::visit (HIR::ExternBlock &block) +{} + +} // namespace Privacy +} // namespace Rust diff --git a/gcc/rust/checks/errors/privacy/rust-visibility-resolver.h b/gcc/rust/checks/errors/privacy/rust-visibility-resolver.h new file mode 100644 index 00000000000..20a581c16d4 --- /dev/null +++ b/gcc/rust/checks/errors/privacy/rust-visibility-resolver.h @@ -0,0 +1,103 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#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