From 57494f7c75b4cdfa3cd4d586f606b0898cce4ebd Mon Sep 17 00:00:00 2001
From: Ken Matsui <26405363+ken-matsui@users.noreply.github.com>
Date: Sat, 13 Nov 2021 16:39:54 +0900
Subject: [PATCH] Suggestion to wrap inner types using `allocator_api` in tuple

---
 compiler/rustc_middle/src/middle/stability.rs | 61 ++++++++++++++++---
 compiler/rustc_resolve/src/macros.rs          |  1 +
 compiler/rustc_span/src/symbol.rs             |  1 +
 .../suggest-vec-allocator-api.rs              |  9 +++
 .../suggest-vec-allocator-api.stderr          | 49 +++++++++++++++
 5 files changed, 113 insertions(+), 8 deletions(-)
 create mode 100644 src/test/ui/stability-attribute/suggest-vec-allocator-api.rs
 create mode 100644 src/test/ui/stability-attribute/suggest-vec-allocator-api.stderr

diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs
index 597622b2ebf..8a5fc5feeb7 100644
--- a/compiler/rustc_middle/src/middle/stability.rs
+++ b/compiler/rustc_middle/src/middle/stability.rs
@@ -3,7 +3,7 @@
 
 pub use self::StabilityLevel::*;
 
-use crate::ty::{self, TyCtxt};
+use crate::ty::{self, DefIdTree, TyCtxt};
 use rustc_ast::NodeId;
 use rustc_attr::{self as attr, ConstStability, Deprecation, Stability};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
@@ -90,6 +90,7 @@ pub fn report_unstable(
     feature: Symbol,
     reason: Option<Symbol>,
     issue: Option<NonZeroU32>,
+    suggestion: Option<(Span, String, String, Applicability)>,
     is_soft: bool,
     span: Span,
     soft_handler: impl FnOnce(&'static Lint, Span, &str),
@@ -116,8 +117,12 @@ pub fn report_unstable(
         if is_soft {
             soft_handler(SOFT_UNSTABLE, span, &msg)
         } else {
-            feature_err_issue(&sess.parse_sess, feature, span, GateIssue::Library(issue), &msg)
-                .emit();
+            let mut err =
+                feature_err_issue(&sess.parse_sess, feature, span, GateIssue::Library(issue), &msg);
+            if let Some((inner_types, ref msg, sugg, applicability)) = suggestion {
+                err.span_suggestion(inner_types, msg, sugg, applicability);
+            }
+            err.emit();
         }
     }
 }
@@ -271,7 +276,13 @@ pub enum EvalResult {
     Allow,
     /// We cannot use the item because it is unstable and we did not provide the
     /// corresponding feature gate.
-    Deny { feature: Symbol, reason: Option<Symbol>, issue: Option<NonZeroU32>, is_soft: bool },
+    Deny {
+        feature: Symbol,
+        reason: Option<Symbol>,
+        issue: Option<NonZeroU32>,
+        suggestion: Option<(Span, String, String, Applicability)>,
+        is_soft: bool,
+    },
     /// The item does not have the `#[stable]` or `#[unstable]` marker assigned.
     Unmarked,
 }
@@ -292,6 +303,32 @@ fn skip_stability_check_due_to_privacy(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
     }
 }
 
+// See issue #83250.
+fn suggestion_for_allocator_api(
+    tcx: TyCtxt<'_>,
+    def_id: DefId,
+    span: Span,
+    feature: Symbol,
+) -> Option<(Span, String, String, Applicability)> {
+    if feature == sym::allocator_api {
+        if let Some(trait_) = tcx.parent(def_id) {
+            if tcx.is_diagnostic_item(sym::Vec, trait_) {
+                let sm = tcx.sess.parse_sess.source_map();
+                let inner_types = sm.span_extend_to_prev_char(span, '<', true);
+                if let Ok(snippet) = sm.span_to_snippet(inner_types) {
+                    return Some((
+                        inner_types,
+                        "consider wrapping the inner types in tuple".to_string(),
+                        format!("({})", snippet),
+                        Applicability::MaybeIncorrect,
+                    ));
+                }
+            }
+        }
+    }
+    None
+}
+
 impl<'tcx> TyCtxt<'tcx> {
     /// Evaluates the stability of an item.
     ///
@@ -406,7 +443,8 @@ impl<'tcx> TyCtxt<'tcx> {
                     }
                 }
 
