From 24529d9c88f84cc35570c44ccc179cfea882455f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 23 May 2018 12:45:11 -0400 Subject: [PATCH 1/2] don't ask what edition we are in; ask what edition a span is in We now track the edition of each span. Using that info when gating the Rust 2018 interpretation means that macros from Rust 2015 crates "just work" when used in Rust 2018 crates (at least in the case of `use` paths). --- src/librustc/lint/mod.rs | 8 ++++- src/librustc/session/mod.rs | 14 +++++---- src/librustc_driver/driver.rs | 2 +- src/librustc_resolve/lib.rs | 4 +-- src/librustc_resolve/resolve_imports.rs | 2 +- src/librustc_typeck/check/method/probe.rs | 8 ++--- src/libsyntax_pos/edition.rs | 5 ++++ .../inject-2015-use-root-module-lib.rs | 29 +++++++++++++++++++ .../rust-2018/inject-2015-use-root-module.rs | 27 +++++++++++++++++ 9 files changed, 84 insertions(+), 15 deletions(-) create mode 100644 src/test/ui/rust-2018/auxiliary/inject-2015-use-root-module-lib.rs create mode 100644 src/test/ui/rust-2018/inject-2015-use-root-module.rs diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index 7645d3486c2f8..10f5bad5265e4 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -89,7 +89,13 @@ impl Lint { pub fn default_level(&self, session: &Session) -> Level { if let Some(edition_deny) = self.edition_deny { - if session.edition() >= edition_deny { + // Ideally, we would get the edition for the actual span, + // but that is kind of a pain in the neck to do right + // now. Also, lints are not breaking things anyway (due to + // `-Acap-lints`), and the lint itself should probably be + // checking the span to see if the code was injected via + // macro etc, so for now we'll just use the local level. + if session.local_edition() >= edition_deny { return Level::Deny } } diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 8df66d8d68855..bd5c528824eb2 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -948,12 +948,14 @@ impl Session { self.opts.debugging_opts.teach && self.parse_sess.span_diagnostic.must_teach(code) } - /// Are we allowed to use features from the Rust 2018 edition? - pub fn rust_2018(&self) -> bool { - self.opts.edition >= Edition::Edition2018 - } - - pub fn edition(&self) -> Edition { + /// What is the edition of the "local crate" being compiled? + /// + /// You should not call this except as a last resort. It is better + /// to do `span.edition()` instead, which gives the edition for a + /// particular span: that way, when you are looking at code + /// creating a macro from a Rust 2015 crate, you will use the Rust + /// 2015 Edition rules. + pub fn local_edition(&self) -> Edition { self.opts.edition } } diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index ed28b05c12551..6a43f0c6f04ed 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -736,7 +736,7 @@ where krate, &sess.parse_sess, sess.opts.test, - sess.edition(), + sess.local_edition(), ); // these need to be set "early" so that expansion sees `quote` if enabled. sess.init_features(features); diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 5f277d03c5238..f2292a737b454 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -3276,7 +3276,7 @@ impl<'a> Resolver<'a> { if prev_name == keywords::Extern.name() || prev_name == keywords::CrateRoot.name() && self.session.features_untracked().extern_absolute_paths && - self.session.rust_2018() { + path_span.edition().rust_2018() { // `::extern_crate::a::b` let crate_id = self.crate_loader.process_path_extern(name, ident.span); let crate_root = @@ -3446,7 +3446,7 @@ impl<'a> Resolver<'a> { fn lint_path_starts_with_module(&self, id: NodeId, span: Span) { // In the 2018 edition this lint is a hard error, so nothing to do - if self.session.rust_2018() { + if span.edition().rust_2018() { return } // In the 2015 edition there's no use in emitting lints unless the diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index 91de3a34cc84a..42d33d6d07a9a 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -677,7 +677,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { module_path[0].name == keywords::Extern.name()) { let is_extern = module_path[0].name == keywords::Extern.name() || (self.session.features_untracked().extern_absolute_paths && - self.session.rust_2018()); + span.edition().rust_2018()); match directive.subclass { GlobImport { .. } if is_extern => { return Some((directive.span, diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 6cdfb0bccc986..2df8420e957ac 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -334,10 +334,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // this case used to be allowed by the compiler, // so we do a future-compat lint here for the 2015 edition // (see https://github.com/rust-lang/rust/issues/46906) - if self.tcx.sess.rust_2018() { - span_err!(self.tcx.sess, span, E0908, - "the type of this value must be known \ - to call a method on a raw pointer on it"); + if span.edition().rust_2018() { + span_err!(self.tcx.sess, span, E0908, + "the type of this value must be known \ + to call a method on a raw pointer on it"); } else { self.tcx.lint_node( lint::builtin::TYVAR_BEHIND_RAW_POINTER, diff --git a/src/libsyntax_pos/edition.rs b/src/libsyntax_pos/edition.rs index 18446c109964d..20ee11c5ec3f9 100644 --- a/src/libsyntax_pos/edition.rs +++ b/src/libsyntax_pos/edition.rs @@ -68,6 +68,11 @@ impl Edition { Edition::Edition2018 => false, } } + + /// Is this at least 2018? + pub fn rust_2018(self) -> bool { + self >= Edition::Edition2018 + } } impl FromStr for Edition { diff --git a/src/test/ui/rust-2018/auxiliary/inject-2015-use-root-module-lib.rs b/src/test/ui/rust-2018/auxiliary/inject-2015-use-root-module-lib.rs new file mode 100644 index 0000000000000..af63ee7d2eabd --- /dev/null +++ b/src/test/ui/rust-2018/auxiliary/inject-2015-use-root-module-lib.rs @@ -0,0 +1,29 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// this is a rust 2015 crate + +#[macro_export] +macro_rules! inject_me_at_the_root { + ($name1:ident, $name2:ident) => { + mod $name1 { + pub(crate) const THE_CONSTANT: u32 = 22; + } + + fn $name2() -> u32 { + // Key point: this `use` statement -- in Rust 2018 -- + // would be an error. But because this crate is in Rust + // 2015, it works, even when executed from a Rust 2018 + // environment. + use $name1::THE_CONSTANT; + THE_CONSTANT + } + } +} diff --git a/src/test/ui/rust-2018/inject-2015-use-root-module.rs b/src/test/ui/rust-2018/inject-2015-use-root-module.rs new file mode 100644 index 0000000000000..38d4322a073c3 --- /dev/null +++ b/src/test/ui/rust-2018/inject-2015-use-root-module.rs @@ -0,0 +1,27 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags:--edition 2018 +// aux-build:inject-2015-use-root-module-lib.rs +// run-pass + +// The macro `inject_me_at_the_root!` generates some code that uses +// `use x::y` to name the global item `x`. In Rust 2018, that should +// be `use crate::x::y`, but we test here that we still accept it, +// as `inject_2015_lib` is in the 2015 edition. + +#[macro_use] +extern crate inject_2015_use_root_module_lib; + +inject_me_at_the_root!(x, y); + +fn main() { + println!("Hello, world: {}", y()); +} From 233135c5d9a435b7a3d15bcbf3e03f0f69416b83 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 23 May 2018 13:26:58 -0400 Subject: [PATCH 2/2] WIP --- .../inject-2015-use-root-module-lib.rs | 10 +++++++ .../inject-2015-use-root-module-path.rs | 29 +++++++++++++++++++ .../rust-2018/inject-2015-use-root-module.rs | 2 ++ 3 files changed, 41 insertions(+) create mode 100644 src/test/ui/rust-2018/inject-2015-use-root-module-path.rs diff --git a/src/test/ui/rust-2018/auxiliary/inject-2015-use-root-module-lib.rs b/src/test/ui/rust-2018/auxiliary/inject-2015-use-root-module-lib.rs index af63ee7d2eabd..4126f9e925ce8 100644 --- a/src/test/ui/rust-2018/auxiliary/inject-2015-use-root-module-lib.rs +++ b/src/test/ui/rust-2018/auxiliary/inject-2015-use-root-module-lib.rs @@ -27,3 +27,13 @@ macro_rules! inject_me_at_the_root { } } } + +#[macro_export] +macro_rules! print_me { + ($p:path) => { + { + use $p as V; + println!("{}", V); + } + } +} diff --git a/src/test/ui/rust-2018/inject-2015-use-root-module-path.rs b/src/test/ui/rust-2018/inject-2015-use-root-module-path.rs new file mode 100644 index 0000000000000..33a643708b251 --- /dev/null +++ b/src/test/ui/rust-2018/inject-2015-use-root-module-path.rs @@ -0,0 +1,29 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags:--edition 2018 +// aux-build:inject-2015-use-root-module-lib.rs + +// The macro `inject_me_at_the_root!` generates some code that uses +// `use x::y` to name the global item `x`. In Rust 2018, that should +// be `use crate::x::y`, but we test here that we still accept it, +// as `inject_2015_lib` is in the 2015 edition. + +#[macro_use] +extern crate inject_2015_use_root_module_lib; + +inject_me_at_the_root!(x, y); + +fn main() { + println!("Hello, world: {}", y()); + + // This path comes out as an error, because `x::y` comes from Rust 2018 + print_me!(x::y); //~ ERROR unresolved import `x::y` +} diff --git a/src/test/ui/rust-2018/inject-2015-use-root-module.rs b/src/test/ui/rust-2018/inject-2015-use-root-module.rs index 38d4322a073c3..60031596c5538 100644 --- a/src/test/ui/rust-2018/inject-2015-use-root-module.rs +++ b/src/test/ui/rust-2018/inject-2015-use-root-module.rs @@ -24,4 +24,6 @@ inject_me_at_the_root!(x, y); fn main() { println!("Hello, world: {}", y()); + + print_me!(crate::x::y); }