Adjusted diagnostic output so that if there is no use
in a item sequence,
then we just suggest the first legal position where you could inject a use. To do this, I added `inject_use_span` field to `ModSpans`, and populate it in parser (it is the span of the first token found after inner attributes, if any). Then I rewrote the use-suggestion code to utilize it, and threw out some stuff that is now unnecessary with this in place. (I think the result is easier to understand.) Then I added a test of issue 87613.
This commit is contained in:
parent
b82795244e
commit
d37da1e332
13 changed files with 172 additions and 70 deletions
|
@ -2322,16 +2322,17 @@ pub enum ModKind {
|
||||||
Unloaded,
|
Unloaded,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Encodable, Decodable, Debug)]
|
#[derive(Copy, Clone, Encodable, Decodable, Debug)]
|
||||||
pub struct ModSpans {
|
pub struct ModSpans {
|
||||||
/// `inner_span` covers the body of the module; for a file module, its the whole file.
|
/// `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.
|
/// For an inline module, its the span inside the `{ ... }`, not including the curly braces.
|
||||||
pub inner_span: Span,
|
pub inner_span: Span,
|
||||||
|
pub inject_use_span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ModSpans {
|
impl Default for ModSpans {
|
||||||
fn default() -> ModSpans {
|
fn default() -> ModSpans {
|
||||||
ModSpans { inner_span: Default::default() }
|
ModSpans { inner_span: Default::default(), inject_use_span: Default::default() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1009,8 +1009,9 @@ pub fn noop_visit_item_kind<T: MutVisitor>(kind: &mut ItemKind, vis: &mut T) {
|
||||||
ItemKind::Mod(unsafety, mod_kind) => {
|
ItemKind::Mod(unsafety, mod_kind) => {
|
||||||
visit_unsafety(unsafety, vis);
|
visit_unsafety(unsafety, vis);
|
||||||
match mod_kind {
|
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(inner_span);
|
||||||
|
vis.visit_span(inject_use_span);
|
||||||
items.flat_map_in_place(|item| vis.flat_map_item(item));
|
items.flat_map_in_place(|item| vis.flat_map_item(item));
|
||||||
}
|
}
|
||||||
ModKind::Unloaded => {}
|
ModKind::Unloaded => {}
|
||||||
|
@ -1112,8 +1113,9 @@ pub fn noop_visit_crate<T: MutVisitor>(krate: &mut Crate, vis: &mut T) {
|
||||||
vis.visit_id(id);
|
vis.visit_id(id);
|
||||||
visit_attrs(attrs, vis);
|
visit_attrs(attrs, vis);
|
||||||
items.flat_map_in_place(|item| vis.flat_map_item(item));
|
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(inner_span);
|
||||||
|
vis.visit_span(inject_use_span);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mutates one item into possibly many items.
|
// Mutates one item into possibly many items.
|
||||||
|
|
|
@ -263,7 +263,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
ItemKind::Mod(_, ref mod_kind) => match mod_kind {
|
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))
|
hir::ItemKind::Mod(self.lower_mod(items, *inner_span))
|
||||||
}
|
}
|
||||||
ModKind::Unloaded => panic!("`mod` items should have been loaded by now"),
|
ModKind::Unloaded => panic!("`mod` items should have been loaded by now"),
|
||||||
|
|
|
@ -129,9 +129,8 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
|
||||||
|
|
||||||
// We don't want to recurse into anything other than mods, since
|
// We don't want to recurse into anything other than mods, since
|
||||||
// mods or tests inside of functions will break things
|
// mods or tests inside of functions will break things
|
||||||
if let ast::ItemKind::Mod(_, ModKind::Loaded(.., ast::ModSpans { inner_span: span })) =
|
if let ast::ItemKind::Mod(_, ModKind::Loaded(.., ref spans)) = item.kind {
|
||||||
item.kind
|
let ast::ModSpans { inner_span: span, inject_use_span: _ } = *spans;
|
||||||
{
|
|
||||||
let prev_tests = mem::take(&mut self.tests);
|
let prev_tests = mem::take(&mut self.tests);
|
||||||
noop_visit_item_kind(&mut item.kind, self);
|
noop_visit_item_kind(&mut item.kind, self);
|
||||||
self.add_test_cases(item.id, span, prev_tests);
|
self.add_test_cases(item.id, span, prev_tests);
|
||||||
|
|
|
@ -55,6 +55,7 @@ impl<'a> Parser<'a> {
|
||||||
let lo = self.token.span;
|
let lo = self.token.span;
|
||||||
let attrs = self.parse_inner_attributes()?;
|
let attrs = self.parse_inner_attributes()?;
|
||||||
|
|
||||||
|
let post_attr_lo = self.token.span;
|
||||||
let mut items = vec![];
|
let mut items = vec![];
|
||||||
while let Some(item) = self.parse_item(ForceCollect::No)? {
|
while let Some(item) = self.parse_item(ForceCollect::No)? {
|
||||||
items.push(item);
|
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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,6 @@ use rustc_span::{Span, DUMMY_SP};
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use std::ops::ControlFlow;
|
|
||||||
use std::{cmp, fmt, iter, mem, ptr};
|
use std::{cmp, fmt, iter, mem, ptr};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
|
@ -315,74 +314,70 @@ impl<'a> From<&'a ast::PathSegment> for Segment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
struct UsePlacementFinder {
|
struct UsePlacementFinder {
|
||||||
target_module: NodeId,
|
target_module: NodeId,
|
||||||
span: Option<Span>,
|
first_legal_span: Option<Span>,
|
||||||
found_use: bool,
|
first_use_span: Option<Span>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UsePlacementFinder {
|
impl UsePlacementFinder {
|
||||||
fn check(krate: &Crate, target_module: NodeId) -> (Option<Span>, bool) {
|
fn check(krate: &Crate, target_module: NodeId) -> (Option<Span>, bool) {
|
||||||
let mut finder = UsePlacementFinder { target_module, span: None, found_use: false };
|
let mut finder =
|
||||||
if let ControlFlow::Continue(..) = finder.check_mod(&krate.items, CRATE_NODE_ID) {
|
UsePlacementFinder { target_module, first_legal_span: None, first_use_span: None };
|
||||||
visit::walk_crate(&mut finder, krate);
|
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<ast::Item>], 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 is_span_suitable_for_use_injection(s: Span) -> bool {
|
||||||
fn visit_item(&mut self, item: &'tcx ast::Item) {
|
// don't suggest placing a use before the prelude
|
||||||
if let ItemKind::Mod(_, ModKind::Loaded(items, ..)) = &item.kind {
|
// import or other generated ones
|
||||||
if let ControlFlow::Break(..) = self.check_mod(items, item.id) {
|
!s.from_expansion()
|
||||||
return;
|
}
|
||||||
|
|
||||||
|
fn search_for_any_use_in_items(items: &[P<ast::Item>]) -> Option<Span> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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}
|
||||||
|
|
63
src/test/ui/proc-macro/amputate-span.rs
Normal file
63
src/test/ui/proc-macro/amputate-span.rs
Normal file
|
@ -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]
|
||||||
|
}
|
||||||
|
}
|
25
src/test/ui/proc-macro/amputate-span.stderr
Normal file
25
src/test/ui/proc-macro/amputate-span.stderr
Normal file
|
@ -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`.
|
14
src/test/ui/proc-macro/auxiliary/amputate-span.rs
Normal file
14
src/test/ui/proc-macro/auxiliary/amputate-span.rs
Normal file
|
@ -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()
|
||||||
|
}
|
|
@ -113,7 +113,7 @@ impl<'a> Parser<'a> {
|
||||||
let result = catch_unwind(AssertUnwindSafe(|| {
|
let result = catch_unwind(AssertUnwindSafe(|| {
|
||||||
let mut parser = new_parser_from_file(sess.inner(), path, Some(span));
|
let mut parser = new_parser_from_file(sess.inner(), path, Some(span));
|
||||||
match parser.parse_mod(&TokenKind::Eof) {
|
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) => {
|
Err(mut e) => {
|
||||||
e.emit();
|
e.emit();
|
||||||
if sess.can_reset_errors() {
|
if sess.can_reset_errors() {
|
||||||
|
|
|
@ -916,7 +916,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
|
||||||
self.push_str(&ident_str);
|
self.push_str(&ident_str);
|
||||||
|
|
||||||
if let ast::ModKind::Loaded(ref items, ast::Inline::Yes, ref spans) = mod_kind {
|
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() {
|
match self.config.brace_style() {
|
||||||
BraceStyle::AlwaysNextLine => {
|
BraceStyle::AlwaysNextLine => {
|
||||||
let indent_str = self.block_indent.to_string_with_newline(self.config);
|
let indent_str = self.block_indent.to_string_with_newline(self.config);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue