From 1657ee53d578d68c7d807312b1063ffd804d7ef9 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Sun, 31 Oct 2021 19:59:37 -0400 Subject: [PATCH] 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 --- gcc/rust/ast/rust-ast-full-test.cc | 71 +++++------------- gcc/rust/ast/rust-item.h | 11 ++- gcc/rust/parse/rust-parse-impl.h | 8 +- gcc/rust/parse/rust-parse.cc | 73 +++++++++++++++++++ gcc/rust/parse/rust-parse.h | 19 +++++ .../rust/compile/missing_middle/both_path.rs | 3 + .../compile/missing_middle/explicit.not.rs | 1 + .../rust/compile/missing_middle/inner_path.rs | 3 + .../rust/compile/missing_middle/other.rs | 3 + .../rust/compile/missing_middle/outer_path.rs | 3 + .../rust/compile/missing_middle/sub/mod.rs | 3 + .../rust/compile/mod_missing_middle.rs | 29 ++++++++ 12 files changed, 169 insertions(+), 58 deletions(-) create mode 100644 gcc/testsuite/rust/compile/missing_middle/both_path.rs create mode 100644 gcc/testsuite/rust/compile/missing_middle/explicit.not.rs create mode 100644 gcc/testsuite/rust/compile/missing_middle/inner_path.rs create mode 100644 gcc/testsuite/rust/compile/missing_middle/other.rs create mode 100644 gcc/testsuite/rust/compile/missing_middle/outer_path.rs create mode 100644 gcc/testsuite/rust/compile/missing_middle/sub/mod.rs create mode 100644 gcc/testsuite/rust/compile/mod_missing_middle.rs diff --git a/gcc/rust/ast/rust-ast-full-test.cc b/gcc/rust/ast/rust-ast-full-test.cc index a189d8c029be..87808536e07e 100644 --- a/gcc/rust/ast/rust-ast-full-test.cc +++ b/gcc/rust/ast/rust-ast-full-test.cc @@ -3984,53 +3984,11 @@ file_exists (const std::string path) static std::string filename_from_path_attribute (std::vector &outer_attrs) { - Attribute path_attr = Attribute::create_empty (); - for (auto attr : outer_attrs) - { - if (attr.get_path ().as_string () == "path") - { - path_attr = attr; - break; - } - } - - // We didn't find a path attribute. This is not an error, there simply isn't - // one present - if (path_attr.is_empty ()) - return ""; - - // Here, we found a path attribute, but it has no associated string. This is - // invalid - if (!path_attr.has_attr_input ()) - { - rust_error_at ( - path_attr.get_locus (), - // Split the format string so that -Wformat-diag does not complain... - "path attributes must contain a filename: '%s'", "#[path = \"file\"]"); - return ""; - } - - auto path_value = path_attr.get_attr_input ().as_string (); - - // At this point, the 'path' is of the following format: '= ""' - // We need to remove the equal sign and only keep the actual filename. - // In order to do this, we can simply go through the string until we find - // a character that is not an equal sign or whitespace - auto filename_begin = path_value.find_first_not_of ("=\t "); - - auto path = path_value.substr (filename_begin); - - // On windows, the path might mix '/' and '\' separators. Replace the - // UNIX-like separators by MSDOS separators to make sure the path will resolve - // properly. - // - // Source: rustc compiler - // (https://github.com/rust-lang/rust/blob/9863bf51a52b8e61bcad312f81b5193d53099f9f/compiler/rustc_expand/src/module.rs#L174) -#if defined(HAVE_DOS_BASED_FILE_SYSTEM) - path.replace ('/', '\\'); -#endif /* HAVE_DOS_BASED_FILE_SYSTEM */ - - return path; + // An out-of-line module cannot have inner attributes. Additionally, the + // default name is specified as `""` so that the caller can detect the case + // of "no path given" and use the default path logic (`name.rs` or + // `name/mod.rs`). + return extract_module_path ({}, outer_attrs, ""); } void @@ -4057,6 +4015,13 @@ Module::process_file_path () current_directory_name = including_fname.substr (0, dir_slash_pos) + file_separator; + // Handle inline module declarations adding path components. + for (auto const &name : module_scope) + { + current_directory_name.append (name); + current_directory_name.append (file_separator); + } + auto path_string = filename_from_path_attribute (get_outer_attrs ()); if (!path_string.empty ()) { @@ -4070,12 +4035,13 @@ Module::process_file_path () // current file is titled `mod.rs`. // First, we search for /.rs - bool file_mod_found - = file_exists (current_directory_name + expected_file_path); + std::string file_mod_path = current_directory_name + expected_file_path; + bool file_mod_found = file_exists (file_mod_path); // Then, search for //mod.rs - current_directory_name += module_name + file_separator; - bool dir_mod_found = file_exists (current_directory_name + expected_dir_path); + std::string dir_mod_path + = current_directory_name + module_name + file_separator + expected_dir_path; + bool dir_mod_found = file_exists (dir_mod_path); bool multiple_candidates_found = file_mod_found && dir_mod_found; bool no_candidates_found = !file_mod_found && !dir_mod_found; @@ -4093,8 +4059,7 @@ Module::process_file_path () if (no_candidates_found || multiple_candidates_found) return; - module_file = file_mod_found ? expected_file_path - : current_directory_name + expected_dir_path; + module_file = std::move (file_mod_found ? file_mod_path : dir_mod_path); } void diff --git a/gcc/rust/ast/rust-item.h b/gcc/rust/ast/rust-item.h index 39411f8adedf..f952dccf3b18 100644 --- a/gcc/rust/ast/rust-item.h +++ b/gcc/rust/ast/rust-item.h @@ -991,6 +991,8 @@ class Module : public VisItem std::vector inner_attrs; // bool has_items; std::vector> items; + // Names of including inline modules (immediate parent is last in the list) + std::vector module_scope; // Filename the module refers to. Empty string on LOADED modules or if an // error occured when dealing with UNLOADED modules @@ -1013,11 +1015,12 @@ class Module : public VisItem // Unloaded module constructor Module (Identifier module_name, Visibility visibility, std::vector outer_attrs, Location locus, - std::string outer_filename) + std::string outer_filename, std::vector module_scope) : VisItem (std::move (visibility), std::move (outer_attrs)), module_name (module_name), locus (locus), kind (ModuleKind::UNLOADED), outer_filename (outer_filename), inner_attrs (std::vector ()), - items (std::vector> ()) + items (std::vector> ()), + module_scope (std::move (module_scope)) {} // Loaded module constructor, with items @@ -1035,7 +1038,8 @@ class Module : public VisItem // Copy constructor with vector clone Module (Module const &other) : VisItem (other), module_name (other.module_name), locus (other.locus), - kind (other.kind), inner_attrs (other.inner_attrs) + kind (other.kind), inner_attrs (other.inner_attrs), + module_scope (other.module_scope) { // We need to check whether we are copying a loaded module or an unloaded // one. In the second case, clear the `items` vector. @@ -1054,6 +1058,7 @@ class Module : public VisItem locus = other.locus; kind = other.kind; inner_attrs = other.inner_attrs; + module_scope = other.module_scope; // Likewise, we need to clear the `items` vector in case the other module is // unloaded diff --git a/gcc/rust/parse/rust-parse-impl.h b/gcc/rust/parse/rust-parse-impl.h index 52aba4f22485..7f0db1b4d1f7 100644 --- a/gcc/rust/parse/rust-parse-impl.h +++ b/gcc/rust/parse/rust-parse-impl.h @@ -2104,14 +2104,18 @@ Parser::parse_module (AST::Visibility vis, // Construct an external module return std::unique_ptr ( new AST::Module (std::move (name), std::move (vis), - std::move (outer_attrs), locus, - lexer.get_filename ())); + std::move (outer_attrs), locus, lexer.get_filename (), + inline_module_stack)); case LEFT_CURLY: { lexer.skip_token (); // parse inner attributes AST::AttrVec inner_attrs = parse_inner_attributes (); + std::string module_path_name + = extract_module_path (inner_attrs, outer_attrs, name); + InlineModuleStackScope scope (*this, std::move (module_path_name)); + // parse items std::vector> items; const_TokenPtr tok = lexer.peek_token (); diff --git a/gcc/rust/parse/rust-parse.cc b/gcc/rust/parse/rust-parse.cc index fdad97c52dd0..e78de5164052 100644 --- a/gcc/rust/parse/rust-parse.cc +++ b/gcc/rust/parse/rust-parse.cc @@ -46,4 +46,77 @@ along with GCC; see the file COPYING3. If not see namespace Rust { +std::string +extract_module_path (const AST::AttrVec &inner_attrs, + const AST::AttrVec &outer_attrs, const std::string &name) +{ + AST::Attribute path_attr = AST::Attribute::create_empty (); + for (const auto &attr : inner_attrs) + { + if (attr.get_path ().as_string () == "path") + { + path_attr = attr; + break; + } + } + + // Here, we found a path attribute, but it has no associated string. This is + // invalid + if (!path_attr.is_empty () && !path_attr.has_attr_input ()) + { + rust_error_at ( + path_attr.get_locus (), + // Split the format string so that -Wformat-diag does not complain... + "path attributes must contain a filename: '%s'", "#![path = \"file\"]"); + return name; + } + + for (const auto &attr : outer_attrs) + { + if (attr.get_path ().as_string () == "path") + { + path_attr = attr; + break; + } + } + + // We didn't find a path attribute. This is not an error, there simply isn't + // one present + if (path_attr.is_empty ()) + return name; + + // Here, we found a path attribute, but it has no associated string. This is + // invalid + if (!path_attr.has_attr_input ()) + { + rust_error_at ( + path_attr.get_locus (), + // Split the format string so that -Wformat-diag does not complain... + "path attributes must contain a filename: '%s'", "#[path = \"file\"]"); + return name; + } + + auto path_value = path_attr.get_attr_input ().as_string (); + + // At this point, the 'path' is of the following format: '= ""' + // We need to remove the equal sign and only keep the actual filename. + // In order to do this, we can simply go through the string until we find + // a character that is not an equal sign or whitespace + auto filename_begin = path_value.find_first_not_of ("=\t "); + + auto path = path_value.substr (filename_begin); + + // On windows, the path might mix '/' and '\' separators. Replace the + // UNIX-like separators by MSDOS separators to make sure the path will resolve + // properly. + // + // Source: rustc compiler + // (https://github.com/rust-lang/rust/blob/9863bf51a52b8e61bcad312f81b5193d53099f9f/compiler/rustc_expand/src/module.rs#L174) +#if defined(HAVE_DOS_BASED_FILE_SYSTEM) + path.replace ('/', '\\'); +#endif /* HAVE_DOS_BASED_FILE_SYSTEM */ + + return path; +} + } // namespace Rust diff --git a/gcc/rust/parse/rust-parse.h b/gcc/rust/parse/rust-parse.h index 45f00f570534..acab7ff4a833 100644 --- a/gcc/rust/parse/rust-parse.h +++ b/gcc/rust/parse/rust-parse.h @@ -622,7 +622,26 @@ template class Parser ManagedTokenSource lexer; // The error list. std::vector error_table; + // The names of inline modules while parsing. + std::vector inline_module_stack; + + class InlineModuleStackScope + { + private: + Parser &parser; + + public: + InlineModuleStackScope (Parser &parser, std::string name) : parser (parser) + { + parser.inline_module_stack.emplace_back (std::move (name)); + } + ~InlineModuleStackScope () { parser.inline_module_stack.pop_back (); } + }; }; + +std::string +extract_module_path (const AST::AttrVec &inner_attrs, + const AST::AttrVec &outer_attrs, const std::string &name); } // namespace Rust // as now template, include implementations of all methods diff --git a/gcc/testsuite/rust/compile/missing_middle/both_path.rs b/gcc/testsuite/rust/compile/missing_middle/both_path.rs new file mode 100644 index 000000000000..5e5ad15457aa --- /dev/null +++ b/gcc/testsuite/rust/compile/missing_middle/both_path.rs @@ -0,0 +1,3 @@ +pub fn f() -> u32 { + 5 +} diff --git a/gcc/testsuite/rust/compile/missing_middle/explicit.not.rs b/gcc/testsuite/rust/compile/missing_middle/explicit.not.rs new file mode 100644 index 000000000000..e28288b0f995 --- /dev/null +++ b/gcc/testsuite/rust/compile/missing_middle/explicit.not.rs @@ -0,0 +1 @@ +mod other; diff --git a/gcc/testsuite/rust/compile/missing_middle/inner_path.rs b/gcc/testsuite/rust/compile/missing_middle/inner_path.rs new file mode 100644 index 000000000000..daf4e3cc25ae --- /dev/null +++ b/gcc/testsuite/rust/compile/missing_middle/inner_path.rs @@ -0,0 +1,3 @@ +pub fn f() -> u32 { + 4 +} diff --git a/gcc/testsuite/rust/compile/missing_middle/other.rs b/gcc/testsuite/rust/compile/missing_middle/other.rs new file mode 100644 index 000000000000..0c0884ec968e --- /dev/null +++ b/gcc/testsuite/rust/compile/missing_middle/other.rs @@ -0,0 +1,3 @@ +pub fn f() -> u32 { + 2 +} diff --git a/gcc/testsuite/rust/compile/missing_middle/outer_path.rs b/gcc/testsuite/rust/compile/missing_middle/outer_path.rs new file mode 100644 index 000000000000..fbe5074191bd --- /dev/null +++ b/gcc/testsuite/rust/compile/missing_middle/outer_path.rs @@ -0,0 +1,3 @@ +pub fn f() -> u32 { + 3 +} diff --git a/gcc/testsuite/rust/compile/missing_middle/sub/mod.rs b/gcc/testsuite/rust/compile/missing_middle/sub/mod.rs new file mode 100644 index 000000000000..f099d61e04a2 --- /dev/null +++ b/gcc/testsuite/rust/compile/missing_middle/sub/mod.rs @@ -0,0 +1,3 @@ +pub fn f() -> u32 { + 1 +} diff --git a/gcc/testsuite/rust/compile/mod_missing_middle.rs b/gcc/testsuite/rust/compile/mod_missing_middle.rs new file mode 100644 index 000000000000..796334076711 --- /dev/null +++ b/gcc/testsuite/rust/compile/mod_missing_middle.rs @@ -0,0 +1,29 @@ +// { dg-additional-options "-w" } + +mod missing_middle { + mod sub; + + #[path = "explicit.not.rs"] + mod explicit; +} + +#[path = "missing_middle"] +mod with_outer_path_attr { + #[path = "outer_path.rs"] + mod inner; +} + +mod with_inner_path_attr { + #![path = "missing_middle"] + + #[path = "inner_path.rs"] + mod inner; +} + +#[path = "missing_middle"] +mod with_both_path_attr { + #![path = "this_is_ignored"] + + #[path = "both_path.rs"] + mod inner; +}