diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 6a74a19c83e..796dac57744 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2322,16 +2322,17 @@ pub enum ModKind { Unloaded, } -#[derive(Clone, Encodable, Decodable, Debug)] +#[derive(Copy, Clone, Encodable, Decodable, Debug)] pub struct ModSpans { /// `inner_span` covers the body of the module; for a file module, its the whole file. /// For an inline module, its the span inside the `{ ... }`, not including the curly braces. pub inner_span: Span, + pub inject_use_span: Span, } impl Default for ModSpans { fn default() -> ModSpans { - ModSpans { inner_span: Default::default() } + ModSpans { inner_span: Default::default(), inject_use_span: Default::default() } } } diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 93588697892..b87637d2dde 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1009,8 +1009,9 @@ pub fn noop_visit_item_kind(kind: &mut ItemKind, vis: &mut T) { ItemKind::Mod(unsafety, mod_kind) => { visit_unsafety(unsafety, vis); match mod_kind { - ModKind::Loaded(items, _inline, ModSpans { inner_span }) => { + ModKind::Loaded(items, _inline, ModSpans { inner_span, inject_use_span }) => { vis.visit_span(inner_span); + vis.visit_span(inject_use_span); items.flat_map_in_place(|item| vis.flat_map_item(item)); } ModKind::Unloaded => {} @@ -1112,8 +1113,9 @@ pub fn noop_visit_crate(krate: &mut Crate, vis: &mut T) { vis.visit_id(id); visit_attrs(attrs, vis); items.flat_map_in_place(|item| vis.flat_map_item(item)); - let ModSpans { inner_span } = spans; + let ModSpans { inner_span, inject_use_span } = spans; vis.visit_span(inner_span); + vis.visit_span(inject_use_span); } // Mutates one item into possibly many items. diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index aee117e554f..a7401be6db3 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -263,7 +263,7 @@ impl<'hir> LoweringContext<'_, 'hir> { }) } ItemKind::Mod(_, ref mod_kind) => match mod_kind { - ModKind::Loaded(items, _, ModSpans { inner_span }) => { + ModKind::Loaded(items, _, ModSpans { inner_span, inject_use_span: _ }) => { hir::ItemKind::Mod(self.lower_mod(items, *inner_span)) } ModKind::Unloaded => panic!("`mod` items should have been loaded by now"), diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 4bbeece2bde..e2553ab40ca 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -129,9 +129,8 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> { // We don't want to recurse into anything other than mods, since // mods or tests inside of functions will break things - if let ast::ItemKind::Mod(_, ModKind::Loaded(.., ast::ModSpans { inner_span: span })) = - item.kind - { + if let ast::ItemKind::Mod(_, ModKind::Loaded(.., ref spans)) = item.kind { + let ast::ModSpans { inner_span: span, inject_use_span: _ } = *spans; let prev_tests = mem::take(&mut self.tests); noop_visit_item_kind(&mut item.kind, self); self.add_test_cases(item.id, span, prev_tests); diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 484a27fa59d..c370195659d 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -55,6 +55,7 @@ impl<'a> Parser<'a> { let lo = self.token.span; let attrs = self.parse_inner_attributes()?; + let post_attr_lo = self.token.span; let mut items = vec![]; while let Some(item) = self.parse_item(ForceCollect::No)? { items.push(item); @@ -71,7 +72,9 @@ impl<'a> Parser<'a> { } } - Ok((attrs, items, ModSpans { inner_span: lo.to(self.prev_token.span) })) + let inject_use_span = post_attr_lo.data().with_hi(post_attr_lo.lo()); + let mod_spans = ModSpans { inner_span: lo.to(self.prev_token.span), inject_use_span }; + Ok((attrs, items, mod_spans)) } } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 9eedd9839eb..03b4f0609bf 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -71,7 +71,6 @@ use rustc_span::{Span, DUMMY_SP}; use smallvec::{smallvec, SmallVec}; use std::cell::{Cell, RefCell}; use std::collections::BTreeSet; -use std::ops::ControlFlow; use std::{cmp, fmt, iter, mem, ptr}; use tracing::debug; @@ -315,74 +314,70 @@ impl<'a> From<&'a ast::PathSegment> for Segment { } } +#[derive(Debug)] struct UsePlacementFinder { target_module: NodeId, - span: Option, - found_use: bool, + first_legal_span: Option, + first_use_span: Option, } impl UsePlacementFinder { fn check(krate: &Crate, target_module: NodeId) -> (Option, bool) { - let mut finder = UsePlacementFinder { target_module, span: None, found_use: false }; - if let ControlFlow::Continue(..) = finder.check_mod(&krate.items, CRATE_NODE_ID) { - visit::walk_crate(&mut finder, krate); + let mut finder = + UsePlacementFinder { target_module, first_legal_span: None, first_use_span: None }; + finder.visit_crate(krate); + if let Some(use_span) = finder.first_use_span { + (Some(use_span), true) + } else { + (finder.first_legal_span, false) } - (finder.span, finder.found_use) - } - - fn check_mod(&mut self, items: &[P], node_id: NodeId) -> ControlFlow<()> { - if self.span.is_some() { - return ControlFlow::Break(()); - } - if node_id != self.target_module { - return ControlFlow::Continue(()); - } - // find a use statement - for item in items { - match item.kind { - ItemKind::Use(..) => { - // don't suggest placing a use before the prelude - // import or other generated ones - if !item.span.from_expansion() { - self.span = Some(item.span.shrink_to_lo()); - self.found_use = true; - return ControlFlow::Break(()); - } - } - // don't place use before extern crate - ItemKind::ExternCrate(_) => {} - // but place them before the first other item - _ => { - if self.span.map_or(true, |span| item.span < span) - && !item.span.from_expansion() - { - self.span = Some(item.span.shrink_to_lo()); - // don't insert between attributes and an item - // find the first attribute on the item - // FIXME: This is broken for active attributes. - for attr in &item.attrs { - if !attr.span.is_dummy() - && self.span.map_or(true, |span| attr.span < span) - { - self.span = Some(attr.span.shrink_to_lo()); - } - } - } - } - } - } - ControlFlow::Continue(()) } } -impl<'tcx> Visitor<'tcx> for UsePlacementFinder { - fn visit_item(&mut self, item: &'tcx ast::Item) { - if let ItemKind::Mod(_, ModKind::Loaded(items, ..)) = &item.kind { - if let ControlFlow::Break(..) = self.check_mod(items, item.id) { - return; +fn is_span_suitable_for_use_injection(s: Span) -> bool { + // don't suggest placing a use before the prelude + // import or other generated ones + !s.from_expansion() +} + +fn search_for_any_use_in_items(items: &[P]) -> Option { + for item in items { + if let ItemKind::Use(..) = item.kind { + if is_span_suitable_for_use_injection(item.span) { + return Some(item.span.shrink_to_lo()); } } - visit::walk_item(self, item); + } + return None; +} + +impl<'tcx> Visitor<'tcx> for UsePlacementFinder { + fn visit_crate(&mut self, c: &Crate) { + if self.target_module == CRATE_NODE_ID { + let inject = c.spans.inject_use_span; + if is_span_suitable_for_use_injection(inject) { + self.first_legal_span = Some(inject); + } + self.first_use_span = search_for_any_use_in_items(&c.items); + return; + } else { + visit::walk_crate(self, c); + } + } + + fn visit_item(&mut self, item: &'tcx ast::Item) { + if self.target_module == item.id { + if let ItemKind::Mod(_, ModKind::Loaded(items, _inline, mod_spans)) = &item.kind { + let inject = mod_spans.inject_use_span; + if is_span_suitable_for_use_injection(inject) { + self.first_legal_span = Some(inject); + } + self.first_use_span = search_for_any_use_in_items(items); + return; + } + } else { + visit::walk_item(self, item); + } } } diff --git a/src/test/ui/ast-json/ast-json-noexpand-output.stdout b/src/test/ui/ast-json/ast-json-noexpand-output.stdout index a456c10cf47..746ced689d4 100644 --- a/src/test/ui/ast-json/ast-json-noexpand-output.stdout +++ b/src/test/ui/ast-json/ast-json-noexpand-output.stdout @@ -1 +1 @@ -{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"kind":{"variant":"Interpolated","fields":[{"variant":"NtExpr","fields":[{"id":0,"kind":{"variant":"Lit","fields":[{"token":{"kind":"Str","symbol":"lib","suffix":null},"kind":{"variant":"Str","fields":["lib","Cooked"]},"span":{"lo":0,"hi":0}}]},"span":{"lo":0,"hi":0},"attrs":{"0":null},"tokens":{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}}]}]},"span":{"lo":0,"hi":0}}]},"tokens":null},{"0":[[{"variant":"Token","fields":[{"kind":"Pound","span":{"lo":0,"hi":0}}]},"Joint"],[{"variant":"Token","fields":[{"kind":"Not","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Delimited","fields":[{"open":{"lo":0,"hi":0},"close":{"lo":0,"hi":0}},"Bracket",{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["crate_type",false]},"span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":"Eq","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"Alone"]]}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0}}],"items":[{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null}],"spans":{"inner_span":{"lo":0,"hi":0}},"id":0,"is_placeholder":false} +{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"kind":{"variant":"Interpolated","fields":[{"variant":"NtExpr","fields":[{"id":0,"kind":{"variant":"Lit","fields":[{"token":{"kind":"Str","symbol":"lib","suffix":null},"kind":{"variant":"Str","fields":["lib","Cooked"]},"span":{"lo":0,"hi":0}}]},"span":{"lo":0,"hi":0},"attrs":{"0":null},"tokens":{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}}]}]},"span":{"lo":0,"hi":0}}]},"tokens":null},{"0":[[{"variant":"Token","fields":[{"kind":"Pound","span":{"lo":0,"hi":0}}]},"Joint"],[{"variant":"Token","fields":[{"kind":"Not","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Delimited","fields":[{"open":{"lo":0,"hi":0},"close":{"lo":0,"hi":0}},"Bracket",{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["crate_type",false]},"span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":"Eq","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"Alone"]]}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0}}],"items":[{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null}],"spans":{"inner_span":{"lo":0,"hi":0},"inject_use_span":{"lo":0,"hi":0}},"id":0,"is_placeholder":false} diff --git a/src/test/ui/ast-json/ast-json-output.stdout b/src/test/ui/ast-json/ast-json-output.stdout index d385c76039a..b0aaa663f38 100644 --- a/src/test/ui/ast-json/ast-json-output.stdout +++ b/src/test/ui/ast-json/ast-json-output.stdout @@ -1 +1 @@ -{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"kind":{"variant":"Interpolated","fields":[{"variant":"NtExpr","fields":[{"id":0,"kind":{"variant":"Lit","fields":[{"token":{"kind":"Str","symbol":"lib","suffix":null},"kind":{"variant":"Str","fields":["lib","Cooked"]},"span":{"lo":0,"hi":0}}]},"span":{"lo":0,"hi":0},"attrs":{"0":null},"tokens":{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}}]}]},"span":{"lo":0,"hi":0}}]},"tokens":null},{"0":[[{"variant":"Token","fields":[{"kind":"Pound","span":{"lo":0,"hi":0}}]},"Joint"],[{"variant":"Token","fields":[{"kind":"Not","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Delimited","fields":[{"open":{"lo":0,"hi":0},"close":{"lo":0,"hi":0}},"Bracket",{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["crate_type",false]},"span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":"Eq","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"Alone"]]}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0}}],"items":[{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"prelude_import","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":"Empty","tokens":null},null]},"id":null,"style":"Outer","span":{"lo":0,"hi":0}}],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"","span":{"lo":0,"hi":0}},"kind":{"variant":"Use","fields":[{"prefix":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"{{root}}","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"std","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"prelude","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"rust_2015","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"kind":"Glob","span":{"lo":0,"hi":0}}]},"tokens":null},{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"macro_use","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":"Empty","tokens":null},null]},"id":null,"style":"Outer","span":{"lo":0,"hi":0}}],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"std","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null},{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null}],"spans":{"inner_span":{"lo":0,"hi":0}},"id":0,"is_placeholder":false} +{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"kind":{"variant":"Interpolated","fields":[{"variant":"NtExpr","fields":[{"id":0,"kind":{"variant":"Lit","fields":[{"token":{"kind":"Str","symbol":"lib","suffix":null},"kind":{"variant":"Str","fields":["lib","Cooked"]},"span":{"lo":0,"hi":0}}]},"span":{"lo":0,"hi":0},"attrs":{"0":null},"tokens":{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}}]}]},"span":{"lo":0,"hi":0}}]},"tokens":null},{"0":[[{"variant":"Token","fields":[{"kind":"Pound","span":{"lo":0,"hi":0}}]},"Joint"],[{"variant":"Token","fields":[{"kind":"Not","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Delimited","fields":[{"open":{"lo":0,"hi":0},"close":{"lo":0,"hi":0}},"Bracket",{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["crate_type",false]},"span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":"Eq","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"Alone"]]}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0}}],"items":[{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"prelude_import","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":"Empty","tokens":null},null]},"id":null,"style":"Outer","span":{"lo":0,"hi":0}}],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"","span":{"lo":0,"hi":0}},"kind":{"variant":"Use","fields":[{"prefix":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"{{root}}","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"std","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"prelude","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"rust_2015","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"kind":"Glob","span":{"lo":0,"hi":0}}]},"tokens":null},{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"macro_use","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":"Empty","tokens":null},null]},"id":null,"style":"Outer","span":{"lo":0,"hi":0}}],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"std","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null},{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null}],"spans":{"inner_span":{"lo":0,"hi":0},"inject_use_span":{"lo":0,"hi":0}},"id":0,"is_placeholder":false} diff --git a/src/test/ui/proc-macro/amputate-span.rs b/src/test/ui/proc-macro/amputate-span.rs new file mode 100644 index 00000000000..3ca7c847a88 --- /dev/null +++ b/src/test/ui/proc-macro/amputate-span.rs @@ -0,0 +1,63 @@ +// aux-build:amputate-span.rs +// edition:2018 +// compile-flags: --extern amputate_span + +// This test has been crafted to ensure the following things: +// +// 1. There's a resolution error that prompts the compiler to suggest +// adding a `use` item. +// +// 2. There are no `use` or `extern crate` items in the source +// code. In fact, there is only one item, the `fn main` +// declaration. +// +// 3. The single `fn main` declaration has an attribute attached to it +// that just deletes the first token from the given item. +// +// You need all of these conditions to hold in order to replicate the +// scenario that yielded issue 87613, where the compiler's suggestion +// looks like: +// +// ``` +// help: consider importing this struct +// | +// 47 | hey */ async use std::process::Command; +// | ++++++++++++++++++++++++++ +// ``` +// +// The first condition is necessary to force the compiler issue a +// suggestion. The second condition is necessary to force the +// suggestion to be issued at a span associated with the sole +// `fn`-item of this crate. The third condition is necessary in order +// to yield the weird state where the associated span of the `fn`-item +// does not actually cover all of the original source code of the +// `fn`-item (which is why we are calling it an "amputated" span +// here). +// +// Note that satisfying conditions 2 and 3 requires the use of the +// `--extern` compile flag. +// +// You might ask yourself: What code would do such a thing? The +// answer is: the #[tokio::main] attribute does *exactly* this (as +// well as injecting some other code into the `fn main` that it +// constructs). + +#[amputate_span::drop_first_token] +/* what the +hey */ async fn main() { + Command::new("git"); //~ ERROR [E0433] +} + +// (The /* ... */ comment in the above is not part of the original +// bug. It is just meant to illustrate one particular facet of the +// original non-ideal behavior, where we were transcribing the +// trailing comment as part of the emitted suggestion, for better or +// for worse.) + +mod inner { + #[amputate_span::drop_first_token] + /* another interesting + case */ async fn foo() { + Command::new("git"); //~ ERROR [E0433] + } +} diff --git a/src/test/ui/proc-macro/amputate-span.stderr b/src/test/ui/proc-macro/amputate-span.stderr new file mode 100644 index 00000000000..75c5cbdabc7 --- /dev/null +++ b/src/test/ui/proc-macro/amputate-span.stderr @@ -0,0 +1,25 @@ +error[E0433]: failed to resolve: use of undeclared type `Command` + --> $DIR/amputate-span.rs:48:5 + | +LL | Command::new("git"); + | ^^^^^^^ not found in this scope + | +help: consider importing this struct + | +LL | use std::process::Command; + | + +error[E0433]: failed to resolve: use of undeclared type `Command` + --> $DIR/amputate-span.rs:61:9 + | +LL | Command::new("git"); + | ^^^^^^^ not found in this scope + | +help: consider importing this struct + | +LL | use std::process::Command; + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0433`. diff --git a/src/test/ui/proc-macro/auxiliary/amputate-span.rs b/src/test/ui/proc-macro/auxiliary/amputate-span.rs new file mode 100644 index 00000000000..1a82119ae95 --- /dev/null +++ b/src/test/ui/proc-macro/auxiliary/amputate-span.rs @@ -0,0 +1,14 @@ +// force-host +// no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::TokenStream; + +#[proc_macro_attribute] +pub fn drop_first_token(attr: TokenStream, input: TokenStream) -> TokenStream { + assert!(attr.is_empty()); + input.into_iter().skip(1).collect() +} diff --git a/src/tools/rustfmt/src/parse/parser.rs b/src/tools/rustfmt/src/parse/parser.rs index 3b4e762b6dd..6983249c15d 100644 --- a/src/tools/rustfmt/src/parse/parser.rs +++ b/src/tools/rustfmt/src/parse/parser.rs @@ -113,7 +113,7 @@ impl<'a> Parser<'a> { let result = catch_unwind(AssertUnwindSafe(|| { let mut parser = new_parser_from_file(sess.inner(), path, Some(span)); match parser.parse_mod(&TokenKind::Eof) { - Ok((a, i, ast::ModSpans { inner_span })) => Some((a, i, inner_span)), + Ok((a, i, ast::ModSpans { inner_span, inject_use_span: _ })) => Some((a, i, inner_span)), Err(mut e) => { e.emit(); if sess.can_reset_errors() { diff --git a/src/tools/rustfmt/src/visitor.rs b/src/tools/rustfmt/src/visitor.rs index c44b2fc6ae3..dec977e98ca 100644 --- a/src/tools/rustfmt/src/visitor.rs +++ b/src/tools/rustfmt/src/visitor.rs @@ -916,7 +916,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { self.push_str(&ident_str); if let ast::ModKind::Loaded(ref items, ast::Inline::Yes, ref spans) = mod_kind { - let ast::ModSpans { inner_span } = *spans; + let ast::ModSpans{ inner_span, inject_use_span: _ } = *spans; match self.config.brace_style() { BraceStyle::AlwaysNextLine => { let indent_str = self.block_indent.to_string_with_newline(self.config);