about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_ast/src/ast.rs7
-rw-r--r--compiler/rustc_attr/src/builtin.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs53
-rw-r--r--compiler/rustc_macros/src/lib.rs1
-rw-r--r--compiler/rustc_macros/src/symbols.rs158
-rw-r--r--compiler/rustc_macros/src/symbols/tests.rs2
-rw-r--r--compiler/rustc_passes/src/lib_features.rs4
-rw-r--r--compiler/rustc_passes/src/stability.rs6
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs53
10 files changed, 211 insertions, 78 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index e94e26a63bc..e09ba03d881 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -734,6 +734,8 @@ pub enum RangeSyntax {
 }
 
 /// All the different flavors of pattern that Rust recognizes.
+//
+// Adding a new variant? Please update `test_pat` in `tests/ui/macros/stringify.rs`.
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub enum PatKind {
     /// Represents a wildcard pattern (`_`).
@@ -967,6 +969,7 @@ impl Stmt {
     }
 }
 
+// Adding a new variant? Please update `test_stmt` in `tests/ui/macros/stringify.rs`.
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub enum StmtKind {
     /// A local (let) binding.
@@ -1345,6 +1348,7 @@ pub struct StructExpr {
     pub rest: StructRest,
 }
 
+// Adding a new variant? Please update `test_expr` in `tests/ui/macros/stringify.rs`.
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub enum ExprKind {
     /// An array (`[a, b, c, d]`)
@@ -2015,6 +2019,8 @@ pub struct BareFnTy {
 }
 
 /// The various kinds of type recognized by the compiler.
+//
+// Adding a new variant? Please update `test_ty` in `tests/ui/macros/stringify.rs`.
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub enum TyKind {
     /// A variable-length slice (`[T]`).
@@ -2880,6 +2886,7 @@ pub struct ConstItem {
     pub expr: Option<P<Expr>>,
 }
 
+// Adding a new variant? Please update `test_item` in `tests/ui/macros/stringify.rs`.
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub enum ItemKind {
     /// An `extern crate` item, with the optional *original* crate name if the crate was renamed.
diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs
index bd85483885e..363ba0d4a81 100644
--- a/compiler/rustc_attr/src/builtin.rs
+++ b/compiler/rustc_attr/src/builtin.rs
@@ -26,10 +26,6 @@ pub const VERSION_PLACEHOLDER: &str = "CURRENT_RUSTC_VERSION";
 
 pub const CURRENT_RUSTC_VERSION: &str = env!("CFG_RELEASE");
 
-pub fn rust_version_symbol() -> Symbol {
-    Symbol::intern(CURRENT_RUSTC_VERSION)
-}
-
 pub fn is_builtin_attr(attr: &Attribute) -> bool {
     attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
 }
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index f379e8477e3..abb83228030 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -962,38 +962,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
     ) -> bool {
-        let ty::Adt(e, args_e) = expected.kind() else {
-            return false;
-        };
-        let ty::Adt(f, args_f) = found.kind() else {
-            return false;
-        };
-        if e.did() != f.did() {
-            return false;
-        }
-        if Some(e.did()) != self.tcx.get_diagnostic_item(sym::Result) {
-            return false;
-        }
         let map = self.tcx.hir();
-        if let Some(hir::Node::Expr(expr)) = map.find_parent(expr.hir_id)
-            && let hir::ExprKind::Ret(_) = expr.kind
-        {
-            // `return foo;`
-        } else if map.get_return_block(expr.hir_id).is_some() {
-            // Function's tail expression.
-        } else {
-            return false;
-        }
-        let e = args_e.type_at(1);
-        let f = args_f.type_at(1);
-        if self
-            .infcx
-            .type_implements_trait(
-                self.tcx.get_diagnostic_item(sym::Into).unwrap(),
-                [f, e],
-                self.param_env,
-            )
-            .must_apply_modulo_regions()
+        let returned = matches!(
+            map.find_parent(expr.hir_id),
+            Some(hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Ret(_), .. }))
+        ) || map.get_return_block(expr.hir_id).is_some();
+        if returned
+            && let ty::Adt(e, args_e) = expected.kind()
+            && let ty::Adt(f, args_f) = found.kind()
+            && e.did() == f.did()
+            && Some(e.did()) == self.tcx.get_diagnostic_item(sym::Result)
+            && let e_ok = args_e.type_at(0)
+            && let f_ok = args_f.type_at(0)
+            && self.infcx.can_eq(self.param_env, f_ok, e_ok)
+            && let e_err = args_e.type_at(1)
+            && let f_err = args_f.type_at(1)
+            && self
+                .infcx
+                .type_implements_trait(
+                    self.tcx.get_diagnostic_item(sym::Into).unwrap(),
+                    [f_err, e_err],
+                    self.param_env,
+                )
+                .must_apply_modulo_regions()
         {
             err.multipart_suggestion(
                 "use `?` to coerce and return an appropriate `Err`, and wrap the resulting value \
diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs
index 85829906f4e..776ba8f9ca1 100644
--- a/compiler/rustc_macros/src/lib.rs
+++ b/compiler/rustc_macros/src/lib.rs
@@ -4,6 +4,7 @@
 #![feature(never_type)]
 #![feature(proc_macro_diagnostic)]
 #![feature(proc_macro_span)]
+#![feature(proc_macro_tracked_env)]
 #![allow(rustc::default_hash_types)]
 #![deny(rustc::untranslatable_diagnostic)]
 #![deny(rustc::diagnostic_outside_of_impl)]
diff --git a/compiler/rustc_macros/src/symbols.rs b/compiler/rustc_macros/src/symbols.rs
index 04facbf657d..4129712a6b2 100644
--- a/compiler/rustc_macros/src/symbols.rs
+++ b/compiler/rustc_macros/src/symbols.rs
@@ -26,7 +26,7 @@ use proc_macro2::{Span, TokenStream};
 use quote::quote;
 use std::collections::HashMap;
 use syn::parse::{Parse, ParseStream, Result};
-use syn::{braced, punctuated::Punctuated, Ident, LitStr, Token};
+use syn::{braced, punctuated::Punctuated, Expr, Ident, Lit, LitStr, Macro, Token};
 
 #[cfg(test)]
 mod tests;
@@ -53,21 +53,46 @@ impl Parse for Keyword {
 
 struct Symbol {
     name: Ident,
-    value: Option<LitStr>,
+    value: Value,
+}
+
+enum Value {
+    SameAsName,
+    String(LitStr),
+    Env(LitStr, Macro),
+    Unsupported(Expr),
 }
 
 impl Parse for Symbol {
     fn parse(input: ParseStream<'_>) -> Result<Self> {
         let name = input.parse()?;
-        let value = match input.parse::<Token![:]>() {
-            Ok(_) => Some(input.parse()?),
-            Err(_) => None,
-        };
+        let colon_token: Option<Token![:]> = input.parse()?;
+        let value = if colon_token.is_some() { input.parse()? } else { Value::SameAsName };
 
         Ok(Symbol { name, value })
     }
 }
 
+impl Parse for Value {
+    fn parse(input: ParseStream<'_>) -> Result<Self> {
+        let expr: Expr = input.parse()?;
+        match &expr {
+            Expr::Lit(expr) => {
+                if let Lit::Str(lit) = &expr.lit {
+                    return Ok(Value::String(lit.clone()));
+                }
+            }
+            Expr::Macro(expr) => {
+                if expr.mac.path.is_ident("env") && let Ok(lit) = expr.mac.parse_body() {
+                    return Ok(Value::Env(lit, expr.mac.clone()));
+                }
+            }
+            _ => {}
+        }
+        Ok(Value::Unsupported(expr))
+    }
+}
+
 struct Input {
     keywords: Punctuated<Keyword, Token![,]>,
     symbols: Punctuated<Symbol, Token![,]>,
@@ -111,6 +136,37 @@ pub fn symbols(input: TokenStream) -> TokenStream {
     output
 }
 
+struct Preinterned {
+    idx: u32,
+    span_of_name: Span,
+}
+
+struct Entries {
+    map: HashMap<String, Preinterned>,
+}
+
+impl Entries {
+    fn with_capacity(capacity: usize) -> Self {
+        Entries { map: HashMap::with_capacity(capacity) }
+    }
+
+    fn insert(&mut self, span: Span, str: &str, errors: &mut Errors) -> u32 {
+        if let Some(prev) = self.map.get(str) {
+            errors.error(span, format!("Symbol `{str}` is duplicated"));
+            errors.error(prev.span_of_name, "location of previous definition".to_string());
+            prev.idx
+        } else {
+            let idx = self.len();
+            self.map.insert(str.to_string(), Preinterned { idx, span_of_name: span });
+            idx
+        }
+    }
+
+    fn len(&self) -> u32 {
+        u32::try_from(self.map.len()).expect("way too many symbols")
+    }
+}
+
 fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec<syn::Error>) {
     let mut errors = Errors::default();
 
@@ -127,20 +183,9 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec<syn::Error>) {
     let mut keyword_stream = quote! {};
     let mut symbols_stream = quote! {};
     let mut prefill_stream = quote! {};
-    let mut counter = 0u32;
-    let mut keys =
-        HashMap::<String, Span>::with_capacity(input.keywords.len() + input.symbols.len() + 10);
+    let mut entries = Entries::with_capacity(input.keywords.len() + input.symbols.len() + 10);
     let mut prev_key: Option<(Span, String)> = None;
 
-    let mut check_dup = |span: Span, str: &str, errors: &mut Errors| {
-        if let Some(prev_span) = keys.get(str) {
-            errors.error(span, format!("Symbol `{str}` is duplicated"));
-            errors.error(*prev_span, "location of previous definition".to_string());
-        } else {
-            keys.insert(str.to_string(), span);
-        }
-    };
-
     let mut check_order = |span: Span, str: &str, errors: &mut Errors| {
         if let Some((prev_span, ref prev_str)) = prev_key {
             if str < prev_str {
@@ -156,49 +201,98 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec<syn::Error>) {
         let name = &keyword.name;
         let value = &keyword.value;
         let value_string = value.value();
-        check_dup(keyword.name.span(), &value_string, &mut errors);
+        let idx = entries.insert(keyword.name.span(), &value_string, &mut errors);
         prefill_stream.extend(quote! {
             #value,
         });
         keyword_stream.extend(quote! {
-            pub const #name: Symbol = Symbol::new(#counter);
+            pub const #name: Symbol = Symbol::new(#idx);
         });
-        counter += 1;
     }
 
     // Generate the listed symbols.
     for symbol in input.symbols.iter() {
         let name = &symbol.name;
+        check_order(symbol.name.span(), &name.to_string(), &mut errors);
+
         let value = match &symbol.value {
-            Some(value) => value.value(),
-            None => name.to_string(),
+            Value::SameAsName => name.to_string(),
+            Value::String(lit) => lit.value(),
+            Value::Env(..) => continue, // in another loop below
+            Value::Unsupported(expr) => {
+                errors.list.push(syn::Error::new_spanned(
+                    expr,
+                    concat!(
+                        "unsupported expression for symbol value; implement support for this in ",
+                        file!(),
+                    ),
+                ));
+                continue;
+            }
         };
-        check_dup(symbol.name.span(), &value, &mut errors);
-        check_order(symbol.name.span(), &name.to_string(), &mut errors);
+        let idx = entries.insert(symbol.name.span(), &value, &mut errors);
 
         prefill_stream.extend(quote! {
             #value,
         });
         symbols_stream.extend(quote! {
-            pub const #name: Symbol = Symbol::new(#counter);
+            pub const #name: Symbol = Symbol::new(#idx);
         });
-        counter += 1;
     }
 
     // Generate symbols for the strings "0", "1", ..., "9".
-    let digits_base = counter;
-    counter += 10;
     for n in 0..10 {
         let n = n.to_string();
-        check_dup(Span::call_site(), &n, &mut errors);
+        entries.insert(Span::call_site(), &n, &mut errors);
         prefill_stream.extend(quote! {
             #n,
         });
     }
 
+    // Symbols whose value comes from an environment variable. It's allowed for
+    // these to have the same value as another symbol.
+    for symbol in &input.symbols {
+        let (env_var, expr) = match &symbol.value {
+            Value::Env(lit, expr) => (lit, expr),
+            Value::SameAsName | Value::String(_) | Value::Unsupported(_) => continue,
+        };
+
+        if !proc_macro::is_available() {
+            errors.error(
+                Span::call_site(),
+                "proc_macro::tracked_env is not available in unit test".to_owned(),
+            );
+            break;
+        }
+
+        let value = match proc_macro::tracked_env::var(env_var.value()) {
+            Ok(value) => value,
+            Err(err) => {
+                errors.list.push(syn::Error::new_spanned(expr, err));
+                continue;
+            }
+        };
+
+        let idx = if let Some(prev) = entries.map.get(&value) {
+            prev.idx
+        } else {
+            prefill_stream.extend(quote! {
+                #value,
+            });
+            entries.insert(symbol.name.span(), &value, &mut errors)
+        };
+
+        let name = &symbol.name;
+        symbols_stream.extend(quote! {
+            pub const #name: Symbol = Symbol::new(#idx);
+        });
+    }
+
+    let symbol_digits_base = entries.map["0"].idx;
+    let preinterned_symbols_count = entries.len();
     let output = quote! {
-        const SYMBOL_DIGITS_BASE: u32 = #digits_base;
-        const PREINTERNED_SYMBOLS_COUNT: u32 = #counter;
+        const SYMBOL_DIGITS_BASE: u32 = #symbol_digits_base;
+        const PREINTERNED_SYMBOLS_COUNT: u32 = #preinterned_symbols_count;
 
         #[doc(hidden)]
         #[allow(non_upper_case_globals)]
diff --git a/compiler/rustc_macros/src/symbols/tests.rs b/compiler/rustc_macros/src/symbols/tests.rs
index bd0c08a53c4..9c53453df5b 100644
--- a/compiler/rustc_macros/src/symbols/tests.rs
+++ b/compiler/rustc_macros/src/symbols/tests.rs
@@ -27,7 +27,7 @@ fn test_symbols() {
 
     let body_tokens = m.mac.tokens.clone();
 
-    test_symbols_macro(body_tokens, &[]);
+    test_symbols_macro(body_tokens, &["proc_macro::tracked_env is not available in unit test"]);
 }
 
 fn test_symbols_macro(input: TokenStream, expected_errors: &[&str]) {
diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs
index ceb8f58cac0..0daa273db67 100644
--- a/compiler/rustc_passes/src/lib_features.rs
+++ b/compiler/rustc_passes/src/lib_features.rs
@@ -5,7 +5,7 @@
 //! collect them instead.
 
 use rustc_ast::Attribute;
-use rustc_attr::{rust_version_symbol, VERSION_PLACEHOLDER};
+use rustc_attr::VERSION_PLACEHOLDER;
 use rustc_hir::intravisit::Visitor;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::middle::lib_features::LibFeatures;
@@ -59,7 +59,7 @@ impl<'tcx> LibFeatureCollector<'tcx> {
                 if let Some(s) = since
                     && s.as_str() == VERSION_PLACEHOLDER
                 {
-                    since = Some(rust_version_symbol());
+                    since = Some(sym::env_CFG_RELEASE);
                 }
 
                 if let Some(feature) = feature {
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 41a240fa880..7bfb0742b8b 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -3,8 +3,8 @@
 
 use crate::errors;
 use rustc_attr::{
-    self as attr, rust_version_symbol, ConstStability, Since, Stability, StabilityLevel, Unstable,
-    UnstableReason, VERSION_PLACEHOLDER,
+    self as attr, ConstStability, Since, Stability, StabilityLevel, Unstable, UnstableReason,
+    VERSION_PLACEHOLDER,
 };
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
 use rustc_hir as hir;
@@ -1115,7 +1115,7 @@ fn unnecessary_stable_feature_lint(
     mut since: Symbol,
 ) {
     if since.as_str() == VERSION_PLACEHOLDER {
-        since = rust_version_symbol();
+        since = sym::env_CFG_RELEASE;
     }
     tcx.emit_spanned_lint(
         lint::builtin::STABLE_FEATURES,
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index ff61143a12b..88d9dab2ba5 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -713,6 +713,7 @@ symbols! {
         encode,
         end,
         env,
+        env_CFG_RELEASE: env!("CFG_RELEASE"),
         eprint_macro,
         eprintln_macro,
         eq,
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index b9a08056ad1..947ea3eece3 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -3465,11 +3465,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         if let Some(Node::Expr(expr)) = hir.find(arg_hir_id)
             && let Some(typeck_results) = &self.typeck_results
         {
-            if let hir::Expr { kind: hir::ExprKind::Block(..), .. } = expr {
-                let expr = expr.peel_blocks();
-                let ty =
-                    typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx));
-                let span = expr.span;
+            if let hir::Expr { kind: hir::ExprKind::Block(block, _), .. } = expr {
+                let inner_expr = expr.peel_blocks();
+                let ty = typeck_results.expr_ty_adjusted_opt(inner_expr)
+                    .unwrap_or(Ty::new_misc_error(tcx));
+                let span = inner_expr.span;
                 if Some(span) != err.span.primary_span() {
                     err.span_label(
                         span,
@@ -3480,6 +3480,49 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                             format!("this tail expression is of type `{ty}`")
                         },
                     );
+                    if let ty::PredicateKind::Clause(clause) = failed_pred.kind().skip_binder()
+                        && let ty::ClauseKind::Trait(pred) = clause
+                        && [
+                            tcx.lang_items().fn_once_trait(),
+                            tcx.lang_items().fn_mut_trait(),
+                            tcx.lang_items().fn_trait(),
+                        ].contains(&Some(pred.def_id()))
+                    {
+                        if let [stmt, ..] = block.stmts
+                            && let hir::StmtKind::Semi(value) = stmt.kind
+                            && let hir::ExprKind::Closure(hir::Closure {
+                                body,
+                                fn_decl_span,
+                                ..
+                            }) = value.kind
+                            && let body = hir.body(*body)
+                            && !matches!(body.value.kind, hir::ExprKind::Block(..))
+                        {
+                            // Check if the failed predicate was an expectation of a closure type
+                            // and if there might have been a `{ |args|` typo instead of `|args| {`.
+                            err.multipart_suggestion(
+                                "you might have meant to open the closure body instead of placing \
+                                 a closure within a block",
+                                vec![
+                                    (expr.span.with_hi(value.span.lo()), String::new()),
+                                    (fn_decl_span.shrink_to_hi(), " {".to_string()),
+                                ],
+                                Applicability::MaybeIncorrect,
+                            );
+                        } else {
+                            // Maybe the bare block was meant to be a closure.
+                            err.span_suggestion_verbose(
+                                expr.span.shrink_to_lo(),
+                                "you might have meant to create the closure instead of a block",
+                                format!(
+                                    "|{}| ",
+                                    (0..pred.trait_ref.args.len() - 1).map(|_| "_")
+                                        .collect::<Vec<_>>()
+                                        .join(", ")),
+                                Applicability::MaybeIncorrect,
+                            );
+                        }
+                    }
                 }
             }