]> git.lizzy.rs Git - rust.git/commitdiff
resolve: Implement edition hygiene for imports and absolute paths
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>
Sun, 18 Nov 2018 00:25:59 +0000 (03:25 +0300)
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>
Mon, 26 Nov 2018 21:32:30 +0000 (00:32 +0300)
Use per-span hygiene in a few other places in resolve
Prefer `rust_2015`/`rust_2018` helpers to comparing editions

17 files changed:
src/librustc/ty/context.rs
src/librustc/ty/item_path.rs
src/librustc_resolve/build_reduced_graph.rs
src/librustc_resolve/error_reporting.rs
src/librustc_resolve/lib.rs
src/librustc_resolve/macros.rs
src/libsyntax/parse/parser.rs
src/libsyntax_pos/lib.rs
src/libsyntax_pos/symbol.rs
src/test/ui-fulldeps/proc-macro/auxiliary/edition-imports-2015.rs [new file with mode: 0644]
src/test/ui-fulldeps/proc-macro/edition-imports-2018.rs [new file with mode: 0644]
src/test/ui/editions/auxiliary/absolute.rs [new file with mode: 0644]
src/test/ui/editions/auxiliary/edition-imports-2015.rs [new file with mode: 0644]
src/test/ui/editions/auxiliary/edition-imports-2018.rs [new file with mode: 0644]
src/test/ui/editions/edition-imports-2015.rs [new file with mode: 0644]
src/test/ui/editions/edition-imports-2015.stderr [new file with mode: 0644]
src/test/ui/editions/edition-imports-2018.rs [new file with mode: 0644]

