Skip to content

Commit 1657ee5

Browse files
committed
rust: track inline module scopes for module file resolution
The set of inline modules is required to find the expected location of a module file. Track this information with an RAII object (`InlineModuleStackScope`) and pass it down to any out-of-line modules so that, when requested, the set of inline modules can be added to the search path. Signed-off-by: Ben Boeckel <[email protected]>
1 parent 09af9b1 commit 1657ee5

File tree

12 files changed

+169
-58
lines changed

12 files changed

+169
-58
lines changed

gcc/rust/ast/rust-ast-full-test.cc

+18-53
Original file line numberDiff line numberDiff line change
@@ -3984,53 +3984,11 @@ file_exists (const std::string path)
39843984
static std::string
39853985
filename_from_path_attribute (std::vector<Attribute> &outer_attrs)
39863986
{
3987-
Attribute path_attr = Attribute::create_empty ();
3988-
for (auto attr : outer_attrs)
3989-
{
3990-
if (attr.get_path ().as_string () == "path")
3991-
{
3992-
path_attr = attr;
3993-
break;
3994-
}
3995-
}
3996-
3997-
// We didn't find a path attribute. This is not an error, there simply isn't
3998-
// one present
3999-
if (path_attr.is_empty ())
4000-
return "";
4001-
4002-
// Here, we found a path attribute, but it has no associated string. This is
4003-
// invalid
4004-
if (!path_attr.has_attr_input ())
4005-
{
4006-
rust_error_at (
4007-
path_attr.get_locus (),
4008-
// Split the format string so that -Wformat-diag does not complain...
4009-
"path attributes must contain a filename: '%s'", "#[path = \"file\"]");
4010-
return "";
4011-
}
4012-
4013-
auto path_value = path_attr.get_attr_input ().as_string ();
4014-
4015-
// At this point, the 'path' is of the following format: '= "<file.rs>"'
4016-
// We need to remove the equal sign and only keep the actual filename.
4017-
// In order to do this, we can simply go through the string until we find
4018-
// a character that is not an equal sign or whitespace
4019-
auto filename_begin = path_value.find_first_not_of ("=\t ");
4020-
4021-
auto path = path_value.substr (filename_begin);
4022-
4023-
// On windows, the path might mix '/' and '\' separators. Replace the
4024-
// UNIX-like separators by MSDOS separators to make sure the path will resolve
4025-
// properly.
4026-
//
4027-
// Source: rustc compiler
4028-
// (https://github.com/rust-lang/rust/blob/9863bf51a52b8e61bcad312f81b5193d53099f9f/compiler/rustc_expand/src/module.rs#L174)
4029-
#if defined(HAVE_DOS_BASED_FILE_SYSTEM)
4030-
path.replace ('/', '\\');
4031-
#endif /* HAVE_DOS_BASED_FILE_SYSTEM */
4032-
4033-
return path;
3987+
// An out-of-line module cannot have inner attributes. Additionally, the
3988+
// default name is specified as `""` so that the caller can detect the case
3989+
// of "no path given" and use the default path logic (`name.rs` or
3990+
// `name/mod.rs`).
3991+
return extract_module_path ({}, outer_attrs, "");
40343992
}
40353993

40363994
void
@@ -4057,6 +4015,13 @@ Module::process_file_path ()
40574015
current_directory_name
40584016
= including_fname.substr (0, dir_slash_pos) + file_separator;
40594017

4018+
// Handle inline module declarations adding path components.
4019+
for (auto const &name : module_scope)
4020+
{
4021+
current_directory_name.append (name);
4022+
current_directory_name.append (file_separator);
4023+
}
4024+
40604025
auto path_string = filename_from_path_attribute (get_outer_attrs ());
40614026
if (!path_string.empty ())
40624027
{
@@ -4070,12 +4035,13 @@ Module::process_file_path ()
40704035
// current file is titled `mod.rs`.
40714036

40724037
// First, we search for <directory>/<module_name>.rs
4073-
bool file_mod_found
4074-
= file_exists (current_directory_name + expected_file_path);
4038+
std::string file_mod_path = current_directory_name + expected_file_path;
4039+
bool file_mod_found = file_exists (file_mod_path);
40754040

40764041
// Then, search for <directory>/<module_name>/mod.rs
4077-
current_directory_name += module_name + file_separator;
4078-
bool dir_mod_found = file_exists (current_directory_name + expected_dir_path);
4042+
std::string dir_mod_path
4043+
= current_directory_name + module_name + file_separator + expected_dir_path;
4044+
bool dir_mod_found = file_exists (dir_mod_path);
40794045

40804046
bool multiple_candidates_found = file_mod_found && dir_mod_found;
40814047
bool no_candidates_found = !file_mod_found && !dir_mod_found;
@@ -4093,8 +4059,7 @@ Module::process_file_path ()
40934059
if (no_candidates_found || multiple_candidates_found)
40944060
return;
40954061

4096-
module_file = file_mod_found ? expected_file_path
4097-
: current_directory_name + expected_dir_path;
4062+
module_file = std::move (file_mod_found ? file_mod_path : dir_mod_path);
40984063
}
40994064

