diff options
| author | bors <bors@rust-lang.org> | 2021-12-18 07:20:50 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2021-12-18 07:20:50 +0000 |
| commit | 208ced64db20bd947a2ba5c90f37936fd3ab352b (patch) | |
| tree | 1aa23a0e304a1935b962258e3884fe6f86d00011 /compiler | |
| parent | dde825db464b08d6f572766579dfb629b837368c (diff) | |
| parent | fd445ddf96f11aadc01fd0d60bb92b419d7ffaa7 (diff) | |
| download | rust-208ced64db20bd947a2ba5c90f37936fd3ab352b.tar.gz rust-208ced64db20bd947a2ba5c90f37936fd3ab352b.zip | |
Auto merge of #92059 - matthiaskrgr:rollup-bainfyh, r=matthiaskrgr
Rollup of 7 pull requests Successful merges: - #87901 (Fix suggestion of additional `pub` when using `pub pub fn ...`) - #89090 (Lint bare traits in AstConv.) - #91818 (Show the unused type for `unused_results` lint) - #91910 (miri: lift restriction on extern types being the only field in a struct) - #91928 (Constify (most) `Option` methods) - #91975 (Move generator check earlier in inlining.) - #92016 (builtin_macros: allow external consumers for AsmArgs parsing) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_ast_lowering/src/lib.rs | 48 | ||||
| -rw-r--r-- | compiler/rustc_builtin_macros/src/asm.rs | 72 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/interpret/eval_context.rs | 16 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/interpret/place.rs | 22 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/context.rs | 10 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/unused.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_lint_defs/src/lib.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/query/mod.rs | 21 | ||||
| -rw-r--r-- | compiler/rustc_mir_transform/src/inline.rs | 14 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/item.rs | 112 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/ty.rs | 8 | ||||
| -rw-r--r-- | compiler/rustc_typeck/src/astconv/mod.rs | 77 | ||||
| -rw-r--r-- | compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs | 58 |
13 files changed, 247 insertions, 216 deletions
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 0077dec889d..77738b2c5cc 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -46,7 +46,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::Lrc; -use rustc_errors::{struct_span_err, Applicability}; +use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Namespace, PartialRes, PerNS, Res}; use rustc_hir::def_id::{DefId, DefPathHash, LocalDefId, CRATE_DEF_ID}; @@ -55,11 +55,9 @@ use rustc_hir::intravisit; use rustc_hir::{ConstArg, GenericArg, ParamName}; use rustc_index::vec::{Idx, IndexVec}; use rustc_query_system::ich::StableHashingContext; -use rustc_session::lint::builtin::BARE_TRAIT_OBJECTS; -use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer}; +use rustc_session::lint::LintBuffer; use rustc_session::utils::{FlattenNonterminals, NtToTokenstream}; use rustc_session::Session; -use rustc_span::edition::Edition; use rustc_span::hygiene::ExpnId; use rustc_span::source_map::{respan, DesugaringKind}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -1184,11 +1182,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) -> hir::Ty<'hir> { let id = self.lower_node_id(t.id); let qpath = self.lower_qpath(t.id, qself, path, param_mode, itctx); - let ty = self.ty_path(id, t.span, qpath); - if let hir::TyKind::TraitObject(..) = ty.kind { - self.maybe_lint_bare_trait(t.span, t.id, qself.is_none() && path.is_global()); - } - ty + self.ty_path(id, t.span, qpath) } fn ty(&mut self, span: Span, kind: hir::TyKind<'hir>) -> hir::Ty<'hir> { @@ -1285,9 +1279,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { lifetime_bound.unwrap_or_else(|| this.elided_dyn_bound(t.span)); (bounds, lifetime_bound) }); - if kind != TraitObjectSyntax::Dyn { - self.maybe_lint_bare_trait(t.span, t.id, false); - } hir::TyKind::TraitObject(bounds, lifetime_bound, kind) } TyKind::ImplTrait(def_node_id, ref bounds) => { @@ -2380,39 +2371,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { name: hir::LifetimeName::Implicit(missing), } } - - fn maybe_lint_bare_trait(&mut self, span: Span, id: NodeId, is_global: bool) { - // FIXME(davidtwco): This is a hack to detect macros which produce spans of the - // call site which do not have a macro backtrace. See #61963. - let is_macro_callsite = self - .sess - .source_map() - .span_to_snippet(span) - .map(|snippet| snippet.starts_with("#[")) - .unwrap_or(true); - if !is_macro_callsite { - if span.edition() < Edition::Edition2021 { - self.resolver.lint_buffer().buffer_lint_with_diagnostic( - BARE_TRAIT_OBJECTS, - id, - span, - "trait objects without an explicit `dyn` are deprecated", - BuiltinLintDiagnostics::BareTraitObject(span, is_global), - ) - } else { - let msg = "trait objects must include the `dyn` keyword"; - let label = "add `dyn` keyword before this trait"; - let mut err = struct_span_err!(self.sess, span, E0782, "{}", msg,); - err.span_suggestion_verbose( - span.shrink_to_lo(), - label, - String::from("dyn "), - Applicability::MachineApplicable, - ); - err.emit(); - } - } - } } /// Helper struct for delayed construction of GenericArgs. diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index d67d872b884..768cb99510f 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -8,13 +8,14 @@ use rustc_expand::base::{self, *}; use rustc_parse::parser::Parser; use rustc_parse_format as parse; use rustc_session::lint; +use rustc_session::parse::ParseSess; use rustc_span::symbol::Ident; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::{InnerSpan, Span}; use rustc_target::asm::InlineAsmArch; use smallvec::smallvec; -struct AsmArgs { +pub struct AsmArgs { templates: Vec<P<ast::Expr>>, operands: Vec<(ast::InlineAsmOperand, Span)>, named_args: FxHashMap<Symbol, usize>, @@ -31,15 +32,28 @@ fn parse_args<'a>( is_global_asm: bool, ) -> Result<AsmArgs, DiagnosticBuilder<'a>> { let mut p = ecx.new_parser_from_tts(tts); + let sess = &ecx.sess.parse_sess; + parse_asm_args(&mut p, sess, sp, is_global_asm) +} + +// Primarily public for rustfmt consumption. +// Internal consumers should continue to leverage `expand_asm`/`expand__global_asm` +pub fn parse_asm_args<'a>( + p: &mut Parser<'a>, + sess: &'a ParseSess, + sp: Span, + is_global_asm: bool, +) -> Result<AsmArgs, DiagnosticBuilder<'a>> { + let diag = &sess.span_diagnostic; if p.token == token::Eof { - return Err(ecx.struct_span_err(sp, "requires at least a template string argument")); + return Err(diag.struct_span_err(sp, "requires at least a template string argument")); } // Detect use of the legacy llvm_asm! syntax (which used to be called asm!) if !is_global_asm && p.look_ahead(1, |t| *t == token::Colon || *t == token::ModSep) { let mut err = - ecx.struct_span_err(sp, "the legacy LLVM-style asm! syntax is no longer supported"); + diag.struct_span_err(sp, "the legacy LLVM-style asm! syntax is no longer supported"); err.note("consider migrating to the new asm! syntax specified in RFC 2873"); err.note("alternatively, switch to llvm_asm! to keep your code working as it is"); return Err(err); @@ -61,7 +75,7 @@ fn parse_args<'a>( if !p.eat(&token::Comma) { if allow_templates { // After a template string, we always expect *only* a comma... - let mut err = ecx.struct_span_err(p.token.span, "expected token: `,`"); + let mut err = diag.struct_span_err(p.token.span, "expected token: `,`"); err.span_label(p.token.span, "expected `,`"); p.maybe_annotate_with_ascription(&mut err, false); return Err(err); @@ -76,14 +90,14 @@ fn parse_args<'a>( // Parse clobber_abi if p.eat_keyword(sym::clobber_abi) { - parse_clobber_abi(&mut p, &mut args)?; + parse_clobber_abi(p, &mut args)?; allow_templates = false; continue; } // Parse options if p.eat_keyword(sym::options) { - parse_options(&mut p, &mut args, is_global_asm)?; + parse_options(p, &mut args, is_global_asm)?; allow_templates = false; continue; } @@ -103,25 +117,25 @@ fn parse_args<'a>( let mut explicit_reg = false; let op = if !is_global_asm && p.eat_keyword(kw::In) { - let reg = parse_reg(&mut p, &mut explicit_reg)?; + let reg = parse_reg(p, &mut explicit_reg)?; if p.eat_keyword(kw::Underscore) { - let err = ecx.struct_span_err(p.token.span, "_ cannot be used for input operands"); + let err = diag.struct_span_err(p.token.span, "_ cannot be used for input operands"); return Err(err); } let expr = p.parse_expr()?; ast::InlineAsmOperand::In { reg, expr } } else if !is_global_asm && p.eat_keyword(sym::out) { - let reg = parse_reg(&mut p, &mut explicit_reg)?; + let reg = parse_reg(p, &mut explicit_reg)?; let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) }; ast::InlineAsmOperand::Out { reg, expr, late: false } } else if !is_global_asm && p.eat_keyword(sym::lateout) { - let reg = parse_reg(&mut p, &mut explicit_reg)?; + let reg = parse_reg(p, &mut explicit_reg)?; let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) }; ast::InlineAsmOperand::Out { reg, expr, late: true } } else if !is_global_asm && p.eat_keyword(sym::inout) { - let reg = parse_reg(&mut p, &mut explicit_reg)?; + let reg = parse_reg(p, &mut explicit_reg)?; if p.eat_keyword(kw::Underscore) { - let err = ecx.struct_span_err(p.token.span, "_ cannot be used for input operands"); + let err = diag.struct_span_err(p.token.span, "_ cannot be used for input operands"); return Err(err); } let expr = p.parse_expr()?; @@ -133,9 +147,9 @@ fn parse_args<'a>( ast::InlineAsmOperand::InOut { reg, expr, late: false } } } else if !is_global_asm && p.eat_keyword(sym::inlateout) { - let reg = parse_reg(&mut p, &mut explicit_reg)?; + let reg = parse_reg(p, &mut explicit_reg)?; if p.eat_keyword(kw::Underscore) { - let err = ecx.struct_span_err(p.token.span, "_ cannot be used for input operands"); + let err = diag.struct_span_err(p.token.span, "_ cannot be used for input operands"); return Err(err); } let expr = p.parse_expr()?; @@ -154,7 +168,7 @@ fn parse_args<'a>( match expr.kind { ast::ExprKind::Path(..) => {} _ => { - let err = ecx + let err = diag .struct_span_err(expr.span, "argument to `sym` must be a path expression"); return Err(err); } @@ -173,7 +187,7 @@ fn parse_args<'a>( } else { "expected operand, clobber_abi, options, or additional template string" }; - let mut err = ecx.struct_span_err(template.span, errstr); + let mut err = diag.struct_span_err(template.span, errstr); err.span_label(template.span, errstr); return Err(err); } @@ -193,31 +207,31 @@ fn parse_args<'a>( // clobber_abi/options. We do this at the end once we have the full span // of the argument available. if !args.options_spans.is_empty() { - ecx.struct_span_err(span, "arguments are not allowed after options") + diag.struct_span_err(span, "arguments are not allowed after options") .span_labels(args.options_spans.clone(), "previous options") .span_label(span, "argument") .emit(); } else if let Some((_, abi_span)) = args.clobber_abis.last() { - ecx.struct_span_err(span, "arguments are not allowed after clobber_abi") + diag.struct_span_err(span, "arguments are not allowed after clobber_abi") .span_label(*abi_span, "clobber_abi") .span_label(span, "argument") .emit(); } if explicit_reg { if name.is_some() { - ecx.struct_span_err(span, "explicit register arguments cannot have names").emit(); + diag.struct_span_err(span, "explicit register arguments cannot have names").emit(); } args.reg_args.insert(slot); } else if let Some(name) = name { if let Some(&prev) = args.named_args.get(&name) { - ecx.struct_span_err(span, &format!("duplicate argument named `{}`", name)) + diag.struct_span_err(span, &format!("duplicate argument named `{}`", name)) .span_label(args.operands[prev].1, "previously here") .span_label(span, "duplicate argument") .emit(); continue; } if !args.reg_args.is_empty() { - let mut err = ecx.struct_span_err( + let mut err = diag.struct_span_err( span, "named arguments cannot follow explicit register arguments", ); @@ -230,7 +244,7 @@ fn parse_args<'a>( args.named_args.insert(name, slot); } else { if !args.named_args.is_empty() || !args.reg_args.is_empty() { - let mut err = ecx.struct_span_err( + let mut err = diag.struct_span_err( span, "positional arguments cannot follow named arguments \ or explicit register arguments", @@ -251,21 +265,21 @@ fn parse_args<'a>( && args.options.contains(ast::InlineAsmOptions::READONLY) { let spans = args.options_spans.clone(); - ecx.struct_span_err(spans, "the `nomem` and `readonly` options are mutually exclusive") + diag.struct_span_err(spans, "the `nomem` and `readonly` options are mutually exclusive") .emit(); } if args.options.contains(ast::InlineAsmOptions::PURE) && args.options.contains(ast::InlineAsmOptions::NORETURN) { let spans = args.options_spans.clone(); - ecx.struct_span_err(spans, "the `pure` and `noreturn` options are mutually exclusive") + diag.struct_span_err(spans, "the `pure` and `noreturn` options are mutually exclusive") .emit(); } if args.options.contains(ast::InlineAsmOptions::PURE) && !args.options.intersects(ast::InlineAsmOptions::NOMEM | ast::InlineAsmOptions::READONLY) { let spans = args.options_spans.clone(); - ecx.struct_span_err( + diag.struct_span_err( spans, "the `pure` option must be combined with either `nomem` or `readonly`", ) @@ -296,14 +310,14 @@ fn parse_args<'a>( } } if args.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output { - ecx.struct_span_err( + diag.struct_span_err( args.options_spans.clone(), "asm with the `pure` option must have at least one output", ) .emit(); } if args.options.contains(ast::InlineAsmOptions::NORETURN) && !outputs_sp.is_empty() { - let err = ecx + let err = diag .struct_span_err(outputs_sp, "asm outputs are not allowed with the `noreturn` option"); // Bail out now since this is likely to confuse MIR @@ -312,7 +326,7 @@ fn parse_args<'a>( if args.clobber_abis.len() > 0 { if is_global_asm { - let err = ecx.struct_span_err( + let err = diag.struct_span_err( args.clobber_abis.iter().map(|(_, span)| *span).collect::<Vec<Span>>(), "`clobber_abi` cannot be used with `global_asm!`", ); @@ -321,7 +335,7 @@ fn parse_args<'a>( return Err(err); } if !regclass_outputs.is_empty() { - ecx.struct_span_err( + diag.struct_span_err( regclass_outputs.clone(), "asm with `clobber_abi` must specify explicit registers for outputs", ) diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index d768f06c4f0..d9faa6777ea 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -616,19 +616,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { match self.size_and_align_of(metadata, &field)? { Some(size_and_align) => size_and_align, None => { - // A field with extern type. If this field is at offset 0, we behave - // like the underlying extern type. - // FIXME: Once we have made decisions for how to handle size and alignment - // of `extern type`, this should be adapted. It is just a temporary hack - // to get some code to work that probably ought to work. - if sized_size == Size::ZERO { - return Ok(None); - } else { - span_bug!( - self.cur_span(), - "Fields cannot be extern types, unless they are at offset 0" - ) - } + // A field with an extern type. We don't know the actual dynamic size + // or the alignment. + return Ok(None); } }; diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 851c2a6bb2e..818b95b7fc4 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -362,21 +362,15 @@ where // Re-use parent metadata to determine dynamic field layout. // With custom DSTS, this *will* execute user-defined code, but the same // happens at run-time so that's okay. - let align = match self.size_and_align_of(&base.meta, &field_layout)? { - Some((_, align)) => align, - None if offset == Size::ZERO => { - // An extern type at offset 0, we fall back to its static alignment. - // FIXME: Once we have made decisions for how to handle size and alignment - // of `extern type`, this should be adapted. It is just a temporary hack - // to get some code to work that probably ought to work. - field_layout.align.abi + match self.size_and_align_of(&base.meta, &field_layout)? { + Some((_, align)) => (base.meta, offset.align_to(align)), + None => { + // For unsized types with an extern type tail we perform no adjustments. + // NOTE: keep this in sync with `PlaceRef::project_field` in the codegen backend. + assert!(matches!(base.meta, MemPlaceMeta::None)); + (base.meta, offset) } - None => span_bug!( - self.cur_span(), - "cannot compute offset for extern type field at non-0 offset" - ), - }; - (base.meta, offset.align_to(align)) + } } else { // base.meta could be present; we might be accessing a sized field of an unsized // struct. diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 71db58f2d8b..ba1b8caa368 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -633,16 +633,6 @@ pub trait LintContext: Sized { } }, BuiltinLintDiagnostics::Normal => (), - BuiltinLintDiagnostics::BareTraitObject(span, is_global) => { - let (sugg, app) = match sess.source_map().span_to_snippet(span) { - Ok(s) if is_global => { - (format!("dyn ({})", s), Applicability::MachineApplicable) - } - Ok(s) => (format!("dyn {}", s), Applicability::MachineApplicable), - Err(_) => ("dyn <type>".to_string(), Applicability::HasPlaceholders), - }; - db.span_suggestion(span, "use `dyn`", sugg, app); - } BuiltinLintDiagnostics::AbsPathWithModule(span) => { let (sugg, app) = match sess.source_map().span_to_snippet(span) { Ok(ref s) => { diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index da1edcf6fe3..ed24b94e2fd 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -169,7 +169,9 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { } if !(type_permits_lack_of_use || fn_warned || op_warned) { - cx.struct_span_lint(UNUSED_RESULTS, s.span, |lint| lint.build("unused result").emit()); + cx.struct_span_lint(UNUSED_RESULTS, s.span, |lint| { + lint.build(&format!("unused result of type `{}`", ty)).emit() + }); } // Returns whether an error has been emitted (and thus another does not need to be later). diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 3f504d75dfc..e22c9c68de6 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -285,7 +285,6 @@ pub enum ExternDepSpec { #[derive(PartialEq, Debug)] pub enum BuiltinLintDiagnostics { Normal, - BareTraitObject(Span, /* is_global */ bool), AbsPathWithModule(Span), ProcMacroDeriveResolutionFallback(Span), MacroExpandedMacroExportsAccessedByAbsolutePaths(Span), diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 78e33544655..e2de9f12aaa 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -771,11 +771,24 @@ rustc_queries! { desc { |tcx| "type-checking `{}`", tcx.def_path_str(key.to_def_id()) } cache_on_disk_if { true } load_cached(tcx, id) { - let typeck_results: Option<ty::TypeckResults<'tcx>> = tcx - .on_disk_cache().as_ref() - .and_then(|c| c.try_load_query_result(*tcx, id)); + #[cfg(bootstrap)] + { + match match tcx.on_disk_cache().as_ref() { + Some(c) => c.try_load_query_result(*tcx, id), + None => None, + } { + Some(x) => Some(&*tcx.arena.alloc(x)), + None => None, + } + } + #[cfg(not(bootstrap))] + { + let typeck_results: Option<ty::TypeckResults<'tcx>> = tcx + .on_disk_cache().as_ref() + .and_then(|c| c.try_load_query_result(*tcx, id)); - typeck_results.map(|x| &*tcx.arena.alloc(x)) + typeck_results.map(|x| &*tcx.arena.alloc(x)) + } } } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 558b1ce082e..8be95b2d95a 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -68,6 +68,12 @@ fn inline<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool { if body.source.promoted.is_some() { return false; } + // Avoid inlining into generators, since their `optimized_mir` is used for layout computation, + // which can create a cycle, even when no attempt is made to inline the function in the other + // direction. + if body.generator.is_some() { + return false; + } let mut this = Inliner { tcx, @@ -202,14 +208,6 @@ impl<'tcx> Inliner<'tcx> { if let Some(callee_def_id) = callee.def_id().as_local() { let callee_hir_id = self.tcx.hir().local_def_id_to_hir_id(callee_def_id); - // Avoid inlining into generators, - // since their `optimized_mir` is used for layout computation, which can - // create a cycle, even when no attempt is made to inline the function - // in the other direction. - if caller_body.generator.is_some() { - return Err("local generator (query cycle avoidance)"); - } - // Avoid a cycle here by only using `instance_mir` only if we have // a lower `HirId` than the callee. This ensures that the callee will // not inline us. This trick only works without incremental compilation. diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 516e301ec3a..618aa3fd002 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -223,7 +223,7 @@ impl<'a> Parser<'a> { (Ident::empty(), ItemKind::Use(tree)) } else if self.check_fn_front_matter(def_final) { // FUNCTION ITEM - let (ident, sig, generics, body) = self.parse_fn(attrs, fn_parse_mode, lo)?; + let (ident, sig, generics, body) = self.parse_fn(attrs, fn_parse_mode, lo, vis)?; (ident, ItemKind::Fn(Box::new(Fn { defaultness: def(), sig, generics, body }))) } else if self.eat_keyword(kw::Extern) { if self.eat_keyword(kw::Crate) { @@ -1511,9 +1511,16 @@ impl<'a> Parser<'a> { let (ident, is_raw) = self.ident_or_err()?; if !is_raw && ident.is_reserved() { let err = if self.check_fn_front_matter(false) { + let inherited_vis = Visibility { + span: rustc_span::DUMMY_SP, + kind: VisibilityKind::Inherited, + tokens: None, + }; // We use `parse_fn` to get a span for the function let fn_parse_mode = FnParseMode { req_name: |_| true, req_body: true }; - if let Err(mut db) = self.parse_fn(&mut Vec::new(), fn_parse_mode, lo) { + if let Err(mut db) = + self.parse_fn(&mut Vec::new(), fn_parse_mode, lo, &inherited_vis) + { db.delay_as_bug(); } let mut err = self.struct_span_err( @@ -1793,8 +1800,9 @@ impl<'a> Parser<'a> { attrs: &mut Vec<Attribute>, fn_parse_mode: FnParseMode, sig_lo: Span, + vis: &Visibility, ) -> PResult<'a, (Ident, FnSig, Generics, Option<P<Block>>)> { - let header = self.parse_fn_front_matter()?; // `const ... fn` + let header = self.parse_fn_front_matter(vis)?; // `const ... fn` let ident = self.parse_ident()?; // `foo` let mut generics = self.parse_generics()?; // `<'a, T, ...>` let decl = @@ -1903,12 +1911,15 @@ impl<'a> Parser<'a> { /// Parses all the "front matter" (or "qualifiers") for a `fn` declaration, /// up to and including the `fn` keyword. The formal grammar is: /// - /// ``` + /// ```text /// Extern = "extern" StringLit? ; /// FnQual = "const"? "async"? "unsafe"? Extern? ; /// FnFrontMatter = FnQual "fn" ; /// ``` - pub(super) fn parse_fn_front_matter(&mut self) -> PResult<'a, FnHeader> { + /// + /// `vis` represents the visibility that was already parsed, if any. Use + /// `Visibility::Inherited` when no visibility is known. + pub(super) fn parse_fn_front_matter(&mut self, orig_vis: &Visibility) -> PResult<'a, FnHeader> { let sp_start = self.token.span; let constness = self.parse_constness(); @@ -1934,51 +1945,94 @@ impl<'a> Parser<'a> { Ok(false) => unreachable!(), Err(mut err) => { // Qualifier keywords ordering check + enum WrongKw { + Duplicated(Span), + Misplaced(Span), + } - // This will allow the machine fix to directly place the keyword in the correct place - let current_qual_sp = if self.check_keyword(kw::Const) { - Some(async_start_sp) + // This will allow the machine fix to directly place the keyword in the correct place or to indicate + // that the keyword is already present and the second instance should be removed. + let wrong_kw = if self.check_keyword(kw::Const) { + match constness { + Const::Yes(sp) => Some(WrongKw::Duplicated(sp)), + Const::No => Some(WrongKw::Misplaced(async_start_sp)), + } } else if self.check_keyword(kw::Async) { - Some(unsafe_start_sp) + match asyncness { + Async::Yes { span, .. } => Some(WrongKw::Duplicated(span)), + Async::No => Some(WrongKw::Misplaced(unsafe_start_sp)), + } } else if self.check_keyword(kw::Unsafe) { - Some(ext_start_sp) + match unsafety { + Unsafe::Yes(sp) => Some(WrongKw::Duplicated(sp)), + Unsafe::No => Some(WrongKw::Misplaced(ext_start_sp)), + } } else { None }; - if let Some(current_qual_sp) = current_qual_sp { - let current_qual_sp = current_qual_sp.to(self.prev_token.span); - if let Ok(current_qual) = self.span_to_snippet(current_qual_sp) { - let invalid_qual_sp = self.token.uninterpolated_span(); - let invalid_qual = self.span_to_snippet(invalid_qual_sp).unwrap(); + // The keyword is already present, suggest removal of the second instance + if let Some(WrongKw::Duplicated(original_sp)) = wrong_kw { + let original_kw = self + .span_to_snippet(original_sp) + .expect("Span extracted directly from keyword should always work"); + + err.span_suggestion( + self.token.uninterpolated_span(), + &format!("`{}` already used earlier, remove this one", original_kw), + "".to_string(), + Applicability::MachineApplicable, + ) + .span_note(original_sp, &format!("`{}` first seen here", original_kw)); + } + // The keyword has not been seen yet, suggest correct placement in the function front matter + else if let Some(WrongKw::Misplaced(correct_pos_sp)) = wrong_kw { + let correct_pos_sp = correct_pos_sp.to(self.prev_token.span); + if let Ok(current_qual) = self.span_to_snippet(correct_pos_sp) { + let misplaced_qual_sp = self.token.uninterpolated_span(); + let misplaced_qual = self.span_to_snippet(misplaced_qual_sp).unwrap(); err.span_suggestion( - current_qual_sp.to(invalid_qual_sp), - &format!("`{}` must come before `{}`", invalid_qual, current_qual), - format!("{} {}", invalid_qual, current_qual), - Applicability::MachineApplicable, - ).note("keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`"); + correct_pos_sp.to(misplaced_qual_sp), + &format!("`{}` must come before `{}`", misplaced_qual, current_qual), + format!("{} {}", misplaced_qual, current_qual), + Applicability::MachineApplicable, + ).note("keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`"); } } - // Recover incorrect visibility order such as `async pub`. + // Recover incorrect visibility order such as `async pub` else if self.check_keyword(kw::Pub) { let sp = sp_start.to(self.prev_token.span); if let Ok(snippet) = self.span_to_snippet(sp) { - let vis = match self.parse_visibility(FollowedByType::No) { + let current_vis = match self.parse_visibility(FollowedByType::No) { Ok(v) => v, Err(mut d) => { d.cancel(); return Err(err); } }; - let vs = pprust::vis_to_string(&vis); + let vs = pprust::vis_to_string(¤t_vis); let vs = vs.trim_end(); - err.span_suggestion( - sp_start.to(self.prev_token.span), - &format!("visibility `{}` must come before `{}`", vs, snippet), - format!("{} {}", vs, snippet), - Applicability::MachineApplicable, - ); + + // There was no explicit visibility + if matches!(orig_vis.kind, VisibilityKind::Inherited) { + err.span_suggestion( + sp_start.to(self.prev_token.span), + &format!("visibility `{}` must come before `{}`", vs, snippet), + format!("{} {}", vs, snippet), + Applicability::MachineApplicable, + ); + } + // There was an explicit visibility + else { + err.span_suggestion( + current_vis.span, + "there is already a visibility modifier, remove one", + "".to_string(), + Applicability::MachineApplicable, + ) + .span_note(orig_vis.span, "explicit visibility first seen here"); + } } } return Err(err); diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 9bfde0e3900..02a774ba129 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -474,7 +474,13 @@ impl<'a> Parser<'a> { params: Vec<GenericParam>, recover_return_sign: RecoverReturnSign, ) -> PResult<'a, TyKind> { - let ast::FnHeader { ext, unsafety, constness, asyncness } = self.parse_fn_front_matter()?; + let inherited_vis = rustc_ast::Visibility { + span: rustc_span::DUMMY_SP, + kind: rustc_ast::VisibilityKind::Inherited, + tokens: None, + }; + let ast::FnHeader { ext, unsafety, constness, asyncness } = + self.parse_fn_front_matter(&inherited_vis)?; let decl = self.parse_fn_decl(|_| false, AllowPlus::No, recover_return_sign)?; let whole_span = lo.to(self.prev_token.span); if let ast::Const::Yes(span) = constness { diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 23c3b5af262..8db706c3709 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -13,6 +13,7 @@ use crate::errors::{ }; use crate::middle::resolve_lifetime as rl; use crate::require_c_abi_if_c_variadic; +use rustc_ast::TraitObjectSyntax; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{struct_span_err, Applicability, ErrorReported, FatalError}; use rustc_hir as hir; @@ -24,7 +25,8 @@ use rustc_hir::{GenericArg, GenericArgs}; use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, Subst, SubstsRef}; use rustc_middle::ty::GenericParamDefKind; use rustc_middle::ty::{self, Const, DefIdTree, Ty, TyCtxt, TypeFoldable}; -use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS; +use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECTS}; +use rustc_span::edition::Edition; use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; @@ -2266,13 +2268,19 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// Parses the programmer's textual representation of a type into our /// internal notion of a type. pub fn ast_ty_to_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> { - self.ast_ty_to_ty_inner(ast_ty, false) + self.ast_ty_to_ty_inner(ast_ty, false, false) + } + + /// Parses the programmer's textual representation of a type into our + /// internal notion of a type. This is meant to be used within a path. + pub fn ast_ty_to_ty_in_path(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> { + self.ast_ty_to_ty_inner(ast_ty, false, true) } /// Turns a `hir::Ty` into a `Ty`. For diagnostics' purposes we keep track of whether trait /// objects are borrowed like `&dyn Trait` to avoid emitting redundant errors. #[tracing::instrument(level = "debug", skip(self))] - fn ast_ty_to_ty_inner(&self, ast_ty: &hir::Ty<'_>, borrowed: bool) -> Ty<'tcx> { + fn ast_ty_to_ty_inner(&self, ast_ty: &hir::Ty<'_>, borrowed: bool, in_path: bool) -> Ty<'tcx> { let tcx = self.tcx(); let result_ty = match ast_ty.kind { @@ -2283,7 +2291,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { hir::TyKind::Rptr(ref region, ref mt) => { let r = self.ast_region_to_region(region, None); debug!(?r); - let t = self.ast_ty_to_ty_inner(mt.ty, true); + let t = self.ast_ty_to_ty_inner(mt.ty, true, false); tcx.mk_ref(r, ty::TypeAndMut { ty: t, mutbl: mt.mutbl }) } hir::TyKind::Never => tcx.types.never, @@ -2302,6 +2310,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { )) } hir::TyKind::TraitObject(bounds, ref lifetime, _) => { + self.maybe_lint_bare_trait(ast_ty, in_path); self.conv_object_ty_poly_trait_ref(ast_ty.span, bounds, lifetime, borrowed) } hir::TyKind::Path(hir::QPath::Resolved(ref maybe_qself, ref path)) => { @@ -2329,7 +2338,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } hir::TyKind::Path(hir::QPath::TypeRelative(ref qself, ref segment)) => { debug!(?qself, ?segment); - let ty = self.ast_ty_to_ty(qself); + let ty = self.ast_ty_to_ty_inner(qself, false, true); let res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = qself.kind { path.res @@ -2586,4 +2595,62 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } Some(r) } + + fn maybe_lint_bare_trait(&self, self_ty: &hir::Ty<'_>, in_path: bool) { + let tcx = self.tcx(); + if let hir::TyKind::TraitObject([poly_trait_ref, ..], _, TraitObjectSyntax::None) = + self_ty.kind + { + let needs_bracket = in_path + && !tcx + .sess + .source_map() + .span_to_prev_source(self_ty.span) + .ok() + .map_or(false, |s| s.trim_end().ends_with('<')); + + let is_global = poly_trait_ref.trait_ref.path.is_global(); + let sugg = Vec::from_iter([ + ( + self_ty.span.shrink_to_lo(), + format!( + "{}dyn {}", + if needs_bracket { "<" } else { "" }, + if is_global { "(" } else { "" }, + ), + ), + ( + self_ty.span.shrink_to_hi(), + format!( + "{}{}", + if is_global { ")" } else { "" }, + if needs_bracket { ">" } else { "" }, + ), + ), + ]); + if self_ty.span.edition() >= Edition::Edition2021 { + let msg = "trait objects must include the `dyn` keyword"; + let label = "add `dyn` keyword before this trait"; + rustc_errors::struct_span_err!(tcx.sess, self_ty.span, E0782, "{}", msg) + .multipart_suggestion_verbose(label, sugg, Applicability::MachineApplicable) + .emit(); + } else { + let msg = "trait objects without an explicit `dyn` are deprecated"; + tcx.struct_span_lint_hir( + BARE_TRAIT_OBJECTS, + self_ty.hir_id, + self_ty.span, + |lint| { + lint.build(msg) + .multipart_suggestion_verbose( + "use `dyn`", + sugg, + Applicability::MachineApplicable, + ) + .emit() + }, + ); + } + } + } } diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs index 738af9bfb8c..67630fd4e58 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs @@ -6,7 +6,6 @@ use crate::check::callee::{self, DeferredCallResolution}; use crate::check::method::{self, MethodCallee, SelfSource}; use crate::check::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy}; -use rustc_ast::TraitObjectSyntax; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, DiagnosticBuilder, ErrorReported}; @@ -14,7 +13,7 @@ use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; -use rustc_hir::{ExprKind, GenericArg, Node, QPath, TyKind}; +use rustc_hir::{ExprKind, GenericArg, Node, QPath}; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; use rustc_infer::infer::{InferOk, InferResult}; @@ -28,8 +27,6 @@ use rustc_middle::ty::{ Ty, UserType, }; use rustc_session::lint; -use rustc_session::lint::builtin::BARE_TRAIT_OBJECTS; -use rustc_span::edition::Edition; use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::{original_sp, DUMMY_SP}; use rustc_span::symbol::{kw, sym, Ident}; @@ -855,7 +852,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // to be object-safe. // We manually call `register_wf_obligation` in the success path // below. - (<dyn AstConv<'_>>::ast_ty_to_ty(self, qself), qself, segment) + (<dyn AstConv<'_>>::ast_ty_to_ty_in_path(self, qself), qself, segment) } QPath::LangItem(..) => { bug!("`resolve_ty_and_res_fully_qualified_call` called on `LangItem`") @@ -901,7 +898,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); if result.is_ok() { - self.maybe_lint_bare_trait(qpath, hir_id, span); self.register_wf_obligation(ty.into(), qself.span, traits::WellFormed(None)); } @@ -914,56 +910,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) } - fn maybe_lint_bare_trait(&self, qpath: &QPath<'_>, hir_id: hir::HirId, span: Span) { - if let QPath::TypeRelative(self_ty, _) = qpath { - if let TyKind::TraitObject([poly_trait_ref, ..], _, TraitObjectSyntax::None) = - self_ty.kind - { - let msg = "trait objects without an explicit `dyn` are deprecated"; - let (sugg, app) = match self.tcx.sess.source_map().span_to_snippet(self_ty.span) { - Ok(s) if poly_trait_ref.trait_ref.path.is_global() => { - (format!("dyn ({})", s), Applicability::MachineApplicable) - } - Ok(s) => (format!("dyn {}", s), Applicability::MachineApplicable), - Err(_) => ("dyn <type>".to_string(), Applicability::HasPlaceholders), - }; - // Wrap in `<..>` if it isn't already. - let sugg = match self.tcx.sess.source_map().span_to_snippet(span) { - Ok(s) if s.starts_with('<') => sugg, - _ => format!("<{}>", sugg), - }; - let sugg_label = "use `dyn`"; - if self.sess().edition() >= Edition::Edition2021 { - let mut err = rustc_errors::struct_span_err!( - self.sess(), - self_ty.span, - E0782, - "{}", - msg, - ); - err.span_suggestion( - self_ty.span, - sugg_label, - sugg, - Applicability::MachineApplicable, - ) - .emit(); - } else { - self.tcx.struct_span_lint_hir( - BARE_TRAIT_OBJECTS, - hir_id, - self_ty.span, - |lint| { - let mut db = lint.build(msg); - db.span_suggestion(self_ty.span, sugg_label, sugg, app); - db.emit() - }, - ); - } - } - } - } - /// Given a function `Node`, return its `FnDecl` if it exists, or `None` otherwise. pub(in super::super) fn get_node_fn_decl( &self, |
