// Copyright (C) 2020-2025 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-ast-resolve-path.h" #include "rust-ast-resolve-type.h" #include "rust-path.h" namespace Rust { namespace Resolver { ResolvePath::ResolvePath () : ResolverBase () {} NodeId ResolvePath::go (AST::PathInExpression &expr) { ResolvePath resolver; return resolver.resolve_path (expr); } NodeId ResolvePath::go (AST::QualifiedPathInExpression &expr) { ResolvePath resolver; return resolver.resolve_path (expr); } NodeId ResolvePath::go (AST::SimplePath &expr) { ResolvePath resolver; return resolver.resolve_path (expr); } NodeId ResolvePath::resolve_path (AST::PathInExpression &expr) { NodeId resolved_node_id = UNKNOWN_NODEID; NodeId module_scope_id = resolver->peek_current_module_scope (); NodeId previous_resolved_node_id = module_scope_id; for (size_t i = 0; i < expr.get_segments ().size (); i++) { auto &segment = expr.get_segments ().at (i); const AST::PathIdentSegment &ident_seg = segment.get_ident_segment (); bool is_first_segment = i == 0; resolved_node_id = UNKNOWN_NODEID; bool in_middle_of_path = i > 0; if (in_middle_of_path && segment.is_lower_self_seg ()) { rust_error_at (segment.get_locus (), ErrorCode::E0433, "failed to resolve: %qs in paths can only be used " "in start position", segment.as_string ().c_str ()); return UNKNOWN_NODEID; } NodeId crate_scope_id = resolver->peek_crate_module_scope (); if (segment.is_crate_path_seg ()) { // what is the current crate scope node id? module_scope_id = crate_scope_id; previous_resolved_node_id = module_scope_id; resolver->insert_resolved_name (segment.get_node_id (), module_scope_id); continue; } else if (segment.is_super_path_seg ()) { if (module_scope_id == crate_scope_id) { rust_error_at (segment.get_locus (), "cannot use % at the crate scope"); return UNKNOWN_NODEID; } module_scope_id = resolver->peek_parent_module_scope (); previous_resolved_node_id = module_scope_id; resolver->insert_resolved_name (segment.get_node_id (), module_scope_id); continue; } // resolve any generic args if (segment.has_generic_args ()) ResolveGenericArgs::go (segment.get_generic_args ()); // logic is awkward here there are a few cases // // T::Default // mod::foo::impl_item // super::super::module::item // self // self::foo // self::foo::baz // // T::Default we can only resolve the T and cant do anything about Default // its dependant on associated types // // mod::foo::impl_item // we can resolve mod::foo but nothing about impl_item but we need to // _always resolve generic arguments // // self is a simple single lookup // // we have module_scope_id for the next module_scope to lookup // resolved_node_id is the thing we have resolve this segment to // // new algo? // we can only use module resolution when the previous segment is either // unknown or equal to this module_scope_id // // can only use old resolution when previous segment is unkown if (is_first_segment) { // name scope first NodeId resolved_node = UNKNOWN_NODEID; const CanonicalPath path = CanonicalPath::new_seg (segment.get_node_id (), ident_seg.as_string ()); if (resolver->get_name_scope ().lookup (path, &resolved_node)) { resolver->insert_resolved_name (segment.get_node_id (), resolved_node); resolved_node_id = resolved_node; } // check the type scope else if (resolver->get_type_scope ().lookup (path, &resolved_node)) { resolver->insert_resolved_type (segment.get_node_id (), resolved_node); resolved_node_id = resolved_node; } else if (segment.is_lower_self_seg ()) { module_scope_id = crate_scope_id; previous_resolved_node_id = module_scope_id; resolver->insert_resolved_name (segment.get_node_id (), module_scope_id); continue; } else { // no error handling here since we might be able to resolve via // the module hierarchy and handle errors at the end } } if (resolved_node_id == UNKNOWN_NODEID && previous_resolved_node_id == module_scope_id) { tl::optional resolved_child = mappings.lookup_module_child (module_scope_id, ident_seg.as_string ()); if (resolved_child.has_value ()) { NodeId resolved_node = resolved_child->get_node_id (); if (resolver->get_name_scope ().decl_was_declared_here ( resolved_node)) { resolved_node_id = resolved_node; resolver->insert_resolved_name (segment.get_node_id (), resolved_node); } else if (resolver->get_type_scope ().decl_was_declared_here ( resolved_node)) { resolved_node_id = resolved_node; resolver->insert_resolved_type (segment.get_node_id (), resolved_node); } else { rust_error_at (segment.get_locus (), "Cannot find path %qs in this scope", segment.as_string ().c_str ()); return UNKNOWN_NODEID; } } } bool did_resolve_segment = resolved_node_id != UNKNOWN_NODEID; if (did_resolve_segment) { if (mappings.node_is_module (resolved_node_id) || mappings.node_is_crate (resolved_node_id)) { module_scope_id = resolved_node_id; } previous_resolved_node_id = resolved_node_id; } else if (is_first_segment) { rust_error_at (segment.get_locus (), ErrorCode::E0433, "Cannot find path %qs in this scope", segment.as_string ().c_str ()); return UNKNOWN_NODEID; } } resolved_node = resolved_node_id; if (resolved_node_id != UNKNOWN_NODEID) { // name scope first if (resolver->get_name_scope ().decl_was_declared_here (resolved_node_id)) { resolver->insert_resolved_name (expr.get_node_id (), resolved_node_id); } // check the type scope else if (resolver->get_type_scope ().decl_was_declared_here ( resolved_node_id)) { resolver->insert_resolved_type (expr.get_node_id (), resolved_node_id); } else { rust_unreachable (); } } return resolved_node_id; } NodeId ResolvePath::resolve_path (AST::QualifiedPathInExpression &expr) { auto &root_segment = expr.get_qualified_path_type (); ResolveType::go (root_segment.get_type ()); if (root_segment.has_as_clause ()) ResolveType::go (root_segment.get_as_type_path ()); for (auto &segment : expr.get_segments ()) { // we cant actually do anything with the segment itself since this is all // the job of the type system to figure it out but we can resolve any // generic arguments used if (segment.has_generic_args ()) ResolveGenericArgs::go (segment.get_generic_args ()); } // cannot fully resolve a qualified path as it is dependant on associated // items return UNKNOWN_NODEID; } NodeId ResolvePath::resolve_path (AST::SimplePath &expr) { NodeId crate_scope_id = resolver->peek_crate_module_scope (); NodeId module_scope_id = resolver->peek_current_module_scope (); NodeId previous_resolved_node_id = UNKNOWN_NODEID; NodeId resolved_node_id = UNKNOWN_NODEID; for (size_t i = 0; i < expr.get_segments ().size (); i++) { AST::SimplePathSegment &segment = expr.get_segments ().at (i); bool is_first_segment = i == 0; bool is_final_segment = i >= (expr.get_segments ().size () - 1); resolved_node_id = UNKNOWN_NODEID; if (segment.is_crate_path_seg ()) { // what is the current crate scope node id? module_scope_id = crate_scope_id; previous_resolved_node_id = module_scope_id; resolver->insert_resolved_name (segment.get_node_id (), module_scope_id); resolved_node_id = module_scope_id; continue; } else if (segment.is_super_path_seg ()) { if (module_scope_id == crate_scope_id) { rust_error_at (segment.get_locus (), "cannot use % at the crate scope"); return UNKNOWN_NODEID; } module_scope_id = resolver->peek_parent_module_scope (); previous_resolved_node_id = module_scope_id; resolver->insert_resolved_name (segment.get_node_id (), module_scope_id); resolved_node_id = module_scope_id; continue; } tl::optional resolved_child = mappings.lookup_module_child (module_scope_id, segment.get_segment_name ()); if (resolved_child.has_value ()) { NodeId resolved_node = resolved_child->get_node_id (); if (resolver->get_name_scope ().decl_was_declared_here ( resolved_node)) { resolved_node_id = resolved_node; resolver->insert_resolved_name (segment.get_node_id (), resolved_node); } else if (resolver->get_type_scope ().decl_was_declared_here ( resolved_node)) { resolved_node_id = resolved_node; resolver->insert_resolved_type (segment.get_node_id (), resolved_node); } else { rust_error_at (segment.get_locus (), "Cannot find path %qs in this scope", segment.as_string ().c_str ()); return UNKNOWN_NODEID; } } if (resolved_node_id == UNKNOWN_NODEID && is_first_segment) { // name scope first NodeId resolved_node = UNKNOWN_NODEID; const CanonicalPath path = CanonicalPath::new_seg (segment.get_node_id (), segment.get_segment_name ()); if (resolver->get_name_scope ().lookup (path, &resolved_node)) { resolved_node_id = resolved_node; resolver->insert_resolved_name (segment.get_node_id (), resolved_node); } // check the type scope else if (resolver->get_type_scope ().lookup (path, &resolved_node)) { resolved_node_id = resolved_node; resolver->insert_resolved_type (segment.get_node_id (), resolved_node); } } // if we still have not resolved and this is the final segment and the // final segment is self its likely the case: pub use // // result::Result::{self, Err, Ok}; // // Then the resolved_node_id is just the previous one so long as it is a // resolved node id // rust_debug_loc (segment.get_locus (), // "trying to resolve seg: [%s] first [%s] last [%s]", // segment.get_segment_name ().c_str (), // is_first_segment ? "true" : "false", // is_final_segment ? "true" : "false"); if (resolved_node_id == UNKNOWN_NODEID && !is_first_segment && is_final_segment && segment.is_lower_self_seg ()) resolved_node_id = previous_resolved_node_id; // final check if (resolved_node_id == UNKNOWN_NODEID) { rust_error_at (segment.get_locus (), "cannot find simple path segment %qs in this scope", segment.as_string ().c_str ()); return UNKNOWN_NODEID; } if (mappings.node_is_module (resolved_node_id)) { module_scope_id = resolved_node_id; } previous_resolved_node_id = resolved_node_id; } resolved_node = resolved_node_id; if (resolved_node_id != UNKNOWN_NODEID) { // name scope first if (resolver->get_name_scope ().decl_was_declared_here (resolved_node_id)) { resolver->insert_resolved_name (expr.get_node_id (), resolved_node_id); } // check the type scope else if (resolver->get_type_scope ().decl_was_declared_here ( resolved_node_id)) { resolver->insert_resolved_type (expr.get_node_id (), resolved_node_id); } else { rust_unreachable (); } } return resolved_node_id; } } // namespace Resolver } // namespace Rust