diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index b71ffa43d85dc..16d222f68a321 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -240,17 +240,17 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { } ty::Tuple(ref tys) => { let mut has_emitted = false; - let spans = if let hir::ExprKind::Tup(comps) = &expr.kind { + let comps = if let hir::ExprKind::Tup(comps) = expr.kind { debug_assert_eq!(comps.len(), tys.len()); - comps.iter().map(|e| e.span).collect() + comps } else { - vec![] + &[] }; for (i, ty) in tys.iter().enumerate() { let descr_post = &format!(" in tuple element {}", i); - let span = *spans.get(i).unwrap_or(&span); - if check_must_use_ty(cx, ty, expr, span, descr_pre, descr_post, plural_len) - { + let e = comps.get(i).unwrap_or(expr); + let span = e.span; + if check_must_use_ty(cx, ty, e, span, descr_pre, descr_post, plural_len) { has_emitted = true; } } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 072e6346cdec9..45610fa77d35e 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -368,6 +368,11 @@ pub enum ObligationCauseCode<'tcx> { /// From `match_impl`. The cause for us having to match an impl, and the DefId we are matching against. MatchImpl(ObligationCause<'tcx>, DefId), + + BinOp { + rhs_span: Option, + is_lit: bool, + }, } /// The 'location' at which we try to perform HIR-based wf checking. diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index f26f32aabda1d..f5beb83729f97 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -501,6 +501,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err.span_label(enclosing_scope_span, s.as_str()); } + self.suggest_floating_point_literal(&obligation, &mut err, &trait_ref); self.suggest_dereferences(&obligation, &mut err, trait_predicate); self.suggest_fn_call(&obligation, &mut err, trait_predicate); self.suggest_remove_reference(&obligation, &mut err, trait_predicate); 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 c4fbd25b8338b..9969f610ac81c 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -174,6 +174,13 @@ pub trait InferCtxtExt<'tcx> { trait_pred: ty::PolyTraitPredicate<'tcx>, span: Span, ); + + fn suggest_floating_point_literal( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut DiagnosticBuilder<'_>, + trait_ref: &ty::PolyTraitRef<'tcx>, + ); } fn predicate_constraint(generics: &hir::Generics<'_>, pred: String) -> (Span, String) { @@ -1910,8 +1917,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { | ObligationCauseCode::AwaitableExpr(_) | ObligationCauseCode::ForLoopIterator | ObligationCauseCode::QuestionMark + | ObligationCauseCode::CheckAssociatedTypeBounds { .. } | ObligationCauseCode::LetElse - | ObligationCauseCode::CheckAssociatedTypeBounds { .. } => {} + | ObligationCauseCode::BinOp { .. } => {} ObligationCauseCode::SliceOrArrayElem => { err.note("slice and array elements must have `Sized` type"); } @@ -2497,6 +2505,32 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } } } + + fn suggest_floating_point_literal( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut DiagnosticBuilder<'_>, + trait_ref: &ty::PolyTraitRef<'tcx>, + ) { + let rhs_span = match obligation.cause.code() { + ObligationCauseCode::BinOp { rhs_span: Some(span), is_lit } if *is_lit => span, + _ => return, + }; + match ( + trait_ref.skip_binder().self_ty().kind(), + trait_ref.skip_binder().substs.type_at(1).kind(), + ) { + (ty::Float(_), ty::Infer(InferTy::IntVar(_))) => { + err.span_suggestion_verbose( + rhs_span.shrink_to_hi(), + "consider using a floating-point literal by writing it with `.0`", + String::from(".0"), + Applicability::MaybeIncorrect, + ); + } + _ => {} + } + } } /// Collect all the returned expressions within the input expression. diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index 7dca95ebdd613..8ed87497f1b32 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs @@ -14,7 +14,7 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::layout::MAX_SIMD_LANES; +use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES}; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::util::{Discr, IntTypeExt}; use rustc_middle::ty::{self, OpaqueTypeKey, ParamEnv, Ty, TyCtxt}; @@ -417,10 +417,31 @@ fn check_static_inhabited<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, span: Spa // have UB during initialization if they are uninhabited, but there also seems to be no good // reason to allow any statics to be uninhabited. let ty = tcx.type_of(def_id); - let Ok(layout) = tcx.layout_of(ParamEnv::reveal_all().and(ty)) else { + let layout = match tcx.layout_of(ParamEnv::reveal_all().and(ty)) { + Ok(l) => l, + // Foreign statics that overflow their allowed size should emit an error + Err(LayoutError::SizeOverflow(_)) + if { + let node = tcx.hir().get_by_def_id(def_id); + matches!( + node, + hir::Node::ForeignItem(hir::ForeignItem { + kind: hir::ForeignItemKind::Static(..), + .. + }) + ) + } => + { + tcx.sess + .struct_span_err(span, "extern static is too large for the current architecture") + .emit(); + return; + } // Generic statics are rejected, but we still reach this case. - tcx.sess.delay_span_bug(span, "generic static must be rejected"); - return; + Err(e) => { + tcx.sess.delay_span_bug(span, &e.to_string()); + return; + } }; if layout.abi.is_uninhabited() { tcx.struct_span_lint_hir( diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs index 0fe5e74da89bf..2c81745836cdb 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs @@ -429,6 +429,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) } + pub(in super::super) fn normalize_op_associated_types_in_as_infer_ok( + &self, + span: Span, + value: T, + opt_input_expr: Option<&hir::Expr<'_>>, + ) -> InferOk<'tcx, T> + where + T: TypeFoldable<'tcx>, + { + self.inh.partially_normalize_associated_types_in( + ObligationCause::new( + span, + self.body_id, + traits::BinOp { + rhs_span: opt_input_expr.map(|expr| expr.span), + is_lit: opt_input_expr + .map_or(false, |expr| matches!(expr.kind, ExprKind::Lit(_))), + }, + ), + self.param_env, + value, + ) + } + pub fn require_type_meets( &self, ty: Ty<'tcx>, diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index 999aafbd5375a..7bb84581a82de 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -333,15 +333,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) } + pub(super) fn obligation_for_op_method( + &self, + span: Span, + trait_def_id: DefId, + self_ty: Ty<'tcx>, + opt_input_type: Option>, + opt_input_expr: Option<&'tcx hir::Expr<'tcx>>, + ) -> (traits::Obligation<'tcx, ty::Predicate<'tcx>>, &'tcx ty::List>) + { + // Construct a trait-reference `self_ty : Trait` + let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| { + match param.kind { + GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => {} + GenericParamDefKind::Type { .. } => { + if param.index == 0 { + return self_ty.into(); + } else if let Some(input_type) = opt_input_type { + return input_type.into(); + } + } + } + self.var_for_def(span, param) + }); + + let trait_ref = ty::TraitRef::new(trait_def_id, substs); + + // Construct an obligation + let poly_trait_ref = ty::Binder::dummy(trait_ref); + ( + traits::Obligation::new( + traits::ObligationCause::new( + span, + self.body_id, + traits::BinOp { + rhs_span: opt_input_expr.map(|expr| expr.span), + is_lit: opt_input_expr + .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))), + }, + ), + self.param_env, + poly_trait_ref.without_const().to_predicate(self.tcx), + ), + substs, + ) + } + /// `lookup_method_in_trait` is used for overloaded operators. /// It does a very narrow slice of what the normal probe/confirm path does. /// In particular, it doesn't really do any probing: it simply constructs /// an obligation for a particular trait with the given self type and checks /// whether that trait is implemented. - // - // FIXME(#18741): it seems likely that we can consolidate some of this - // code with the other method-lookup code. In particular, the second half - // of this method is basically the same as confirmation. #[instrument(level = "debug", skip(self, span, opt_input_types))] pub(super) fn lookup_method_in_trait( &self, @@ -358,7 +400,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (obligation, substs) = self.obligation_for_method(span, trait_def_id, self_ty, opt_input_types); + self.construct_obligation_for_trait( + span, + m_name, + trait_def_id, + obligation, + substs, + None, + false, + ) + } + pub(super) fn lookup_op_method_in_trait( + &self, + span: Span, + m_name: Ident, + trait_def_id: DefId, + self_ty: Ty<'tcx>, + opt_input_type: Option>, + opt_input_expr: Option<&'tcx hir::Expr<'tcx>>, + ) -> Option>> { + let (obligation, substs) = self.obligation_for_op_method( + span, + trait_def_id, + self_ty, + opt_input_type, + opt_input_expr, + ); + self.construct_obligation_for_trait( + span, + m_name, + trait_def_id, + obligation, + substs, + opt_input_expr, + true, + ) + } + + // FIXME(#18741): it seems likely that we can consolidate some of this + // code with the other method-lookup code. In particular, the second half + // of this method is basically the same as confirmation. + fn construct_obligation_for_trait( + &self, + span: Span, + m_name: Ident, + trait_def_id: DefId, + obligation: traits::PredicateObligation<'tcx>, + substs: &'tcx ty::List>, + opt_input_expr: Option<&'tcx hir::Expr<'tcx>>, + is_op: bool, + ) -> Option>> { debug!(?obligation); // Now we want to know if this can be matched @@ -395,8 +487,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let fn_sig = self.replace_bound_vars_with_fresh_vars(span, infer::FnCall, fn_sig).0; let fn_sig = fn_sig.subst(self.tcx, substs); - let InferOk { value, obligations: o } = - self.normalize_associated_types_in_as_infer_ok(span, fn_sig); + let InferOk { value, obligations: o } = if is_op { + self.normalize_op_associated_types_in_as_infer_ok(span, fn_sig, opt_input_expr) + } else { + self.normalize_associated_types_in_as_infer_ok(span, fn_sig) + }; let fn_sig = { obligations.extend(o); value @@ -412,8 +507,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // any late-bound regions appearing in its bounds. let bounds = self.tcx.predicates_of(def_id).instantiate(self.tcx, substs); - let InferOk { value, obligations: o } = - self.normalize_associated_types_in_as_infer_ok(span, bounds); + let InferOk { value, obligations: o } = if is_op { + self.normalize_op_associated_types_in_as_infer_ok(span, bounds, opt_input_expr) + } else { + self.normalize_associated_types_in_as_infer_ok(span, bounds) + }; let bounds = { obligations.extend(o); value @@ -421,7 +519,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { assert!(!bounds.has_escaping_bound_vars()); - let cause = traits::ObligationCause::misc(span, self.body_id); + let cause = if is_op { + ObligationCause::new( + span, + self.body_id, + traits::BinOp { + rhs_span: opt_input_expr.map(|expr| expr.span), + is_lit: opt_input_expr + .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))), + }, + ) + } else { + traits::ObligationCause::misc(span, self.body_id) + }; obligations.extend(traits::predicates_for_generics(cause.clone(), self.param_env, bounds)); // Also add an obligation for the method type being well-formed. diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs index 5873b0f52a60e..0629dd4aa5193 100644 --- a/compiler/rustc_typeck/src/check/op.rs +++ b/compiler/rustc_typeck/src/check/op.rs @@ -201,7 +201,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: rhs_expr.span, }); - let result = self.lookup_op_method(lhs_ty, &[rhs_ty_var], Op::Binary(op, is_assign)); + let result = self.lookup_op_method( + lhs_ty, + Some(rhs_ty_var), + Some(rhs_expr), + Op::Binary(op, is_assign), + ); // see `NB` above let rhs_ty = self.check_expr_coercable_to_type(rhs_expr, rhs_ty_var, Some(lhs_expr)); @@ -382,6 +387,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lhs_expr.span, lhs_ty, rhs_ty, + rhs_expr, op, is_assign, ); @@ -390,6 +396,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_expr.span, rhs_ty, lhs_ty, + lhs_expr, op, is_assign, ); @@ -400,7 +407,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if let Ref(_, rty, _) = lhs_ty.kind() { if self.infcx.type_is_copy_modulo_regions(self.param_env, *rty, lhs_expr.span) - && self.lookup_op_method(*rty, &[rhs_ty], Op::Binary(op, is_assign)).is_ok() + && self + .lookup_op_method( + *rty, + Some(rhs_ty), + Some(rhs_expr), + Op::Binary(op, is_assign), + ) + .is_ok() { if let Ok(lstring) = source_map.span_to_snippet(lhs_expr.span) { let msg = &format!( @@ -443,7 +457,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let needs_bound = self .lookup_op_method( eraser.fold_ty(lhs_ty), - &[eraser.fold_ty(rhs_ty)], + Some(eraser.fold_ty(rhs_ty)), + Some(rhs_expr), Op::Binary(op, is_assign), ) .is_ok(); @@ -487,6 +502,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, ty: Ty<'tcx>, other_ty: Ty<'tcx>, + other_expr: &'tcx hir::Expr<'tcx>, op: hir::BinOp, is_assign: IsAssign, ) -> bool /* did we suggest to call a function because of missing parentheses? */ { @@ -513,7 +529,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if self - .lookup_op_method(fn_sig.output(), &[other_ty], Op::Binary(op, is_assign)) + .lookup_op_method( + fn_sig.output(), + Some(other_ty), + Some(other_expr), + Op::Binary(op, is_assign), + ) .is_ok() { let (variable_snippet, applicability) = if !fn_sig.inputs().is_empty() { @@ -631,7 +652,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { op: hir::UnOp, ) -> Ty<'tcx> { assert!(op.is_by_value()); - match self.lookup_op_method(operand_ty, &[], Op::Unary(op, ex.span)) { + match self.lookup_op_method(operand_ty, None, None, Op::Unary(op, ex.span)) { Ok(method) => { self.write_method_call(ex.hir_id, method); method.sig.output() @@ -705,7 +726,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn lookup_op_method( &self, lhs_ty: Ty<'tcx>, - other_tys: &[Ty<'tcx>], + other_ty: Option>, + other_ty_expr: Option<&'tcx hir::Expr<'tcx>>, op: Op, ) -> Result, Vec>> { let lang = self.tcx.lang_items(); @@ -791,7 +813,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let opname = Ident::with_dummy_span(opname); let method = trait_did.and_then(|trait_did| { - self.lookup_method_in_trait(span, opname, trait_did, lhs_ty, Some(other_tys)) + self.lookup_op_method_in_trait(span, opname, trait_did, lhs_ty, other_ty, other_ty_expr) }); match (method, trait_did) { @@ -803,7 +825,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (None, None) => Err(vec![]), (None, Some(trait_did)) => { let (obligation, _) = - self.obligation_for_method(span, trait_did, lhs_ty, Some(other_tys)); + self.obligation_for_op_method(span, trait_did, lhs_ty, other_ty, other_ty_expr); let mut fulfill = >::new(self.tcx); fulfill.register_predicate_obligation(self, obligation); Err(fulfill.select_where_possible(&self.infcx)) diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index c7fd5ed6fcb2e..141d5d2d2b2d0 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -512,3 +512,17 @@ crate being documented (`foobar`) and a path to output the calls To scrape examples from test code, e.g. functions marked `#[test]`, then add the `--scrape-tests` flag. + +### `--check-cfg`: check configuration flags + +This flag accepts the same values as `rustc --check-cfg`, and uses it to check configuration flags. + +Using this flag looks like this: + +```bash +$ rustdoc src/lib.rs -Z unstable-options \ + --check-cfg='names()' --check-cfg='values(feature, "foo", "bar")' +``` + +The example above check every well known names (`target_os`, `doc`, `test`, ... via `names()`) +and check the values of `feature`: `foo` and `bar`. diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 6e483d27f336b..93f90b90e0a0d 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -80,6 +80,8 @@ crate struct Options { crate extern_strs: Vec, /// List of `cfg` flags to hand to the compiler. Always includes `rustdoc`. crate cfgs: Vec, + /// List of check cfg flags to hand to the compiler. + crate check_cfgs: Vec, /// Codegen options to hand to the compiler. crate codegen_options: CodegenOptions, /// Codegen options strings to hand to the compiler. @@ -172,6 +174,7 @@ impl fmt::Debug for Options { .field("libs", &self.libs) .field("externs", &FmtExterns(&self.externs)) .field("cfgs", &self.cfgs) + .field("check-cfgs", &self.check_cfgs) .field("codegen_options", &"...") .field("debugging_options", &"...") .field("target", &self.target) @@ -506,6 +509,7 @@ impl Options { }; let cfgs = matches.opt_strs("cfg"); + let check_cfgs = matches.opt_strs("check-cfg"); let extension_css = matches.opt_str("e").map(|s| PathBuf::from(&s)); @@ -677,6 +681,7 @@ impl Options { externs, extern_strs, cfgs, + check_cfgs, codegen_options, codegen_options_strs, debugging_opts, diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index c2f6f7aea757e..2b82575f71083 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -192,6 +192,7 @@ crate fn create_config( libs, externs, mut cfgs, + check_cfgs, codegen_options, debugging_opts, target, @@ -219,6 +220,7 @@ crate fn create_config( // these are definitely not part of rustdoc, but we want to warn on them anyway. rustc_lint::builtin::RENAMED_AND_REMOVED_LINTS.name.to_string(), rustc_lint::builtin::UNKNOWN_LINTS.name.to_string(), + rustc_lint::builtin::UNEXPECTED_CFGS.name.to_string(), ]; lints_to_show.extend(crate::lint::RUSTDOC_LINTS.iter().map(|lint| lint.name.to_string())); @@ -253,7 +255,7 @@ crate fn create_config( interface::Config { opts: sessopts, crate_cfg: interface::parse_cfgspecs(cfgs), - crate_check_cfg: interface::parse_check_cfg(vec![]), + crate_check_cfg: interface::parse_check_cfg(check_cfgs), input, input_path: cpath, output_file: None, diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index dc9ce052cb5ee..1325331290104 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -91,7 +91,7 @@ crate fn run(options: RustdocOptions) -> Result<(), ErrorReported> { let config = interface::Config { opts: sessopts, crate_cfg: interface::parse_cfgspecs(cfgs), - crate_check_cfg: interface::parse_check_cfg(vec![]), + crate_check_cfg: interface::parse_check_cfg(options.check_cfgs.clone()), input, input_path: None, output_file: None, @@ -321,6 +321,12 @@ fn run_test( for cfg in &rustdoc_options.cfgs { compiler.arg("--cfg").arg(&cfg); } + if !rustdoc_options.check_cfgs.is_empty() { + compiler.arg("-Z").arg("unstable-options"); + for check_cfg in &rustdoc_options.check_cfgs { + compiler.arg("--check-cfg").arg(&check_cfg); + } + } if let Some(sysroot) = rustdoc_options.maybe_sysroot { compiler.arg("--sysroot").arg(sysroot); } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 9d3e58a3a66fc..d7e1f90bcf170 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -259,6 +259,7 @@ fn opts() -> Vec { o.optmulti("L", "library-path", "directory to add to crate search path", "DIR") }), stable("cfg", |o| o.optmulti("", "cfg", "pass a --cfg to rustc", "")), + unstable("check-cfg", |o| o.optmulti("", "check-cfg", "pass a --check-cfg to rustc", "")), stable("extern", |o| o.optmulti("", "extern", "pass an --extern to rustc", "NAME[=PATH]")), unstable("extern-html-root-url", |o| { o.optmulti( diff --git a/src/test/rustdoc-ui/check-cfg-test.rs b/src/test/rustdoc-ui/check-cfg-test.rs new file mode 100644 index 0000000000000..626cc838704b3 --- /dev/null +++ b/src/test/rustdoc-ui/check-cfg-test.rs @@ -0,0 +1,12 @@ +// check-pass +// compile-flags: --test --nocapture --check-cfg=values(feature,"test") -Z unstable-options +// normalize-stderr-test: "src/test/rustdoc-ui" -> "$$DIR" +// normalize-stdout-test: "src/test/rustdoc-ui" -> "$$DIR" +// normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" + +/// The doctest will produce a warning because feature invalid is unexpected +/// ``` +/// #[cfg(feature = "invalid")] +/// assert!(false); +/// ``` +pub struct Foo; diff --git a/src/test/rustdoc-ui/check-cfg-test.stderr b/src/test/rustdoc-ui/check-cfg-test.stderr new file mode 100644 index 0000000000000..dc25205da772e --- /dev/null +++ b/src/test/rustdoc-ui/check-cfg-test.stderr @@ -0,0 +1,11 @@ +warning: unexpected `cfg` condition value + --> $DIR/check-cfg-test.rs:9:7 + | +LL | #[cfg(feature = "invalid")] + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(unexpected_cfgs)]` on by default + = note: expected values for `feature` are: test + +warning: 1 warning emitted + diff --git a/src/test/rustdoc-ui/check-cfg-test.stdout b/src/test/rustdoc-ui/check-cfg-test.stdout new file mode 100644 index 0000000000000..b7db49bcfa87a --- /dev/null +++ b/src/test/rustdoc-ui/check-cfg-test.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/check-cfg-test.rs - Foo (line 8) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/src/test/rustdoc-ui/check-cfg-unstable.rs b/src/test/rustdoc-ui/check-cfg-unstable.rs new file mode 100644 index 0000000000000..5c500ce6ce082 --- /dev/null +++ b/src/test/rustdoc-ui/check-cfg-unstable.rs @@ -0,0 +1,2 @@ +// check-fail +// compile-flags: --check-cfg=names() diff --git a/src/test/rustdoc-ui/check-cfg-unstable.stderr b/src/test/rustdoc-ui/check-cfg-unstable.stderr new file mode 100644 index 0000000000000..9b27c2bc058a7 --- /dev/null +++ b/src/test/rustdoc-ui/check-cfg-unstable.stderr @@ -0,0 +1,2 @@ +error: the `-Z unstable-options` flag must also be passed to enable the flag `check-cfg` + diff --git a/src/test/rustdoc-ui/check-cfg.rs b/src/test/rustdoc-ui/check-cfg.rs new file mode 100644 index 0000000000000..fa8789ad3ede4 --- /dev/null +++ b/src/test/rustdoc-ui/check-cfg.rs @@ -0,0 +1,7 @@ +// check-pass +// compile-flags: --check-cfg=names() -Z unstable-options + +/// uniz is nor a builtin nor pass as arguments so is unexpected +#[cfg(uniz)] +//~^ WARNING unexpected `cfg` condition name +pub struct Bar; diff --git a/src/test/rustdoc-ui/check-cfg.stderr b/src/test/rustdoc-ui/check-cfg.stderr new file mode 100644 index 0000000000000..1db8e1d91c249 --- /dev/null +++ b/src/test/rustdoc-ui/check-cfg.stderr @@ -0,0 +1,10 @@ +warning: unexpected `cfg` condition name + --> $DIR/check-cfg.rs:5:7 + | +LL | #[cfg(uniz)] + | ^^^^ help: did you mean: `unix` + | + = note: `#[warn(unexpected_cfgs)]` on by default + +warning: 1 warning emitted + diff --git a/src/test/ui/extern/extern-static-size-overflow.rs b/src/test/ui/extern/extern-static-size-overflow.rs new file mode 100644 index 0000000000000..30a0c44546686 --- /dev/null +++ b/src/test/ui/extern/extern-static-size-overflow.rs @@ -0,0 +1,43 @@ +#[repr(C)] +struct ReallyBig { + _a: [u8; usize::MAX], +} + +// The limit for "too big for the current architecture" is dependent on the target pointer size +// however it's artifically limited on 64 bits +// logic copied from rustc_target::abi::TargetDataLayout::obj_size_bound() +const fn max_size() -> usize { + #[cfg(target_pointer_width = "16")] + { + 1 << 15 + } + + #[cfg(target_pointer_width = "32")] + { + 1 << 31 + } + + #[cfg(target_pointer_width = "64")] + { + 1 << 47 + } + + #[cfg(not(any( + target_pointer_width = "16", + target_pointer_width = "32", + target_pointer_width = "64" + )))] + { + isize::MAX as usize + } +} + +extern "C" { + static FOO: [u8; 1]; + static BAR: [u8; max_size() - 1]; + static BAZ: [u8; max_size()]; //~ ERROR extern static is too large + static UWU: [usize; usize::MAX]; //~ ERROR extern static is too large + static A: ReallyBig; //~ ERROR extern static is too large +} + +fn main() {} diff --git a/src/test/ui/extern/extern-static-size-overflow.stderr b/src/test/ui/extern/extern-static-size-overflow.stderr new file mode 100644 index 0000000000000..f5173feec75d4 --- /dev/null +++ b/src/test/ui/extern/extern-static-size-overflow.stderr @@ -0,0 +1,20 @@ +error: extern static is too large for the current architecture + --> $DIR/extern-static-size-overflow.rs:38:5 + | +LL | static BAZ: [u8; max_size()]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: extern static is too large for the current architecture + --> $DIR/extern-static-size-overflow.rs:39:5 + | +LL | static UWU: [usize; usize::MAX]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: extern static is too large for the current architecture + --> $DIR/extern-static-size-overflow.rs:40:5 + | +LL | static A: ReallyBig; + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/issues/issue-24352.stderr b/src/test/ui/issues/issue-24352.stderr index 69cd7789065d7..69731bfe7ee7d 100644 --- a/src/test/ui/issues/issue-24352.stderr +++ b/src/test/ui/issues/issue-24352.stderr @@ -5,6 +5,10 @@ LL | 1.0f64 - 1 | ^ no implementation for `f64 - {integer}` | = help: the trait `Sub<{integer}>` is not implemented for `f64` +help: consider using a floating-point literal by writing it with `.0` + | +LL | 1.0f64 - 1.0 + | ++ error: aborting due to previous error diff --git a/src/test/ui/lint/unused/must_use-array.stderr b/src/test/ui/lint/unused/must_use-array.stderr index c42223b519851..45a5317fccc6e 100644 --- a/src/test/ui/lint/unused/must_use-array.stderr +++ b/src/test/ui/lint/unused/must_use-array.stderr @@ -32,7 +32,7 @@ error: unused array of boxed `T` trait objects in tuple element 1 that must be u --> $DIR/must_use-array.rs:43:5 | LL | impl_array(); - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^ error: unused array of arrays of arrays of `S` that must be used --> $DIR/must_use-array.rs:45:5 diff --git a/src/test/ui/lint/unused/must_use-trait.stderr b/src/test/ui/lint/unused/must_use-trait.stderr index 11555d80825a4..a42eb8841789d 100644 --- a/src/test/ui/lint/unused/must_use-trait.stderr +++ b/src/test/ui/lint/unused/must_use-trait.stderr @@ -26,13 +26,13 @@ error: unused boxed `Critical` trait object in tuple element 1 that must be used --> $DIR/must_use-trait.rs:37:5 | LL | get_critical_tuple(); - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ error: unused implementer of `Critical` in tuple element 2 that must be used --> $DIR/must_use-trait.rs:37:5 | LL | get_critical_tuple(); - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ error: aborting due to 5 previous errors diff --git a/src/test/ui/lint/unused/must_use-tuple.stderr b/src/test/ui/lint/unused/must_use-tuple.stderr index 0532d89e039eb..e5709a5f0af5a 100644 --- a/src/test/ui/lint/unused/must_use-tuple.stderr +++ b/src/test/ui/lint/unused/must_use-tuple.stderr @@ -31,15 +31,15 @@ error: unused `Result` in tuple element 0 that must be used --> $DIR/must_use-tuple.rs:14:5 | LL | foo(); - | ^^^^^^ + | ^^^^^ | = note: this `Result` may be an `Err` variant, which should be handled error: unused `Result` in tuple element 0 that must be used - --> $DIR/must_use-tuple.rs:16:6 + --> $DIR/must_use-tuple.rs:16:7 | LL | ((Err::<(), ()>(()), ()), ()); - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ | = note: this `Result` may be an `Err` variant, which should be handled diff --git a/src/test/ui/numbers-arithmetic/not-suggest-float-literal.rs b/src/test/ui/numbers-arithmetic/not-suggest-float-literal.rs new file mode 100644 index 0000000000000..513d02db988ed --- /dev/null +++ b/src/test/ui/numbers-arithmetic/not-suggest-float-literal.rs @@ -0,0 +1,53 @@ +fn add_float_to_integer(x: u8) -> f32 { + x + 100.0 //~ ERROR cannot add `{float}` to `u8` +} + +fn add_str_to_float(x: f64) -> f64 { + x + "foo" //~ ERROR cannot add `&str` to `f64` +} + +fn add_lvar_to_float(x: f64) -> f64 { + let y = 3; + x + y //~ ERROR cannot add `{integer}` to `f64` +} + +fn subtract_float_from_integer(x: u8) -> f32 { + x - 100.0 //~ ERROR cannot subtract `{float}` from `u8` +} + +fn subtract_str_from_f64(x: f64) -> f64 { + x - "foo" //~ ERROR cannot subtract `&str` from `f64` +} + +fn subtract_lvar_from_f64(x: f64) -> f64 { + let y = 3; + x - y //~ ERROR cannot subtract `{integer}` from `f64` +} + +fn multiply_integer_by_float(x: u8) -> f32 { + x * 100.0 //~ ERROR cannot multiply `u8` by `{float}` +} + +fn multiply_f64_by_str(x: f64) -> f64 { + x * "foo" //~ ERROR cannot multiply `f64` by `&str` +} + +fn multiply_f64_by_lvar(x: f64) -> f64 { + let y = 3; + x * y //~ ERROR cannot multiply `f64` by `{integer}` +} + +fn divide_integer_by_float(x: u8) -> u8 { + x / 100.0 //~ ERROR cannot divide `u8` by `{float}` +} + +fn divide_f64_by_str(x: f64) -> f64 { + x / "foo" //~ ERROR cannot divide `f64` by `&str` +} + +fn divide_f64_by_lvar(x: f64) -> f64 { + let y = 3; + x / y //~ ERROR cannot divide `f64` by `{integer}` +} + +fn main() {} diff --git a/src/test/ui/numbers-arithmetic/not-suggest-float-literal.stderr b/src/test/ui/numbers-arithmetic/not-suggest-float-literal.stderr new file mode 100644 index 0000000000000..ce9a08a15897a --- /dev/null +++ b/src/test/ui/numbers-arithmetic/not-suggest-float-literal.stderr @@ -0,0 +1,99 @@ +error[E0277]: cannot add `{float}` to `u8` + --> $DIR/not-suggest-float-literal.rs:2:7 + | +LL | x + 100.0 + | ^ no implementation for `u8 + {float}` + | + = help: the trait `Add<{float}>` is not implemented for `u8` + +error[E0277]: cannot add `&str` to `f64` + --> $DIR/not-suggest-float-literal.rs:6:7 + | +LL | x + "foo" + | ^ no implementation for `f64 + &str` + | + = help: the trait `Add<&str>` is not implemented for `f64` + +error[E0277]: cannot add `{integer}` to `f64` + --> $DIR/not-suggest-float-literal.rs:11:7 + | +LL | x + y + | ^ no implementation for `f64 + {integer}` + | + = help: the trait `Add<{integer}>` is not implemented for `f64` + +error[E0277]: cannot subtract `{float}` from `u8` + --> $DIR/not-suggest-float-literal.rs:15:7 + | +LL | x - 100.0 + | ^ no implementation for `u8 - {float}` + | + = help: the trait `Sub<{float}>` is not implemented for `u8` + +error[E0277]: cannot subtract `&str` from `f64` + --> $DIR/not-suggest-float-literal.rs:19:7 + | +LL | x - "foo" + | ^ no implementation for `f64 - &str` + | + = help: the trait `Sub<&str>` is not implemented for `f64` + +error[E0277]: cannot subtract `{integer}` from `f64` + --> $DIR/not-suggest-float-literal.rs:24:7 + | +LL | x - y + | ^ no implementation for `f64 - {integer}` + | + = help: the trait `Sub<{integer}>` is not implemented for `f64` + +error[E0277]: cannot multiply `u8` by `{float}` + --> $DIR/not-suggest-float-literal.rs:28:7 + | +LL | x * 100.0 + | ^ no implementation for `u8 * {float}` + | + = help: the trait `Mul<{float}>` is not implemented for `u8` + +error[E0277]: cannot multiply `f64` by `&str` + --> $DIR/not-suggest-float-literal.rs:32:7 + | +LL | x * "foo" + | ^ no implementation for `f64 * &str` + | + = help: the trait `Mul<&str>` is not implemented for `f64` + +error[E0277]: cannot multiply `f64` by `{integer}` + --> $DIR/not-suggest-float-literal.rs:37:7 + | +LL | x * y + | ^ no implementation for `f64 * {integer}` + | + = help: the trait `Mul<{integer}>` is not implemented for `f64` + +error[E0277]: cannot divide `u8` by `{float}` + --> $DIR/not-suggest-float-literal.rs:41:7 + | +LL | x / 100.0 + | ^ no implementation for `u8 / {float}` + | + = help: the trait `Div<{float}>` is not implemented for `u8` + +error[E0277]: cannot divide `f64` by `&str` + --> $DIR/not-suggest-float-literal.rs:45:7 + | +LL | x / "foo" + | ^ no implementation for `f64 / &str` + | + = help: the trait `Div<&str>` is not implemented for `f64` + +error[E0277]: cannot divide `f64` by `{integer}` + --> $DIR/not-suggest-float-literal.rs:50:7 + | +LL | x / y + | ^ no implementation for `f64 / {integer}` + | + = help: the trait `Div<{integer}>` is not implemented for `f64` + +error: aborting due to 12 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/numbers-arithmetic/suggest-float-literal.fixed b/src/test/ui/numbers-arithmetic/suggest-float-literal.fixed new file mode 100644 index 0000000000000..9278262a6ffee --- /dev/null +++ b/src/test/ui/numbers-arithmetic/suggest-float-literal.fixed @@ -0,0 +1,37 @@ +// run-rustfix + +#![allow(dead_code)] + +fn add_integer_to_f32(x: f32) -> f32 { + x + 100.0 //~ ERROR cannot add `{integer}` to `f32` +} + +fn add_integer_to_f64(x: f64) -> f64 { + x + 100.0 //~ ERROR cannot add `{integer}` to `f64` +} + +fn subtract_integer_from_f32(x: f32) -> f32 { + x - 100.0 //~ ERROR cannot subtract `{integer}` from `f32` +} + +fn subtract_integer_from_f64(x: f64) -> f64 { + x - 100.0 //~ ERROR cannot subtract `{integer}` from `f64` +} + +fn multiply_f32_by_integer(x: f32) -> f32 { + x * 100.0 //~ ERROR cannot multiply `f32` by `{integer}` +} + +fn multiply_f64_by_integer(x: f64) -> f64 { + x * 100.0 //~ ERROR cannot multiply `f64` by `{integer}` +} + +fn divide_f32_by_integer(x: f32) -> f32 { + x / 100.0 //~ ERROR cannot divide `f32` by `{integer}` +} + +fn divide_f64_by_integer(x: f64) -> f64 { + x / 100.0 //~ ERROR cannot divide `f64` by `{integer}` +} + +fn main() {} diff --git a/src/test/ui/numbers-arithmetic/suggest-float-literal.rs b/src/test/ui/numbers-arithmetic/suggest-float-literal.rs new file mode 100644 index 0000000000000..59e67f8d33e00 --- /dev/null +++ b/src/test/ui/numbers-arithmetic/suggest-float-literal.rs @@ -0,0 +1,37 @@ +// run-rustfix + +#![allow(dead_code)] + +fn add_integer_to_f32(x: f32) -> f32 { + x + 100 //~ ERROR cannot add `{integer}` to `f32` +} + +fn add_integer_to_f64(x: f64) -> f64 { + x + 100 //~ ERROR cannot add `{integer}` to `f64` +} + +fn subtract_integer_from_f32(x: f32) -> f32 { + x - 100 //~ ERROR cannot subtract `{integer}` from `f32` +} + +fn subtract_integer_from_f64(x: f64) -> f64 { + x - 100 //~ ERROR cannot subtract `{integer}` from `f64` +} + +fn multiply_f32_by_integer(x: f32) -> f32 { + x * 100 //~ ERROR cannot multiply `f32` by `{integer}` +} + +fn multiply_f64_by_integer(x: f64) -> f64 { + x * 100 //~ ERROR cannot multiply `f64` by `{integer}` +} + +fn divide_f32_by_integer(x: f32) -> f32 { + x / 100 //~ ERROR cannot divide `f32` by `{integer}` +} + +fn divide_f64_by_integer(x: f64) -> f64 { + x / 100 //~ ERROR cannot divide `f64` by `{integer}` +} + +fn main() {} diff --git a/src/test/ui/numbers-arithmetic/suggest-float-literal.stderr b/src/test/ui/numbers-arithmetic/suggest-float-literal.stderr new file mode 100644 index 0000000000000..eb0be785061e6 --- /dev/null +++ b/src/test/ui/numbers-arithmetic/suggest-float-literal.stderr @@ -0,0 +1,99 @@ +error[E0277]: cannot add `{integer}` to `f32` + --> $DIR/suggest-float-literal.rs:6:7 + | +LL | x + 100 + | ^ no implementation for `f32 + {integer}` + | + = help: the trait `Add<{integer}>` is not implemented for `f32` +help: consider using a floating-point literal by writing it with `.0` + | +LL | x + 100.0 + | ++ + +error[E0277]: cannot add `{integer}` to `f64` + --> $DIR/suggest-float-literal.rs:10:7 + | +LL | x + 100 + | ^ no implementation for `f64 + {integer}` + | + = help: the trait `Add<{integer}>` is not implemented for `f64` +help: consider using a floating-point literal by writing it with `.0` + | +LL | x + 100.0 + | ++ + +error[E0277]: cannot subtract `{integer}` from `f32` + --> $DIR/suggest-float-literal.rs:14:7 + | +LL | x - 100 + | ^ no implementation for `f32 - {integer}` + | + = help: the trait `Sub<{integer}>` is not implemented for `f32` +help: consider using a floating-point literal by writing it with `.0` + | +LL | x - 100.0 + | ++ + +error[E0277]: cannot subtract `{integer}` from `f64` + --> $DIR/suggest-float-literal.rs:18:7 + | +LL | x - 100 + | ^ no implementation for `f64 - {integer}` + | + = help: the trait `Sub<{integer}>` is not implemented for `f64` +help: consider using a floating-point literal by writing it with `.0` + | +LL | x - 100.0 + | ++ + +error[E0277]: cannot multiply `f32` by `{integer}` + --> $DIR/suggest-float-literal.rs:22:7 + | +LL | x * 100 + | ^ no implementation for `f32 * {integer}` + | + = help: the trait `Mul<{integer}>` is not implemented for `f32` +help: consider using a floating-point literal by writing it with `.0` + | +LL | x * 100.0 + | ++ + +error[E0277]: cannot multiply `f64` by `{integer}` + --> $DIR/suggest-float-literal.rs:26:7 + | +LL | x * 100 + | ^ no implementation for `f64 * {integer}` + | + = help: the trait `Mul<{integer}>` is not implemented for `f64` +help: consider using a floating-point literal by writing it with `.0` + | +LL | x * 100.0 + | ++ + +error[E0277]: cannot divide `f32` by `{integer}` + --> $DIR/suggest-float-literal.rs:30:7 + | +LL | x / 100 + | ^ no implementation for `f32 / {integer}` + | + = help: the trait `Div<{integer}>` is not implemented for `f32` +help: consider using a floating-point literal by writing it with `.0` + | +LL | x / 100.0 + | ++ + +error[E0277]: cannot divide `f64` by `{integer}` + --> $DIR/suggest-float-literal.rs:34:7 + | +LL | x / 100 + | ^ no implementation for `f64 / {integer}` + | + = help: the trait `Div<{integer}>` is not implemented for `f64` +help: consider using a floating-point literal by writing it with `.0` + | +LL | x / 100.0 + | ++ + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0277`.