Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More robust fallback for use suggestion #94584

Merged
merged 8 commits into from
Mar 15, 2022
18 changes: 16 additions & 2 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,7 @@ pub struct WhereEqPredicate {
pub struct Crate {
pub attrs: Vec<Attribute>,
pub items: Vec<P<Item>>,
pub span: Span,
pub spans: ModSpans,
/// Must be equal to `CRATE_NODE_ID` after the crate root is expanded, but may hold
/// expansion placeholders or an unassigned value (`DUMMY_NODE_ID`) before that.
pub id: NodeId,
Expand Down Expand Up @@ -2317,11 +2317,25 @@ pub enum ModKind {
/// or with definition outlined to a separate file `mod foo;` and already loaded from it.
/// The inner span is from the first token past `{` to the last token until `}`,
/// or from the first to the last token in the loaded file.
Loaded(Vec<P<Item>>, Inline, Span),
Loaded(Vec<P<Item>>, Inline, ModSpans),
/// Module with definition outlined to a separate file `mod foo;` but not yet loaded from it.
Unloaded,
}

#[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(), inject_use_span: Default::default() }
}
}

/// Foreign module declaration.
///
/// E.g., `extern { .. }` or `extern "C" { .. }`.
Expand Down
11 changes: 7 additions & 4 deletions compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1009,8 +1009,9 @@ pub fn noop_visit_item_kind<T: MutVisitor>(kind: &mut ItemKind, vis: &mut T) {
ItemKind::Mod(unsafety, mod_kind) => {
visit_unsafety(unsafety, vis);
match mod_kind {
ModKind::Loaded(items, _inline, 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 => {}
Expand Down Expand Up @@ -1108,11 +1109,13 @@ pub fn noop_visit_fn_header<T: MutVisitor>(header: &mut FnHeader, vis: &mut T) {
}

pub fn noop_visit_crate<T: MutVisitor>(krate: &mut Crate, vis: &mut T) {
let Crate { attrs, items, span, id, is_placeholder: _ } = krate;
let Crate { attrs, items, spans, id, is_placeholder: _ } = krate;
vis.visit_id(id);
visit_attrs(attrs, vis);
items.flat_map_in_place(|item| vis.flat_map_item(item));
vis.visit_span(span);
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.
Expand Down Expand Up @@ -1536,7 +1539,7 @@ impl DummyAstNode for Crate {
Crate {
attrs: Default::default(),
items: Default::default(),
span: Default::default(),
spans: Default::default(),
id: DUMMY_NODE_ID,
is_placeholder: Default::default(),
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
})
}
ItemKind::Mod(_, ref mod_kind) => match mod_kind {
ModKind::Loaded(items, _, 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"),
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
visit::walk_crate(&mut item::ItemLowerer { lctx: &mut self }, c);

self.with_hir_id_owner(CRATE_NODE_ID, |lctx| {
let module = lctx.lower_mod(&c.items, c.span);
let module = lctx.lower_mod(&c.items, c.spans.inner_span);
lctx.lower_attrs(hir::CRATE_HIR_ID, &c.attrs);
hir::OwnerNode::Crate(lctx.arena.alloc(module))
});
Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_builtin_macros/src/test_harness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
fn visit_crate(&mut self, c: &mut ast::Crate) {
let prev_tests = mem::take(&mut self.tests);
noop_visit_crate(c, self);
self.add_test_cases(ast::CRATE_NODE_ID, c.span, prev_tests);
self.add_test_cases(ast::CRATE_NODE_ID, c.spans.inner_span, prev_tests);

// Create a main function to run our tests
c.items.push(mk_main(&mut self.cx));
Expand All @@ -129,7 +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(.., 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);
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_expand/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ impl Annotatable {
Annotatable::Param(ref p) => p.span,
Annotatable::FieldDef(ref sf) => sf.span,
Annotatable::Variant(ref v) => v.span,
Annotatable::Crate(ref c) => c.span,
Annotatable::Crate(ref c) => c.spans.inner_span,
}
}

Expand Down
10 changes: 5 additions & 5 deletions compiler/rustc_expand/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ use rustc_ast::token;
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::visit::{self, AssocCtxt, Visitor};
use rustc_ast::{AssocItemKind, AstLike, AstLikeWrapper, AttrStyle, ExprKind, ForeignItemKind};
use rustc_ast::{Inline, ItemKind, MacArgs, MacStmtStyle, MetaItemKind, ModKind, NestedMetaItem};
use rustc_ast::{NodeId, PatKind, StmtKind, TyKind};
use rustc_ast::{Inline, ItemKind, MacArgs, MacStmtStyle, MetaItemKind, ModKind};
use rustc_ast::{NestedMetaItem, NodeId, PatKind, StmtKind, TyKind};
use rustc_ast_pretty::pprust;
use rustc_data_structures::map_in_place::MapInPlace;
use rustc_data_structures::sync::Lrc;
Expand Down Expand Up @@ -364,7 +364,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
}

pub fn expand_crate(&mut self, krate: ast::Crate) -> ast::Crate {
let file_path = match self.cx.source_map().span_to_filename(krate.span) {
let file_path = match self.cx.source_map().span_to_filename(krate.spans.inner_span) {
FileName::Real(name) => name
.into_local_path()
.expect("attempting to resolve a file path in an external file"),
Expand Down Expand Up @@ -1091,7 +1091,7 @@ impl InvocationCollectorNode for P<ast::Item> {
ModKind::Unloaded => {
// We have an outline `mod foo;` so we need to parse the file.
let old_attrs_len = attrs.len();
let ParsedExternalMod { items, inner_span, file_path, dir_path, dir_ownership } =
let ParsedExternalMod { items, spans, file_path, dir_path, dir_ownership } =
parse_external_mod(
&ecx.sess,
ident,
Expand All @@ -1112,7 +1112,7 @@ impl InvocationCollectorNode for P<ast::Item> {
);
}

*mod_kind = ModKind::Loaded(items, Inline::No, inner_span);
*mod_kind = ModKind::Loaded(items, Inline::No, spans);
node.attrs = attrs;
if node.attrs.len() > old_attrs_len {
// If we loaded an out-of-line module and added some inner attributes,
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_expand/src/module.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::base::ModuleData;
use rustc_ast::ptr::P;
use rustc_ast::{token, Attribute, Inline, Item};
use rustc_ast::{token, Attribute, Inline, Item, ModSpans};
use rustc_errors::{struct_span_err, DiagnosticBuilder, ErrorGuaranteed};
use rustc_parse::new_parser_from_file;
use rustc_parse::validate_attr;
Expand Down Expand Up @@ -28,7 +28,7 @@ pub struct ModulePathSuccess {

crate struct ParsedExternalMod {
pub items: Vec<P<Item>>,
pub inner_span: Span,
pub spans: ModSpans,
pub file_path: PathBuf,
pub dir_path: PathBuf,
pub dir_ownership: DirOwnership,
Expand Down Expand Up @@ -69,13 +69,13 @@ crate fn parse_external_mod(
(items, inner_span, mp.file_path)
};
// (1) ...instead, we return a dummy module.
let (items, inner_span, file_path) =
let (items, spans, file_path) =
result.map_err(|err| err.report(sess, span)).unwrap_or_default();

// Extract the directory path for submodules of the module.
let dir_path = file_path.parent().unwrap_or(&file_path).to_owned();

ParsedExternalMod { items, inner_span, file_path, dir_path, dir_ownership }
ParsedExternalMod { items, spans, file_path, dir_path, dir_ownership }
}

crate fn mod_dir_path(
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_expand/src/placeholders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ pub fn placeholder(
AstFragmentKind::Crate => AstFragment::Crate(ast::Crate {
attrs: Default::default(),
items: Default::default(),
span,
spans: ast::ModSpans { inner_span: span, ..Default::default() },
id,
is_placeholder: true,
}),
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_metadata/src/creader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -899,7 +899,7 @@ impl<'a> CrateLoader<'a> {

fn report_unused_deps(&mut self, krate: &ast::Crate) {
// Make a point span rather than covering the whole file
let span = krate.span.shrink_to_lo();
let span = krate.spans.inner_span.shrink_to_lo();
// Complain about anything left over
for (name, entry) in self.sess.opts.externs.iter() {
if let ExternLocation::FoundInLibrarySearchDirectories = entry.location {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_parse/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ pub fn fake_token_stream(sess: &ParseSess, nt: &Nonterminal) -> TokenStream {
pub fn fake_token_stream_for_crate(sess: &ParseSess, krate: &ast::Crate) -> TokenStream {
let source = pprust::crate_to_string_for_macros(krate);
let filename = FileName::macro_expansion_source_code(&source);
parse_stream_from_source_str(filename, source, sess, Some(krate.span))
parse_stream_from_source_str(filename, source, sess, Some(krate.spans.inner_span))
}

pub fn parse_cfg_attr(
Expand Down
11 changes: 7 additions & 4 deletions compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ use tracing::debug;
impl<'a> Parser<'a> {
/// Parses a source module as a crate. This is the main entry point for the parser.
pub fn parse_crate_mod(&mut self) -> PResult<'a, ast::Crate> {
let (attrs, items, span) = self.parse_mod(&token::Eof)?;
Ok(ast::Crate { attrs, items, span, id: DUMMY_NODE_ID, is_placeholder: false })
let (attrs, items, spans) = self.parse_mod(&token::Eof)?;
Ok(ast::Crate { attrs, items, spans, id: DUMMY_NODE_ID, is_placeholder: false })
}

/// Parses a `mod <foo> { ... }` or `mod <foo>;` item.
Expand All @@ -51,10 +51,11 @@ impl<'a> Parser<'a> {
pub fn parse_mod(
&mut self,
term: &TokenKind,
) -> PResult<'a, (Vec<Attribute>, Vec<P<Item>>, Span)> {
) -> PResult<'a, (Vec<Attribute>, Vec<P<Item>>, ModSpans)> {
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);
Expand All @@ -71,7 +72,9 @@ impl<'a> Parser<'a> {
}
}

Ok((attrs, items, 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))
}
}

Expand Down
105 changes: 50 additions & 55 deletions compiler/rustc_resolve/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -315,74 +314,70 @@ impl<'a> From<&'a ast::PathSegment> for Segment {
}
}

#[derive(Debug)]
struct UsePlacementFinder {
target_module: NodeId,
span: Option<Span>,
found_use: bool,
first_legal_span: Option<Span>,
first_use_span: Option<Span>,
}

impl UsePlacementFinder {
fn check(krate: &Crate, target_module: NodeId) -> (Option<Span>, 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);
}
(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());
}
}
}
}
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)
}
}
}

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<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());
}
}
ControlFlow::Continue(())
}
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 let ItemKind::Mod(_, ModKind::Loaded(items, ..)) = &item.kind {
if let ControlFlow::Break(..) = self.check_mod(items, item.id) {
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);
}
visit::walk_item(self, item);
}
}

Expand Down Expand Up @@ -1282,7 +1277,7 @@ impl<'a> Resolver<'a> {
None,
ModuleKind::Def(DefKind::Mod, root_def_id, kw::Empty),
ExpnId::root(),
krate.span,
krate.spans.inner_span,
session.contains_name(&krate.attrs, sym::no_implicit_prelude),
&mut module_map,
);
Expand All @@ -1295,7 +1290,7 @@ impl<'a> Resolver<'a> {
&mut FxHashMap::default(),
);

let definitions = Definitions::new(session.local_stable_crate_id(), krate.span);
let definitions = Definitions::new(session.local_stable_crate_id(), krate.spans.inner_span);
let root = definitions.get_root_def();

let mut visibilities = FxHashMap::default();
Expand Down
Loading