41004065
void

gcc/rust/ast/rust-item.h

+8-3
Original file line numberDiff line numberDiff line change
@@ -991,6 +991,8 @@ class Module : public VisItem
991991
std::vector<Attribute> inner_attrs;
992992
// bool has_items;
993993
std::vector<std::unique_ptr<Item>> items;
994+
// Names of including inline modules (immediate parent is last in the list)
995+
std::vector<std::string> module_scope;
994996

995997
// Filename the module refers to. Empty string on LOADED modules or if an
996998
// error occured when dealing with UNLOADED modules
@@ -1013,11 +1015,12 @@ class Module : public VisItem
10131015
// Unloaded module constructor
10141016
Module (Identifier module_name, Visibility visibility,
10151017
std::vector<Attribute> outer_attrs, Location locus,
1016-
std::string outer_filename)
1018+
std::string outer_filename, std::vector<std::string> module_scope)
10171019
: VisItem (std::move (visibility), std::move (outer_attrs)),
10181020
module_name (module_name), locus (locus), kind (ModuleKind::UNLOADED),
10191021
outer_filename (outer_filename), inner_attrs (std::vector<Attribute> ()),
1020-
items (std::vector<std::unique_ptr<Item>> ())
1022+
items (std::vector<std::unique_ptr<Item>> ()),
1023+
module_scope (std::move (module_scope))
10211024
{}
10221025

