about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs66
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs16
-rw-r--r--compiler/rustc_resolve/src/imports.rs50
-rw-r--r--compiler/rustc_span/src/lib.rs1
-rw-r--r--compiler/rustc_span/src/source_map.rs19
-rw-r--r--compiler/rustc_typeck/src/check/demand.rs83
6 files changed, 185 insertions, 50 deletions
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 5d9e7aaf72f..c8e898c6849 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -649,30 +649,74 @@ pub trait PrettyPrinter<'tcx>:
 
                     let mut first = true;
                     let mut is_sized = false;
+                    let mut is_future = false;
+                    let mut future_output_ty = None;
+
                     p!("impl");
                     for (predicate, _) in bounds {
                         let predicate = predicate.subst(self.tcx(), substs);
                         let bound_predicate = predicate.kind();
-                        if let ty::PredicateKind::Trait(pred) = bound_predicate.skip_binder() {
-                            let trait_ref = bound_predicate.rebind(pred.trait_ref);
-                            // Don't print +Sized, but rather +?Sized if absent.
-                            if Some(trait_ref.def_id()) == self.tcx().lang_items().sized_trait() {
-                                is_sized = true;
-                                continue;
+
+                        match bound_predicate.skip_binder() {
+                            ty::PredicateKind::Projection(projection_predicate) => {
+                                let Some(future_trait) = self.tcx().lang_items().future_trait() else { continue };
+                                let future_output_def_id =
+                                    self.tcx().associated_item_def_ids(future_trait)[0];
+
+                                if projection_predicate.projection_ty.item_def_id
+                                    == future_output_def_id
+                                {
+                                    // We don't account for multiple `Future::Output = Ty` contraints.
+                                    is_future = true;
+                                    future_output_ty = Some(projection_predicate.ty);
+                                }
                             }
+                            ty::PredicateKind::Trait(pred) => {
+                                let trait_ref = bound_predicate.rebind(pred.trait_ref);
+                                // Don't print +Sized, but rather +?Sized if absent.
+                                if Some(trait_ref.def_id()) == self.tcx().lang_items().sized_trait()
+                                {
+                                    is_sized = true;
+                                    continue;
+                                }
 
-                            p!(
-                                write("{}", if first { " " } else { "+" }),
-                                print(trait_ref.print_only_trait_path())
-                            );
-                            first = false;
+                                if Some(trait_ref.def_id())
+                                    == self.tcx().lang_items().future_trait()
+                                {
+                                    is_future = true;
+                                    continue;
+                                }
+
+                                p!(
+                                    write("{}", if first { " " } else { "+" }),
+                                    print(trait_ref.print_only_trait_path())
+                                );
+
+                                first = false;
+                            }
+                            _ => {}
                         }
                     }
+
+                    if is_future {
+                        p!(write("{}Future", if first { " " } else { "+" }));
+                        first = false;
+
+                        if let Some(future_output_ty) = future_output_ty {
+                            // Don't print projection types, which we (unfortunately) see often
+                            // in the error outputs involving async blocks.
+                            if !matches!(future_output_ty.kind(), ty::Projection(_)) {
+                                p!("<Output = ", print(future_output_ty), ">");
+                            }
+                        }
+                    }
+
                     if !is_sized {
                         p!(write("{}?Sized", if first { " " } else { "+" }));
                     } else if first {
                         p!(" Sized");
                     }
+
                     Ok(self)
                 });
             }
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index c46a18e5103..2e4cb4ff727 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -450,12 +450,24 @@ impl<'a> Resolver<'a> {
                 // let foo =...
                 //     ^^^ given this Span
                 // ------- get this Span to have an applicable suggestion
+
+                // edit:
+                // only do this if the const and usage of the non-constant value are on the same line
+                // the further the two are apart, the higher the chance of the suggestion being wrong
+                // also make sure that the pos for the suggestion is not 0 (ICE #90878)
+
                 let sp =
                     self.session.source_map().span_extend_to_prev_str(ident.span, current, true);
-                if sp.lo().0 == 0 {
+
+                let pos_for_suggestion = sp.lo().0.saturating_sub(current.len() as u32);
+
+                if sp.lo().0 == 0
+                    || pos_for_suggestion == 0
+                    || self.session.source_map().is_multiline(sp)
+                {
                     err.span_label(ident.span, &format!("this would need to be a `{}`", sugg));
                 } else {
-                    let sp = sp.with_lo(BytePos(sp.lo().0 - current.len() as u32));
+                    let sp = sp.with_lo(BytePos(pos_for_suggestion));
                     err.span_suggestion(
                         sp,
                         &format!("consider using `{}` instead of `{}`", sugg, current),
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index 4262c1e9051..bf4cece8bde 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -1180,11 +1180,17 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
 
         let mut reexport_error = None;
         let mut any_successful_reexport = false;
+        let mut crate_private_reexport = false;
         self.r.per_ns(|this, ns| {
             if let Ok(binding) = source_bindings[ns].get() {
                 let vis = import.vis.get();
                 if !binding.vis.is_at_least(vis, &*this) {
                     reexport_error = Some((ns, binding));
+                    if let ty::Visibility::Restricted(binding_def_id) = binding.vis {
+                        if binding_def_id.is_top_level_module() {
+                            crate_private_reexport = true;
+                        }
+                    }
                 } else {
                     any_successful_reexport = true;
                 }
@@ -1207,24 +1213,34 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
                     import.span,
                     &msg,
                 );
-            } else if ns == TypeNS {
-                struct_span_err!(
-                    self.r.session,
-                    import.span,
-                    E0365,
-                    "`{}` is private, and cannot be re-exported",
-                    ident
-                )
-                .span_label(import.span, format!("re-export of private `{}`", ident))
-                .note(&format!("consider declaring type or module `{}` with `pub`", ident))
-                .emit();
             } else {
-                let msg = format!("`{}` is private, and cannot be re-exported", ident);
-                let note_msg =
-                    format!("consider marking `{}` as `pub` in the imported module", ident,);
-                struct_span_err!(self.r.session, import.span, E0364, "{}", &msg)
-                    .span_note(import.span, &note_msg)
-                    .emit();
+                let error_msg = if crate_private_reexport {
+                    format!(
+                        "`{}` is only public within the crate, and cannot be re-exported outside",
+                        ident
+                    )
+                } else {
+                    format!("`{}` is private, and cannot be re-exported", ident)
+                };
+
+                if ns == TypeNS {
+                    let label_msg = if crate_private_reexport {
+                        format!("re-export of crate public `{}`", ident)
+                    } else {
+                        format!("re-export of private `{}`", ident)
+                    };
+
+                    struct_span_err!(self.r.session, import.span, E0365, "{}", error_msg)
+                        .span_label(import.span, label_msg)
+                        .note(&format!("consider declaring type or module `{}` with `pub`", ident))
+                        .emit();
+                } else {
+                    let note_msg =
+                        format!("consider marking `{}` as `pub` in the imported module", ident);
+                    struct_span_err!(self.r.session, import.span, E0364, "{}", error_msg)
+                        .span_note(import.span, &note_msg)
+                        .emit();
+                }
             }
         }
 
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index dfc64f37e4c..1445c59710c 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -1935,6 +1935,7 @@ pub struct Loc {
 #[derive(Debug)]
 pub struct SourceFileAndLine {
     pub sf: Lrc<SourceFile>,
+    /// Index of line, starting from 0.
     pub line: usize,
 }
 #[derive(Debug)]
diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs
index 74958c49849..7414d201f51 100644
--- a/compiler/rustc_span/src/source_map.rs
+++ b/compiler/rustc_span/src/source_map.rs
@@ -593,14 +593,19 @@ impl SourceMap {
     }
 
     pub fn span_to_margin(&self, sp: Span) -> Option<usize> {
-        match self.span_to_prev_source(sp) {
-            Err(_) => None,
-            Ok(source) => {
-                let last_line = source.rsplit_once('\n').unwrap_or(("", &source)).1;
+        Some(self.indentation_before(sp)?.len())
+    }
 
-                Some(last_line.len() - last_line.trim_start().len())
-            }
-        }
+    pub fn indentation_before(&self, sp: Span) -> Option<String> {
+        self.span_to_source(sp, |src, start_index, _| {
+            let before = &src[..start_index];
+            let last_line = before.rsplit_once('\n').map_or(before, |(_, last)| last);
+            Ok(last_line
+                .split_once(|c: char| !c.is_whitespace())
+                .map_or(last_line, |(indent, _)| indent)
+                .to_string())
+        })
+        .ok()
     }
 
     /// Returns the source snippet as `String` before the given `Span`.
diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs
index 9bbe5259147..ece2d7b4f37 100644
--- a/compiler/rustc_typeck/src/check/demand.rs
+++ b/compiler/rustc_typeck/src/check/demand.rs
@@ -199,7 +199,50 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 return;
             }
 
-            let mut compatible_variants = expected_adt
+            // If the expression is of type () and it's the return expression of a block,
+            // we suggest adding a separate return expression instead.
+            // (To avoid things like suggesting `Ok(while .. { .. })`.)
+            if expr_ty.is_unit() {
+                if let Some(hir::Node::Block(&hir::Block {
+                    span: block_span, expr: Some(e), ..
+                })) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id))
+                {
+                    if e.hir_id == expr.hir_id {
+                        if let Some(span) = expr.span.find_ancestor_inside(block_span) {
+                            let return_suggestions =
+                                if self.tcx.is_diagnostic_item(sym::Result, expected_adt.did) {
+                                    vec!["Ok(())".to_string()]
+                                } else if self.tcx.is_diagnostic_item(sym::Option, expected_adt.did)
+                                {
+                                    vec!["None".to_string(), "Some(())".to_string()]
+                                } else {
+                                    return;
+                                };
+                            if let Some(indent) =
+                                self.tcx.sess.source_map().indentation_before(span.shrink_to_lo())
+                            {
+                                // Add a semicolon, except after `}`.
+                                let semicolon =
+                                    match self.tcx.sess.source_map().span_to_snippet(span) {
+                                        Ok(s) if s.ends_with('}') => "",
+                                        _ => ";",
+                                    };
+                                err.span_suggestions(
+                                    span.shrink_to_hi(),
+                                    "try adding an expression at the end of the block",
+                                    return_suggestions
+                                        .into_iter()
+                                        .map(|r| format!("{}\n{}{}", semicolon, indent, r)),
+                                    Applicability::MaybeIncorrect,
+                                );
+                            }
+                            return;
+                        }
+                    }
+                }
+            }
+
+            let compatible_variants: Vec<String> = expected_adt
                 .variants
                 .iter()
                 .filter(|variant| variant.fields.len() == 1)
@@ -220,19 +263,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         None
                     }
                 })
-                .peekable();
+                .collect();
 
-            if compatible_variants.peek().is_some() {
-                if let Ok(expr_text) = self.tcx.sess.source_map().span_to_snippet(expr.span) {
-                    let suggestions = compatible_variants.map(|v| format!("{}({})", v, expr_text));
-                    let msg = "try using a variant of the expected enum";
-                    err.span_suggestions(
-                        expr.span,
-                        msg,
-                        suggestions,
-                        Applicability::MaybeIncorrect,
-                    );
-                }
+            if let [variant] = &compatible_variants[..] {
+                // Just a single matching variant.
+                err.multipart_suggestion(
+                    &format!("try wrapping the expression in `{}`", variant),
+                    vec![
+                        (expr.span.shrink_to_lo(), format!("{}(", variant)),
+                        (expr.span.shrink_to_hi(), ")".to_string()),
+                    ],
+                    Applicability::MaybeIncorrect,
+                );
+            } else if compatible_variants.len() > 1 {
+                // More than one matching variant.
+                err.multipart_suggestions(
+                    &format!(
+                        "try wrapping the expression in a variant of `{}`",
+                        self.tcx.def_path_str(expected_adt.did)
+                    ),
+                    compatible_variants.into_iter().map(|variant| {
+                        vec![
+                            (expr.span.shrink_to_lo(), format!("{}(", variant)),
+                            (expr.span.shrink_to_hi(), ")".to_string()),
+                        ]
+                    }),
+                    Applicability::MaybeIncorrect,
+                );
             }
         }
     }