-                EvalResult::Deny { feature, reason, issue, is_soft }
+                let suggestion = suggestion_for_allocator_api(self, def_id, span, feature);
+                EvalResult::Deny { feature, reason, issue, suggestion, is_soft }
             }
             Some(_) => {
                 // Stable APIs are always ok to call and deprecated APIs are
@@ -457,9 +495,16 @@ impl<'tcx> TyCtxt<'tcx> {
         };
         match self.eval_stability(def_id, id, span, method_span) {
             EvalResult::Allow => {}
-            EvalResult::Deny { feature, reason, issue, is_soft } => {
-                report_unstable(self.sess, feature, reason, issue, is_soft, span, soft_handler)
-            }
+            EvalResult::Deny { feature, reason, issue, suggestion, is_soft } => report_unstable(
+                self.sess,
+                feature,
+                reason,
+                issue,
+                suggestion,
+                is_soft,
+                span,
+                soft_handler,
+            ),
             EvalResult::Unmarked => unmarked(span, def_id),
         }
     }
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index 31fd9b989e1..28dbce0471e 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -1133,6 +1133,7 @@ impl<'a> Resolver<'a> {
                         feature,
                         reason,
                         issue,
+                        None,
                         is_soft,
                         span,
                         soft_handler,
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 99fa9f00094..eb4d5d17674 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -308,6 +308,7 @@ symbols! {
         alloc_layout,
         alloc_zeroed,
         allocator,
+        allocator_api,
         allocator_internals,
         allow,
         allow_fail,
diff --git a/src/test/ui/stability-attribute/suggest-vec-allocator-api.rs b/src/test/ui/stability-attribute/suggest-vec-allocator-api.rs
new file mode 100644
index 00000000000..fac52ab77c6
--- /dev/null
+++ b/src/test/ui/stability-attribute/suggest-vec-allocator-api.rs
@@ -0,0 +1,9 @@
+fn main() {
+    let _: Vec<u8, _> = vec![]; //~ ERROR use of unstable library feature 'allocator_api'
+    #[rustfmt::skip]
+    let _: Vec<
+        String,
+        _> = vec![]; //~ ERROR use of unstable library feature 'allocator_api'
+    let _ = Vec::<u16, _>::new(); //~ ERROR use of unstable library feature 'allocator_api'
+    let _boxed: Box<u32, _> = Box::new(10); //~ ERROR use of unstable library feature 'allocator_api'
+}
diff --git a/src/test/ui/stability-attribute/suggest-vec-allocator-api.stderr b/src/test/ui/stability-attribute/suggest-vec-allocator-api.stderr
new file mode 100644
index 00000000000..41e5787b8c2
--- /dev/null
+++ b/src/test/ui/stability-attribute/suggest-vec-allocator-api.stderr
@@ -0,0 +1,49 @@
+error[E0658]: use of unstable library feature 'allocator_api'
+  --> $DIR/suggest-vec-allocator-api.rs:2:20
+   |
+LL |     let _: Vec<u8, _> = vec![];
+   |                ----^
+   |                |
+   |                help: consider wrapping the inner types in tuple: `(u8, _)`
+   |
+   = note: see issue #32838 <https://github.com/rust-lang/rust/issues/32838> for more information
+   = help: add `#![feature(allocator_api)]` to the crate attributes to enable
+
+error[E0658]: use of unstable library feature 'allocator_api'
+  --> $DIR/suggest-vec-allocator-api.rs:6:9
+   |
+LL |         _> = vec![];
+   |         ^
+   |
+   = note: see issue #32838 <https://github.com/rust-lang/rust/issues/32838> for more information
+   = help: add `#![feature(allocator_api)]` to the crate attributes to enable
+help: consider wrapping the inner types in tuple
+   |
+LL ~     let _: Vec<(
+LL +         String,
+LL ~         _)> = vec![];
+   |
+
+error[E0658]: use of unstable library feature 'allocator_api'
+  --> $DIR/suggest-vec-allocator-api.rs:8:26
+   |
+LL |     let _boxed: Box<u32, _> = Box::new(10);
+   |                          ^
+   |
+   = note: see issue #32838 <https://github.com/rust-lang/rust/issues/32838> for more information
+   = help: add `#![feature(allocator_api)]` to the crate attributes to enable
+
+error[E0658]: use of unstable library feature 'allocator_api'
+  --> $DIR/suggest-vec-allocator-api.rs:7:24
+   |
+LL |     let _ = Vec::<u16, _>::new();
+   |                   -----^
+   |                   |
+   |                   help: consider wrapping the inner types in tuple: `(u16, _)`
+   |
+   = note: see issue #32838 <https://github.com/rust-lang/rust/issues/32838> for more information
+   = help: add `#![feature(allocator_api)]` to the crate attributes to enable
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0658`.