10231026
// Loaded module constructor, with items
@@ -1035,7 +1038,8 @@ class Module : public VisItem
10351038
// Copy constructor with vector clone
10361039
Module (Module const &other)
10371040
: VisItem (other), module_name (other.module_name), locus (other.locus),
1038-
kind (other.kind), inner_attrs (other.inner_attrs)
1041+
kind (other.kind), inner_attrs (other.inner_attrs),
1042+
module_scope (other.module_scope)
10391043
{
10401044
// We need to check whether we are copying a loaded module or an unloaded
10411045
// one. In the second case, clear the `items` vector.
@@ -1054,6 +1058,7 @@ class Module : public VisItem
10541058
locus = other.locus;
10551059
kind = other.kind;
10561060
inner_attrs = other.inner_attrs;
1061+
module_scope = other.module_scope;
10571062

10581063
// Likewise, we need to clear the `items` vector in case the other module is
10591064
// unloaded

gcc/rust/parse/rust-parse-impl.h

+6-2
Original file line numberDiff line numberDiff line change
@@ -2104,14 +2104,18 @@ Parser<ManagedTokenSource>::parse_module (AST::Visibility vis,
21042104
// Construct an external module
21052105
return std::unique_ptr<AST::Module> (
21062106
new AST::Module (std::move (name), std::move (vis),
2107-
std::move (outer_attrs), locus,
2108-
lexer.get_filename ()));
2107+
std::move (outer_attrs), locus, lexer.get_filename (),
2108+
inline_module_stack));
21092109
case LEFT_CURLY: {
21102110
lexer.skip_token ();
21112111

21122112
// parse inner attributes
21132113
AST::AttrVec inner_attrs = parse_inner_attributes ();
21142114

2115+
std::string module_path_name
2116+
= extract_module_path (inner_attrs, outer_attrs, name);
2117+
InlineModuleStackScope scope (*this, std::move (module_path_name));
2118+
21152119
// parse items
21162120
std::vector<std::unique_ptr<AST::Item>> items;
21172121
const_TokenPtr tok = lexer.peek_token ();

gcc/rust/parse/rust-parse.cc

+73
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,77 @@ along with GCC; see the file COPYING3. If not see
4646

4747
namespace Rust {
4848

49+
std::string
50+
extract_module_path (const AST::AttrVec &inner_attrs,
51+
const AST::AttrVec &outer_attrs, const std::string &name)
52+
{
53+
AST::Attribute path_attr = AST::Attribute::create_empty ();
54+
for (const auto &attr : inner_attrs)
55+
{
56+
if (attr.get_path ().as_string () == "path")
57+
{
58+
path_attr = attr;
59+
break;
60+
}
61+
}
62+
63+
// Here, we found a path attribute, but it has no associated string. This is
64+
// invalid
65+
if (!path_attr.is_empty () && !path_attr.has_attr_input ())
66+
{
67+
rust_error_at (
68+
path_attr.get_locus (),
69+
// Split the format string so that -Wformat-diag does not complain...
70+
"path attributes must contain a filename: '%s'", "#![path = \"file\"]");
71+
return name;
72+
}
73+
74+
for (const auto &attr : outer_attrs)
75+
{
76+
if (attr.get_path ().as_string () == "path")
77+
{
78+
path_attr = attr;
79+
break;
80+
}
81+
}
82+
83+
// We didn't find a path attribute. This is not an error, there simply isn't
84+
// one present
85+
if (path_attr.is_empty ())
86+
return name;
87+
88+
// Here, we found a path attribute, but it has no associated string. This is
89+
// invalid
90+
if (!path_attr.has_attr_input ())
91+
{
92+
rust_error_at (
93+
path_attr.get_locus (),
94+
// Split the format string so that -Wformat-diag does not complain...
95+
"path attributes must contain a filename: '%s'", "#[path = \"file\"]");
96+
return name;
97+
}
98+
99+
auto path_value = path_attr.get_attr_input ().as_string ();
100+
101+
// At this point, the 'path' is of the following format: '= "<file.rs>"'
102+
// We need to remove the equal sign and only keep the actual filename.
103+
// In order to do this, we can simply go through the string until we find
104+
// a character that is not an equal sign or whitespace
105+
auto filename_begin = path_value.find_first_not_of ("=\t ");
106+
107+
auto path = path_value.substr (filename_begin);
108+
109+
// On windows, the path might mix '/' and '\' separators. Replace the
110+
// UNIX-like separators by MSDOS separators to make sure the path will resolve
111+
// properly.
112+
//
113+
// Source: rustc compiler
114+
// (https://github.com/rust-lang/rust/blob/9863bf51a52b8e61bcad312f81b5193d53099f9f/compiler/rustc_expand/src/module.rs#L174)
115+
#if defined(HAVE_DOS_BASED_FILE_SYSTEM)
116+
path.replace ('/', '\\');
117+
#endif /* HAVE_DOS_BASED_FILE_SYSTEM */
118+
119+
return path;
120+
}
121+
49122
} // namespace Rust

gcc/rust/parse/rust-parse.h

+19
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,26 @@ template <typename ManagedTokenSource> class Parser
622622
ManagedTokenSource lexer;
623623
// The error list.
624624
std::vector<Error> error_table;
625+
// The names of inline modules while parsing.
626+
std::vector<std::string> inline_module_stack;
627+
628+
class InlineModuleStackScope
629+
{
630+
private:
631+
Parser &parser;
632+
633+
public:
634+
InlineModuleStackScope (Parser &parser, std::string name) : parser (parser)
635+
{
636+
parser.inline_module_stack.emplace_back (std::move (name));
637+
}
638+
~InlineModuleStackScope () { parser.inline_module_stack.pop_back (); }
639+
};
625640
};
641+
642+
std::string
643+
extract_module_path (const AST::AttrVec &inner_attrs,
644+
const AST::AttrVec &outer_attrs, const std::string &name);
626645
} // namespace Rust
627646

628647
// as now template, include implementations of all methods
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pub fn f() -> u32 {
2+
5
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
mod other;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pub fn f() -> u32 {
2+
4
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pub fn f() -> u32 {
2+
2
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pub fn f() -> u32 {
2+
3
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pub fn f() -> u32 {
2+
1
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// { dg-additional-options "-w" }
2+
3+
mod missing_middle {
4+
mod sub;
5+
6+
#[path = "explicit.not.rs"]
7+
mod explicit;
8+
}
9+
10+
#[path = "missing_middle"]
11+
mod with_outer_path_attr {
12+
#[path = "outer_path.rs"]
13+
mod inner;
14+
}
15+
16+
mod with_inner_path_attr {
17+
#![path = "missing_middle"]
18+
19+
#[path = "inner_path.rs"]
20+
mod inner;
21+
}
22+
23+
#[path = "missing_middle"]
24+
mod with_both_path_attr {
25+
#![path = "this_is_ignored"]
26+
27+
#[path = "both_path.rs"]
28+
mod inner;
29+
}

0 commit comments

Comments
 (0)