index 22d5ea6e4bc938e3e682e2d6358ae511156ebbe2..d383d8375a948dd7b132bf85af124cd0f88ddeed 100644 (file)
@@ -1478,15 +1478,8 @@ pub fn all_pat_vars_are_implicit_refs_within_guards(self) -> bool {
     /// done with either: `-Ztwo-phase-borrows`, `#![feature(nll)]`,
     /// or by opting into an edition after 2015.
     pub fn two_phase_borrows(self) -> bool {
-        if self.features().nll || self.sess.opts.debugging_opts.two_phase_borrows {
-            return true;
-        }
-
-        match self.sess.edition() {
-            Edition::Edition2015 => false,
-            Edition::Edition2018 => true,
-            _ => true,
-        }
+        self.sess.rust_2018() || self.features().nll ||
+        self.sess.opts.debugging_opts.two_phase_borrows
     }
 
     /// What mode(s) of borrowck should we run? AST? MIR? both?
index f6c90ab0a1ad2b04a4bb8fb49a2cf53d56b1ac0c..350e55288ea10a24d716d373f1be9abc652bf567 100644 (file)
@@ -14,7 +14,6 @@
 use middle::cstore::{ExternCrate, ExternCrateSource};
 use syntax::ast;
 use syntax::symbol::{keywords, LocalInternedString, Symbol};
-use syntax_pos::edition::Edition;
 
 use std::cell::Cell;
 use std::fmt::Debug;
@@ -140,7 +139,7 @@ pub fn push_krate_path<T>(self, buffer: &mut T, cnum: CrateNum, pushed_prelude_c
                         debug!("push_krate_path: name={:?}", name);
                         buffer.push(&name);
                     }
-                } else if self.sess.edition() == Edition::Edition2018 && !pushed_prelude_crate {
+                } else if self.sess.rust_2018() && !pushed_prelude_crate {
                     SHOULD_PREFIX_WITH_CRATE.with(|flag| {
                         // We only add the `crate::` keyword where appropriate. In particular,
                         // when we've not previously pushed a prelude crate to this path.
index 72fe7355e4c7ca6e2e7d39b01ff4c7ba75e35019..3f0780191fb2a4c49d3f6ada05cc8ef8e6f228c3 100644 (file)
@@ -133,14 +133,17 @@ fn build_reduced_graph_for_use_tree(
         // The root is prepended lazily, when the first non-empty prefix or terminating glob
         // appears, so imports in braced groups can have roots prepended independently.
         let is_glob = if let ast::UseTreeKind::Glob = use_tree.kind { true } else { false };
-        let crate_root = if !self.session.rust_2018() &&
-                prefix_iter.peek().map_or(is_glob, |seg| !seg.ident.is_path_segment_keyword()) {
-            Some(Segment::from_ident(Ident::new(
-                keywords::CrateRoot.name(), use_tree.prefix.span.shrink_to_lo()
-            )))
-        } else {
-            None
-        };
+        let crate_root = match prefix_iter.peek() {
+            Some(seg) if !seg.ident.is_path_segment_keyword() && seg.ident.span.rust_2015() => {
+                Some(seg.ident.span.ctxt())
+            }
+            None if is_glob && use_tree.span.rust_2015() => {
+                Some(use_tree.span.ctxt())
+            }
+            _ => None,
+        }.map(|ctxt| Segment::from_ident(Ident::new(
+            keywords::CrateRoot.name(), use_tree.prefix.span.shrink_to_lo().with_ctxt(ctxt)
+        )));
 
         let prefix = crate_root.into_iter().chain(prefix_iter).collect::<Vec<_>>();
         debug!("build_reduced_graph_for_use_tree: prefix={:?}", prefix);
index 263d23d133e1cf2627340d59035e174b2bcbe8c2..e2a6303f579449e8f60e272c29b1119154c6a709 100644 (file)
@@ -33,7 +33,8 @@ pub(crate) fn make_path_suggestion(
             (Some(fst), Some(snd)) if fst.ident.name == keywords::CrateRoot.name() &&
                                       !snd.ident.is_path_segment_keyword() => {}
             // `ident::...` on 2018
-            (Some(fst), _) if self.session.rust_2018() && !fst.ident.is_path_segment_keyword() => {
+            (Some(fst), _) if fst.ident.span.rust_2018() &&
+                              !fst.ident.is_path_segment_keyword() => {
                 // Insert a placeholder that's later replaced by `self`/`super`/etc.
                 path.insert(0, Segment::from_ident(keywords::Invalid.ident()));
             }
@@ -141,7 +142,7 @@ fn make_external_crate_suggestion(
         mut path: Vec<Segment>,
         parent_scope: &ParentScope<'b>,
     ) -> Option<(Vec<Segment>, Option<String>)> {
-        if !self.session.rust_2018() {
+        if path[1].ident.span.rust_2015() {
             return None;
         }
 
index 443b1ccdef836f6828030678cc4b6e5ab4a4da21..2e7ed80c91bb31403d5646c9ec3e20e43849f429 100644 (file)
@@ -2354,14 +2354,10 @@ fn resolve_adt(&mut self, item: &Item, generics: &Generics) {
     }
 
     fn future_proof_import(&mut self, use_tree: &ast::UseTree) {
-        if !self.session.rust_2018() {
-            return;
-        }
-
         let segments = &use_tree.prefix.segments;
         if !segments.is_empty() {
             let ident = segments[0].ident;
-            if ident.is_path_segment_keyword() {
+            if ident.is_path_segment_keyword() || ident.span.rust_2015() {
                 return;
             }
 
@@ -3181,10 +3177,10 @@ fn smart_resolve_path_fragment(&mut self,
 
             // Try to lookup the name in more relaxed fashion for better error reporting.
             let ident = path.last().unwrap().ident;
-            let candidates = this.lookup_import_candidates(ident.name, ns, is_expected);
+            let candidates = this.lookup_import_candidates(ident, ns, is_expected);
             if candidates.is_empty() && is_expected(Def::Enum(DefId::local(CRATE_DEF_INDEX))) {
                 let enum_candidates =
-                    this.lookup_import_candidates(ident.name, ns, is_enum_variant);
+                    this.lookup_import_candidates(ident, ns, is_enum_variant);
                 let mut enum_candidates = enum_candidates.iter()
                     .map(|suggestion| import_candidate_to_paths(&suggestion)).collect::<Vec<_>>();
                 enum_candidates.sort();
@@ -3772,7 +3768,7 @@ fn resolve_path(
                         continue;
                     }
                     if name == keywords::Extern.name() ||
-                       name == keywords::CrateRoot.name() && self.session.rust_2018() {
+                       name == keywords::CrateRoot.name() && ident.span.rust_2018() {
                         module =
                             Some(ModuleOrUniformRoot::UniformRoot(UniformRootKind::ExternPrelude));
                         continue;
@@ -3875,7 +3871,7 @@ fn resolve_path(
                     let msg = if module_def == self.graph_root.def() {
                         let is_mod = |def| match def { Def::Mod(..) => true, _ => false };
                         let mut candidates =
-                            self.lookup_import_candidates(name, TypeNS, is_mod);
+                            self.lookup_import_candidates(ident, TypeNS, is_mod);
                         candidates.sort_by_cached_key(|c| {
                             (c.path.segments.len(), c.path.to_string())
                         });
@@ -3911,11 +3907,6 @@ fn lint_if_path_starts_with_module(
         path_span: Span,
         second_binding: Option<&NameBinding>,
     ) {
-        // In the 2018 edition this lint is a hard error, so nothing to do
-        if self.session.rust_2018() {
-            return
-        }
-
         let (diag_id, diag_span) = match crate_lint {
             CrateLint::No => return,
             CrateLint::SimplePath(id) => (id, path_span),
@@ -3924,8 +3915,9 @@ fn lint_if_path_starts_with_module(
         };
 
         let first_name = match path.get(0) {
-            Some(ident) => ident.ident.name,
-            None => return,
+            // In the 2018 edition this lint is a hard error, so nothing to do
+            Some(seg) if seg.ident.span.rust_2015() => seg.ident.name,
+            _ => return,
         };
 
         // We're only interested in `use` paths which should start with
@@ -4507,7 +4499,7 @@ fn get_traits_in_module_containing_item(&mut self,
     }
 
     fn lookup_import_candidates_from_module<FilterFn>(&mut self,
-                                          lookup_name: Name,
+                                          lookup_ident: Ident,
                                           namespace: Namespace,
                                           start_module: &'a ModuleData<'a>,
                                           crate_name: Ident,
@@ -4534,11 +4526,11 @@ fn lookup_import_candidates_from_module<FilterFn>(&mut self,
                 if !name_binding.is_importable() { return; }
 
                 // collect results based on the filter function
-                if ident.name == lookup_name && ns == namespace {
+                if ident.name == lookup_ident.name && ns == namespace {
                     if filter_fn(name_binding.def()) {
                         // create the path
                         let mut segms = path_segments.clone();
-                        if self.session.rust_2018() {
+                        if lookup_ident.span.rust_2018() {
                             // crate-local absolute paths start with `crate::` in edition 2018
                             // FIXME: may also be stabilized for Rust 2015 (Issues #45477, #44660)
                             segms.insert(
@@ -4572,7 +4564,7 @@ fn lookup_import_candidates_from_module<FilterFn>(&mut self,
 
                     let is_extern_crate_that_also_appears_in_prelude =
                         name_binding.is_extern_crate() &&
-                        self.session.rust_2018();
+                        lookup_ident.span.rust_2018();
 
                     let is_visible_to_user =
                         !in_module_is_extern || name_binding.vis == ty::Visibility::Public;
@@ -4599,16 +4591,16 @@ fn lookup_import_candidates_from_module<FilterFn>(&mut self,
     /// NOTE: The method does not look into imports, but this is not a problem,
     /// since we report the definitions (thus, the de-aliased imports).
     fn lookup_import_candidates<FilterFn>(&mut self,
-                                          lookup_name: Name,
+                                          lookup_ident: Ident,
                                           namespace: Namespace,
                                           filter_fn: FilterFn)
                                           -> Vec<ImportSuggestion>
         where FilterFn: Fn(Def) -> bool
     {
         let mut suggestions = self.lookup_import_candidates_from_module(
-            lookup_name, namespace, self.graph_root, keywords::Crate.ident(), &filter_fn);
+            lookup_ident, namespace, self.graph_root, keywords::Crate.ident(), &filter_fn);
 
-        if self.session.rust_2018() {
+        if lookup_ident.span.rust_2018() {
             let extern_prelude_names = self.extern_prelude.clone();
             for (ident, _) in extern_prelude_names.into_iter() {
                 if let Some(crate_id) = self.crate_loader.maybe_process_path_extern(ident.name,
@@ -4620,7 +4612,7 @@ fn lookup_import_candidates<FilterFn>(&mut self,
                     self.populate_module_if_necessary(&crate_root);
 
                     suggestions.extend(self.lookup_import_candidates_from_module(
-                        lookup_name, namespace, crate_root, ident, &filter_fn));
+                        lookup_ident, namespace, crate_root, ident, &filter_fn));
                 }
             }
         }
@@ -4712,19 +4704,26 @@ fn resolve_visibility(&mut self, vis: &ast::Visibility) -> ty::Visibility {
             ast::VisibilityKind::Restricted { ref path, id, .. } => {
                 // For visibilities we are not ready to provide correct implementation of "uniform
                 // paths" right now, so on 2018 edition we only allow module-relative paths for now.
-                let first_ident = path.segments[0].ident;
-                if self.session.rust_2018() && !first_ident.is_path_segment_keyword() {
+                // On 2015 edition visibilities are resolved as crate-relative by default,
+                // so we are prepending a root segment if necessary.
+                let ident = path.segments.get(0).expect("empty path in visibility").ident;
+                let crate_root = if ident.is_path_segment_keyword() {
+                    None
+                } else if ident.span.rust_2018() {
                     let msg = "relative paths are not supported in visibilities on 2018 edition";
-                    self.session.struct_span_err(first_ident.span, msg)
+                    self.session.struct_span_err(ident.span, msg)
                                 .span_suggestion(path.span, "try", format!("crate::{}", path))
                                 .emit();
                     return ty::Visibility::Public;
-                }
-                // On 2015 visibilities are resolved as crate-relative by default,
-                // add starting root segment if necessary.
-                let segments = path.make_root().iter().chain(path.segments.iter())
-                    .map(|seg| Segment { ident: seg.ident, id: Some(seg.id) })
-                    .collect::<Vec<_>>();
+                } else {
+                    let ctxt = ident.span.ctxt();
+                    Some(Segment::from_ident(Ident::new(
+                        keywords::CrateRoot.name(), path.span.shrink_to_lo().with_ctxt(ctxt)
+                    )))
+                };
+
+                let segments = crate_root.into_iter()
+                    .chain(path.segments.iter().map(|seg| seg.into())).collect::<Vec<_>>();
                 let def = self.smart_resolve_path_fragment(
                     id,
                     None,
@@ -4837,7 +4836,7 @@ fn report_ambiguity_error(&self, ambiguity_error: &AmbiguityError) {
                 help_msgs.push(format!("consider adding an explicit import of \
                                         `{ident}` to disambiguate", ident = ident))
             }
-            if b.is_extern_crate() && self.session.rust_2018() {
+            if b.is_extern_crate() && ident.span.rust_2018() {
                 help_msgs.push(format!("use `::{ident}` to refer to this {thing} unambiguously",
                                        ident = ident, thing = b.descr()))
             }
index 581756dc6bf0b32b250a5203e2e6f92690aab78d..6fa9110fedb7fa9612a4f0f422f4d48b138f51b3 100644 (file)
@@ -605,6 +605,7 @@ struct Flags: u8 {
 
         assert!(force || !record_used); // `record_used` implies `force`
         assert!(macro_kind.is_none() || !is_import); // `is_import` implies no macro kind
+        let rust_2015 = ident.span.rust_2015();
         ident = ident.modern();
 
         // Make sure `self`, `super` etc produce an error when passed to here.
@@ -696,7 +697,7 @@ struct Flags: u8 {
                     }
                 }
                 WhereToResolve::MacroUsePrelude => {
-                    if use_prelude || self.session.rust_2015() {
+                    if use_prelude || rust_2015 {
                         match self.macro_use_prelude.get(&ident.name).cloned() {
                             Some(binding) =>
                                 Ok((binding, Flags::PRELUDE | Flags::MISC_FROM_PRELUDE)),
@@ -725,7 +726,7 @@ struct Flags: u8 {
                     }
                 }
                 WhereToResolve::LegacyPluginHelpers => {
-                    if (use_prelude || self.session.rust_2015()) &&
+                    if (use_prelude || rust_2015) &&
                        self.session.plugin_attributes.borrow().iter()
                                                      .any(|(name, _)| ident.name == &**name) {
                         let binding = (Def::NonMacroAttr(NonMacroAttrKind::LegacyPluginHelper),
index e2f09affd4fea5cd823b6a9d3552aee36b1a06f9..c82ebd6aa5128c32329a575830b25aa07b6e0543 100644 (file)
@@ -43,7 +43,7 @@
 use ast::{RangeEnd, RangeSyntax};
 use {ast, attr};
 use source_map::{self, SourceMap, Spanned, respan};
-use syntax_pos::{self, Span, MultiSpan, BytePos, FileName, edition::Edition};
+use syntax_pos::{self, Span, MultiSpan, BytePos, FileName};
 use errors::{self, Applicability, DiagnosticBuilder, DiagnosticId};
 use parse::{self, SeqSep, classify, token};
 use parse::lexer::TokenAndSpan;
@@ -1404,11 +1404,7 @@ fn parse_trait_item_(&mut self,
                 // definition...
 
                 // We don't allow argument names to be left off in edition 2018.
-                if p.span.edition() >= Edition::Edition2018 {
-                    p.parse_arg_general(true)
-                } else {
-                    p.parse_arg_general(false)
-                }
+                p.parse_arg_general(p.span.rust_2018())
             })?;
             generics.where_clause = self.parse_where_clause()?;
 
@@ -1601,9 +1597,9 @@ fn parse_ty_common(&mut self, allow_plus: bool, allow_qpath_recovery: bool)
             impl_dyn_multi = bounds.len() > 1 || self.prev_token_kind == PrevTokenKind::Plus;
             TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds)
         } else if self.check_keyword(keywords::Dyn) &&
-                  (self.span.edition() == Edition::Edition2018 ||
+                  (self.span.rust_2018() ||
                    self.look_ahead(1, |t| t.can_begin_bound() &&
-                                         !can_continue_type_after_non_fn_ident(t))) {
+                                          !can_continue_type_after_non_fn_ident(t))) {
             self.bump(); // `dyn`
             // Always parse bounds greedily for better error recovery.
             let bounds = self.parse_generic_bounds()?;
@@ -2084,8 +2080,9 @@ pub fn parse_path(&mut self, style: PathStyle) -> PResult<'a, ast::Path> {
 
         let lo = self.meta_var_span.unwrap_or(self.span);
         let mut segments = Vec::new();
+        let mod_sep_ctxt = self.span.ctxt();
         if self.eat(&token::ModSep) {
-            segments.push(PathSegment::crate_root(lo.shrink_to_lo()));
+            segments.push(PathSegment::crate_root(lo.shrink_to_lo().with_ctxt(mod_sep_ctxt)));
         }
         self.parse_path_segments(&mut segments, style, enable_warning)?;
 
@@ -2423,8 +2420,7 @@ fn parse_bottom_expr(&mut self) -> PResult<'a, P<Expr>> {
                     hi = path.span;
                     return Ok(self.mk_expr(lo.to(hi), ExprKind::Path(Some(qself), path), attrs));
                 }
-                if self.span.edition() >= Edition::Edition2018 &&
-                    self.check_keyword(keywords::Async)
+                if self.span.rust_2018() && self.check_keyword(keywords::Async)
                 {
                     if self.is_async_block() { // check for `async {` and `async move {`
                         return self.parse_async_block(attrs);
@@ -3440,7 +3436,7 @@ fn parse_lambda_expr(&mut self,
         } else {
             Movability::Movable
         };
-        let asyncness = if self.span.edition() >= Edition::Edition2018 {
+        let asyncness = if self.span.rust_2018() {
             self.parse_asyncness()
         } else {
             IsAsync::NotAsync
@@ -4562,9 +4558,7 @@ fn is_do_catch_block(&mut self) -> bool {
     fn is_try_block(&mut self) -> bool {
         self.token.is_keyword(keywords::Try) &&
         self.look_ahead(1, |t| *t == token::OpenDelim(token::Brace)) &&
-
-        self.span.edition() >= Edition::Edition2018 &&
-
+        self.span.rust_2018() &&
         // prevent `while try {} {}`, `if try {} {} else {}`, etc.
         !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL)
     }
@@ -7648,8 +7642,11 @@ fn parse_use_tree(&mut self) -> PResult<'a, UseTree> {
                       self.check(&token::BinOp(token::Star)) ||
                       self.is_import_coupler() {
             // `use *;` or `use ::*;` or `use {...};` or `use ::{...};`
+            let mod_sep_ctxt = self.span.ctxt();
             if self.eat(&token::ModSep) {
-                prefix.segments.push(PathSegment::crate_root(lo.shrink_to_lo()));
+                prefix.segments.push(
+                    PathSegment::crate_root(lo.shrink_to_lo().with_ctxt(mod_sep_ctxt))
+                );
             }
 
             if self.eat(&token::BinOp(token::Star)) {
index a780a38ff96f53fb1b8ea73fa1036c7e17771154..65f6d27239b83c53c4094fbd6dafecb911f7b155 100644 (file)
@@ -325,6 +325,16 @@ pub fn edition(self) -> edition::Edition {
                                                     |einfo| einfo.edition)
     }
 
+    #[inline]
+    pub fn rust_2015(&self) -> bool {
+        self.edition() == edition::Edition::Edition2015
+    }
+
+    #[inline]
+    pub fn rust_2018(&self) -> bool {
+        self.edition() >= edition::Edition::Edition2018
+    }
+
     /// Return the source callee.
     ///
     /// Returns `None` if the supplied span has no expansion trace,
index 361353c82e25cbc1a980a5d82ef2a7026ce03ee3..e333a4f2176f55b0027dc1987b68fdc5dae5b7ca 100644 (file)
@@ -12,7 +12,6 @@
 //! allows bidirectional lookup; i.e. given a value, one can easily find the
 //! type, and vice versa.
 
-use edition::Edition;
 use hygiene::SyntaxContext;
 use {Span, DUMMY_SP, GLOBALS};
 
@@ -444,7 +443,7 @@ pub fn is_used_keyword(self) -> bool {
     pub fn is_unused_keyword(self) -> bool {
         // Note: `span.edition()` is relatively expensive, don't call it unless necessary.
         self.name >= keywords::Abstract.name() && self.name <= keywords::Yield.name() ||
-        self.name.is_unused_keyword_2018() && self.span.edition() == Edition::Edition2018
+        self.name.is_unused_keyword_2018() && self.span.rust_2018()
     }
 
     /// Returns `true` if the token is either a special identifier or a keyword.
diff --git a/src/test/ui-fulldeps/proc-macro/auxiliary/edition-imports-2015.rs b/src/test/ui-fulldeps/proc-macro/auxiliary/edition-imports-2015.rs
new file mode 100644 (file)
index 0000000..5bb818f
--- /dev/null
@@ -0,0 +1,19 @@
+// edition:2015
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+
+#[proc_macro_derive(Derive2015)]
+pub fn derive_2015(_: TokenStream) -> TokenStream {
+    "
+    use import::Path;
+
+    fn check_absolute() {
+        let x = ::absolute::Path;
+    }
+    ".parse().unwrap()
+}
diff --git a/src/test/ui-fulldeps/proc-macro/edition-imports-2018.rs b/src/test/ui-fulldeps/proc-macro/edition-imports-2018.rs
new file mode 100644 (file)
index 0000000..f8d6bc5
--- /dev/null
@@ -0,0 +1,24 @@
+// compile-pass
+// edition:2018
+// aux-build:edition-imports-2015.rs
+
+#[macro_use]
+extern crate edition_imports_2015;
+
+mod import {
+    pub struct Path;
+}
+mod absolute {
+    pub struct Path;
+}
+
+mod check {
+    #[derive(Derive2015)] // OK
+    struct S;
+
+    fn check() {
+        Path;
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/editions/auxiliary/absolute.rs b/src/test/ui/editions/auxiliary/absolute.rs
new file mode 100644 (file)
index 0000000..d596f97
--- /dev/null
@@ -0,0 +1 @@
+pub struct Path;
diff --git a/src/test/ui/editions/auxiliary/edition-imports-2015.rs b/src/test/ui/editions/auxiliary/edition-imports-2015.rs
new file mode 100644 (file)
index 0000000..fb1a89d
--- /dev/null
@@ -0,0 +1,17 @@
+// edition:2015
+
+#[macro_export]
+macro_rules! gen_imports { () => {
+    use import::Path;
+    // use std::collections::LinkedList; // FIXME
+
+    fn check_absolute() {
+        ::absolute::Path;
+        // ::std::collections::LinkedList::<u8>::new(); // FIXME
+    }
+}}
+
+#[macro_export]
+macro_rules! gen_glob { () => {
+    use *;
+}}
diff --git a/src/test/ui/editions/auxiliary/edition-imports-2018.rs b/src/test/ui/editions/auxiliary/edition-imports-2018.rs
new file mode 100644 (file)
index 0000000..b08dc49
--- /dev/null
@@ -0,0 +1,17 @@
+// edition:2018
+
+#[macro_export]
+macro_rules! gen_imports { () => {
+    use import::Path;
+    use std::collections::LinkedList;
+
+    fn check_absolute() {
+        ::absolute::Path;
+        ::std::collections::LinkedList::<u8>::new();
+    }
+}}
+
+#[macro_export]
+macro_rules! gen_glob { () => {
+    use *;
+}}
diff --git a/src/test/ui/editions/edition-imports-2015.rs b/src/test/ui/editions/edition-imports-2015.rs
new file mode 100644 (file)
index 0000000..b89ca28
--- /dev/null
@@ -0,0 +1,28 @@
+// edition:2015
+// compile-flags:--extern absolute
+// aux-build:edition-imports-2018.rs
+// aux-build:absolute.rs
+
+#![feature(uniform_paths)]
+
+#[macro_use]
+extern crate edition_imports_2018;
+
+mod check {
+    mod import {
+        pub struct Path;
+    }
+
+    gen_imports!(); // OK
+
+    fn check() {
+        Path;
+        LinkedList::<u8>::new();
+    }
+}
+
+mod check_glob {
+    gen_glob!(); //~ ERROR cannot glob-import all possible crates
+}
+
+fn main() {}
diff --git a/src/test/ui/editions/edition-imports-2015.stderr b/src/test/ui/editions/edition-imports-2015.stderr
new file mode 100644 (file)
index 0000000..fb6b2e6
--- /dev/null
@@ -0,0 +1,10 @@
+error: cannot glob-import all possible crates
+  --> $DIR/edition-imports-2015.rs:25:5
+   |
+LL |     gen_glob!(); //~ ERROR cannot glob-import all possible crates
+   |     ^^^^^^^^^^^^
+   |
+   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/editions/edition-imports-2018.rs b/src/test/ui/editions/edition-imports-2018.rs
new file mode 100644 (file)
index 0000000..cef4ce3
--- /dev/null
@@ -0,0 +1,33 @@
+// compile-pass
+// edition:2018
+// aux-build:edition-imports-2015.rs
+
+#[macro_use]
+extern crate edition_imports_2015;
+
+mod import {
+    pub struct Path;
+}
+mod absolute {
+    pub struct Path;
+}
+
+mod check {
+    gen_imports!(); // OK
+
+    fn check() {
+        Path;
+        // LinkedList::<u8>::new(); // FIXME
+    }
+}
+
+mod check_glob {
+    gen_glob!(); // OK
+
+    fn check() {
+        import::Path;
+        absolute::Path;
+    }
+}
+
+fn main() {}