about summary refs log tree commit diff
path: root/compiler/rustc_resolve
diff options
context:
space:
mode:
authorFelix S. Klock II <pnkfelix@pnkfx.org>2022-01-20 14:07:54 -0500
committerFelix S. Klock II <pnkfelix@pnkfx.org>2022-03-03 18:58:37 -0500
commitd37da1e332a77a4cd66c2f36b4a5457f40a7bfd5 (patch)
treef3638bd95437411b3a0c926e0ce9a864997542e9 /compiler/rustc_resolve
parentb82795244e31ce1ad60bbb823c4e4b91f921c296 (diff)
downloadrust-d37da1e332a77a4cd66c2f36b4a5457f40a7bfd5.tar.gz
rust-d37da1e332a77a4cd66c2f36b4a5457f40a7bfd5.zip
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.
Diffstat (limited to 'compiler/rustc_resolve')
-rw-r--r--compiler/rustc_resolve/src/lib.rs101
1 files changed, 48 insertions, 53 deletions
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<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);
     }
 }