diff --git a/Cargo.lock b/Cargo.lock index 8bd1e5f3308b4..32a6f5fce74fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,7 +74,7 @@ name = "arena" version = "0.0.0" dependencies = [ "rustc_data_structures", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -492,7 +492,7 @@ dependencies = [ "regex-syntax", "semver", "serde", - "smallvec 1.0.0", + "smallvec 1.4.0", "toml", "unicode-normalization", "url 2.1.0", @@ -2428,7 +2428,7 @@ dependencies = [ "cloudabi", "libc", "redox_syscall", - "smallvec 1.0.0", + "smallvec 1.4.0", "winapi 0.3.8", ] @@ -3151,7 +3151,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81dfcfbb0ddfd533abf8c076e3b49d1e5042d1962526a12ce2c66d514b24cca3" dependencies = [ "rustc-ap-rustc_data_structures", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -3174,7 +3174,7 @@ dependencies = [ "rustc-ap-rustc_span", "rustc-ap-serialize", "scoped-tls", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -3223,7 +3223,7 @@ dependencies = [ "rustc-ap-rustc_session", "rustc-ap-rustc_span", "rustc-ap-serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -3249,7 +3249,7 @@ dependencies = [ "rustc-hash", "rustc-rayon", "rustc-rayon-core", - "smallvec 1.0.0", + "smallvec 1.4.0", "stable_deref_trait", "winapi 0.3.8", ] @@ -3291,7 +3291,7 @@ dependencies = [ "rustc-ap-rustc_session", "rustc-ap-rustc_span", "rustc-ap-serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -3318,7 +3318,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32220c3e6cdf226f38e4474b747dca15f3106bb680c74f10b299af3f6cdb1663" dependencies = [ "rustc-ap-serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -3358,7 +3358,7 @@ dependencies = [ "rustc-ap-rustc_lexer", "rustc-ap-rustc_session", "rustc-ap-rustc_span", - "smallvec 1.0.0", + "smallvec 1.4.0", "unicode-normalization", ] @@ -3423,7 +3423,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "756e8f526ec7906e132188bf25e3c10a6ee42ab77294ecb3b3602647f0508eef" dependencies = [ "indexmap", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -3507,7 +3507,7 @@ dependencies = [ "serde", "serde_json", "smallvec 0.6.10", - "smallvec 1.0.0", + "smallvec 1.4.0", "syn 0.15.35", "url 2.1.0", "winapi 0.3.8", @@ -3518,7 +3518,7 @@ name = "rustc_apfloat" version = "0.0.0" dependencies = [ "bitflags", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -3533,7 +3533,7 @@ dependencies = [ "rustc_span", "scoped-tls", "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -3551,7 +3551,7 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -3612,7 +3612,7 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -3641,7 +3641,7 @@ dependencies = [ "rustc_span", "rustc_target", "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -3694,7 +3694,7 @@ dependencies = [ "rustc-rayon-core", "rustc_index", "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", "stable_deref_trait", "winapi 0.3.8", ] @@ -3768,7 +3768,7 @@ dependencies = [ "rustc_session", "rustc_span", "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -3797,7 +3797,7 @@ dependencies = [ "rustc_span", "rustc_target", "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -3833,7 +3833,7 @@ name = "rustc_index" version = "0.0.0" dependencies = [ "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -3853,7 +3853,7 @@ dependencies = [ "rustc_span", "rustc_target", "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -3895,7 +3895,7 @@ dependencies = [ "rustc_ty", "rustc_typeck", "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", "tempfile", "winapi 0.3.8", ] @@ -3968,7 +3968,7 @@ dependencies = [ "rustc_span", "rustc_target", "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", "stable_deref_trait", "winapi 0.3.8", ] @@ -4000,7 +4000,7 @@ dependencies = [ "rustc_target", "scoped-tls", "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -4029,7 +4029,7 @@ dependencies = [ "rustc_target", "rustc_trait_selection", "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -4053,7 +4053,7 @@ dependencies = [ "rustc_target", "rustc_trait_selection", "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -4134,7 +4134,7 @@ dependencies = [ "rustc_index", "rustc_span", "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -4157,7 +4157,7 @@ dependencies = [ "rustc_middle", "rustc_session", "rustc_span", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -4272,7 +4272,7 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -4287,7 +4287,7 @@ dependencies = [ "rustc_middle", "rustc_span", "rustc_trait_selection", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -4324,7 +4324,7 @@ dependencies = [ "rustc_span", "rustc_target", "rustc_trait_selection", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -4568,7 +4568,7 @@ name = "serialize" version = "0.0.0" dependencies = [ "indexmap", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -4635,9 +4635,9 @@ checksum = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" [[package]] name = "smallvec" -version = "1.0.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ecf3b85f68e8abaa7555aa5abdb1153079387e60b718283d732f03897fcfc86" +checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" [[package]] name = "socket2" @@ -5359,11 +5359,11 @@ dependencies = [ [[package]] name = "unicode-normalization" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b561e267b2326bb4cebfc0ef9e68355c7abe6c6f522aeac2f5bf95d56c59bdcf" +checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" dependencies = [ - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -5374,10 +5374,11 @@ checksum = "5b2c5c29e805da6817f5af6a627d65adb045cebf05cccd5a3493d6109454391c" [[package]] name = "unicode-security" -version = "0.0.2" +version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c49d35967fa037b881acc34ef717c38c4b5560eba10e3685271b3f530bb19634" +checksum = "a5f9011bbed9c13372bc8df618b55a38138445199caf3b61d432c6859c36dee0" dependencies = [ + "unicode-normalization", "unicode-script", ] diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 7b357bb487a0a..f74c6862006c1 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -23,7 +23,6 @@ // * The `raw` and `bytes` submodules. // * Boilerplate trait implementations. -use crate::borrow::Borrow; use crate::cmp; use crate::cmp::Ordering::{self, Equal, Greater, Less}; use crate::fmt; @@ -2157,14 +2156,16 @@ impl [T] { /// assert_eq!(buf, vec![1; 10]); /// ``` #[unstable(feature = "slice_fill", issue = "70758")] - pub fn fill(&mut self, value: V) + pub fn fill(&mut self, value: T) where - V: Borrow, T: Clone, { - let value = value.borrow(); - for el in self { - el.clone_from(value) + if let Some((last, elems)) = self.split_last_mut() { + for el in elems { + el.clone_from(&value); + } + + *last = value } } diff --git a/src/librustc_ast_passes/ast_validation.rs b/src/librustc_ast_passes/ast_validation.rs index 395fd7460850f..5f47c9eb2c2a6 100644 --- a/src/librustc_ast_passes/ast_validation.rs +++ b/src/librustc_ast_passes/ast_validation.rs @@ -23,6 +23,7 @@ use rustc_session::Session; use rustc_span::symbol::{kw, sym}; use rustc_span::Span; use std::mem; +use std::ops::DerefMut; const MORE_EXTERN: &str = "for more information, visit https://doc.rust-lang.org/std/keyword.extern.html"; @@ -1113,17 +1114,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { for predicate in &generics.where_clause.predicates { if let WherePredicate::EqPredicate(ref predicate) = *predicate { - self.err_handler() - .struct_span_err( - predicate.span, - "equality constraints are not yet supported in `where` clauses", - ) - .span_label(predicate.span, "not supported") - .note( - "see issue #20041 \ - for more information", - ) - .emit(); + deny_equality_constraints(self, predicate, generics); } } @@ -1300,6 +1291,87 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } } +fn deny_equality_constraints( + this: &mut AstValidator<'_>, + predicate: &WhereEqPredicate, + generics: &Generics, +) { + let mut err = this.err_handler().struct_span_err( + predicate.span, + "equality constraints are not yet supported in `where` clauses", + ); + err.span_label(predicate.span, "not supported"); + + // Given `::Bar = RhsTy`, suggest `A: Foo`. + if let TyKind::Path(Some(qself), full_path) = &predicate.lhs_ty.kind { + if let TyKind::Path(None, path) = &qself.ty.kind { + match &path.segments[..] { + [PathSegment { ident, args: None, .. }] => { + for param in &generics.params { + if param.ident == *ident { + let param = ident; + match &full_path.segments[qself.position..] { + [PathSegment { ident, .. }] => { + // Make a new `Path` from `foo::Bar` to `Foo`. + let mut assoc_path = full_path.clone(); + // Remove `Bar` from `Foo::Bar`. + assoc_path.segments.pop(); + let len = assoc_path.segments.len() - 1; + // Build ``. + let arg = AngleBracketedArg::Constraint(AssocTyConstraint { + id: rustc_ast::node_id::DUMMY_NODE_ID, + ident: *ident, + kind: AssocTyConstraintKind::Equality { + ty: predicate.rhs_ty.clone(), + }, + span: ident.span, + }); + // Add `` to `Foo`. + match &mut assoc_path.segments[len].args { + Some(args) => match args.deref_mut() { + GenericArgs::Parenthesized(_) => continue, + GenericArgs::AngleBracketed(args) => { + args.args.push(arg); + } + }, + empty_args => { + *empty_args = AngleBracketedArgs { + span: ident.span, + args: vec![arg], + } + .into(); + } + } + err.span_suggestion_verbose( + predicate.span, + &format!( + "if `{}` is an associated type you're trying to set, \ + use the associated type binding syntax", + ident + ), + format!( + "{}: {}", + param, + pprust::path_to_string(&assoc_path) + ), + Applicability::MaybeIncorrect, + ); + } + _ => {} + }; + } + } + } + _ => {} + } + } + } + err.note( + "see issue #20041 for more information", + ); + err.emit(); +} + pub fn check_crate(session: &Session, krate: &Crate, lints: &mut LintBuffer) -> bool { let mut validator = AstValidator { session, diff --git a/src/librustc_expand/proc_macro_server.rs b/src/librustc_expand/proc_macro_server.rs index 5f21ff503d59e..10baffb28ac2d 100644 --- a/src/librustc_expand/proc_macro_server.rs +++ b/src/librustc_expand/proc_macro_server.rs @@ -141,10 +141,10 @@ impl FromInternal<(TreeAndJoint, &'_ ParseSess, &'_ mut Vec)> SingleQuote => op!('\''), Ident(name, false) if name == kw::DollarCrate => tt!(Ident::dollar_crate()), - Ident(name, is_raw) => tt!(Ident::new(name, is_raw)), + Ident(name, is_raw) => tt!(Ident::new(sess, name, is_raw)), Lifetime(name) => { let ident = ast::Ident::new(name, span).without_first_quote(); - stack.push(tt!(Ident::new(ident.name, false))); + stack.push(tt!(Ident::new(sess, ident.name, false))); tt!(Punct::new('\'', true)) } Literal(lit) => tt!(Literal { lit }), @@ -322,7 +322,7 @@ impl Ident { false } } - fn new(sym: Symbol, is_raw: bool, span: Span) -> Ident { + fn new(sess: &ParseSess, sym: Symbol, is_raw: bool, span: Span) -> Ident { let sym = nfc_normalize(&sym.as_str()); let string = sym.as_str(); if !Self::is_valid(&string) { @@ -331,6 +331,7 @@ impl Ident { if is_raw && !sym.can_be_raw() { panic!("`{}` cannot be a raw identifier", string); } + sess.symbol_gallery.insert(sym, span); Ident { sym, is_raw, span } } fn dollar_crate(span: Span) -> Ident { @@ -495,7 +496,7 @@ impl server::Punct for Rustc<'_> { impl server::Ident for Rustc<'_> { fn new(&mut self, string: &str, span: Self::Span, is_raw: bool) -> Self::Ident { - Ident::new(Symbol::intern(string), is_raw, span) + Ident::new(self.sess, Symbol::intern(string), is_raw, span) } fn span(&mut self, ident: Self::Ident) -> Self::Span { ident.span diff --git a/src/librustc_hir/hir.rs b/src/librustc_hir/hir.rs index 258428d77da10..e6d673b30f7bc 100644 --- a/src/librustc_hir/hir.rs +++ b/src/librustc_hir/hir.rs @@ -2626,8 +2626,42 @@ impl Node<'_> { match self { Node::TraitItem(TraitItem { generics, .. }) | Node::ImplItem(ImplItem { generics, .. }) - | Node::Item(Item { kind: ItemKind::Fn(_, generics, _), .. }) => Some(generics), + | Node::Item(Item { + kind: + ItemKind::Trait(_, _, generics, ..) + | ItemKind::Impl { generics, .. } + | ItemKind::Fn(_, generics, _), + .. + }) => Some(generics), _ => None, } } + + pub fn hir_id(&self) -> Option { + match self { + Node::Item(Item { hir_id, .. }) + | Node::ForeignItem(ForeignItem { hir_id, .. }) + | Node::TraitItem(TraitItem { hir_id, .. }) + | Node::ImplItem(ImplItem { hir_id, .. }) + | Node::Field(StructField { hir_id, .. }) + | Node::AnonConst(AnonConst { hir_id, .. }) + | Node::Expr(Expr { hir_id, .. }) + | Node::Stmt(Stmt { hir_id, .. }) + | Node::Ty(Ty { hir_id, .. }) + | Node::Binding(Pat { hir_id, .. }) + | Node::Pat(Pat { hir_id, .. }) + | Node::Arm(Arm { hir_id, .. }) + | Node::Block(Block { hir_id, .. }) + | Node::Local(Local { hir_id, .. }) + | Node::MacroDef(MacroDef { hir_id, .. }) + | Node::Lifetime(Lifetime { hir_id, .. }) + | Node::Param(Param { hir_id, .. }) + | Node::GenericParam(GenericParam { hir_id, .. }) => Some(*hir_id), + Node::TraitRef(TraitRef { hir_ref_id, .. }) => Some(*hir_ref_id), + Node::PathSegment(PathSegment { hir_id, .. }) => *hir_id, + Node::Variant(Variant { id, .. }) => Some(*id), + Node::Ctor(variant) => variant.ctor_hir_id(), + Node::Crate(_) | Node::Visibility(_) => None, + } + } } diff --git a/src/librustc_infer/infer/nll_relate/mod.rs b/src/librustc_infer/infer/nll_relate/mod.rs index 0fb926d94166a..a2907e6e373b8 100644 --- a/src/librustc_infer/infer/nll_relate/mod.rs +++ b/src/librustc_infer/infer/nll_relate/mod.rs @@ -657,7 +657,7 @@ where // Reset the ambient variance to covariant. This is needed // to correctly handle cases like // - // for<'a> fn(&'a u32, &'a u3) == for<'b, 'c> fn(&'b u32, &'c u32) + // for<'a> fn(&'a u32, &'a u32) == for<'b, 'c> fn(&'b u32, &'c u32) // // Somewhat surprisingly, these two types are actually // **equal**, even though the one on the right looks more diff --git a/src/librustc_lint/Cargo.toml b/src/librustc_lint/Cargo.toml index b238a3156fae5..ada6f2a9381dc 100644 --- a/src/librustc_lint/Cargo.toml +++ b/src/librustc_lint/Cargo.toml @@ -10,7 +10,7 @@ path = "lib.rs" [dependencies] log = "0.4" -unicode-security = "0.0.2" +unicode-security = "0.0.3" rustc_middle = { path = "../librustc_middle" } rustc_ast_pretty = { path = "../librustc_ast_pretty" } rustc_attr = { path = "../librustc_attr" } diff --git a/src/librustc_lint/levels.rs b/src/librustc_lint/levels.rs index 12f4cd33689e6..274e57ae64cac 100644 --- a/src/librustc_lint/levels.rs +++ b/src/librustc_lint/levels.rs @@ -388,6 +388,11 @@ impl<'s> LintLevelsBuilder<'s> { self.cur = push.prev; } + /// Find the lint level for a lint. + pub fn lint_level(&self, lint: &'static Lint) -> (Level, LintSource) { + self.sets.get_lint_level(lint, self.cur, None, self.sess) + } + /// Used to emit a lint-related diagnostic based on the current state of /// this lint context. pub fn struct_lint( @@ -396,7 +401,7 @@ impl<'s> LintLevelsBuilder<'s> { span: Option, decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), ) { - let (level, src) = self.sets.get_lint_level(lint, self.cur, None, self.sess); + let (level, src) = self.lint_level(lint); struct_lint_level(self.sess, lint, level, src, span, decorate) } diff --git a/src/librustc_lint/non_ascii_idents.rs b/src/librustc_lint/non_ascii_idents.rs index 470fac2675bac..94fc1a228df03 100644 --- a/src/librustc_lint/non_ascii_idents.rs +++ b/src/librustc_lint/non_ascii_idents.rs @@ -1,5 +1,9 @@ use crate::{EarlyContext, EarlyLintPass, LintContext}; use rustc_ast::ast; +use rustc_data_structures::fx::FxHashMap; +use rustc_span::symbol::SymbolStr; +use std::hash::{Hash, Hasher}; +use std::ops::Deref; declare_lint! { pub NON_ASCII_IDENTS, @@ -13,9 +17,144 @@ declare_lint! { "detects uncommon Unicode codepoints in identifiers" } -declare_lint_pass!(NonAsciiIdents => [NON_ASCII_IDENTS, UNCOMMON_CODEPOINTS]); +// FIXME: Change this to warn. +declare_lint! { + pub CONFUSABLE_IDENTS, + Allow, + "detects visually confusable pairs between identifiers" +} + +declare_lint_pass!(NonAsciiIdents => [NON_ASCII_IDENTS, UNCOMMON_CODEPOINTS, CONFUSABLE_IDENTS]); + +enum CowBoxSymStr { + Interned(SymbolStr), + Owned(Box), +} + +impl Deref for CowBoxSymStr { + type Target = str; + + fn deref(&self) -> &str { + match self { + CowBoxSymStr::Interned(interned) => interned, + CowBoxSymStr::Owned(ref owned) => owned, + } + } +} + +impl Hash for CowBoxSymStr { + #[inline] + fn hash(&self, state: &mut H) { + Hash::hash(&**self, state) + } +} + +impl PartialEq for CowBoxSymStr { + #[inline] + fn eq(&self, other: &CowBoxSymStr) -> bool { + PartialEq::eq(&**self, &**other) + } +} + +impl Eq for CowBoxSymStr {} + +fn calc_skeleton(symbol_str: SymbolStr, buffer: &'_ mut String) -> CowBoxSymStr { + use std::mem::swap; + use unicode_security::confusable_detection::skeleton; + buffer.clear(); + buffer.extend(skeleton(&symbol_str)); + if symbol_str == *buffer { + CowBoxSymStr::Interned(symbol_str) + } else { + let mut owned = String::new(); + swap(buffer, &mut owned); + CowBoxSymStr::Owned(owned.into_boxed_str()) + } +} + +fn is_in_ascii_confusable_closure(c: char) -> bool { + // FIXME: move this table to `unicode_security` crate. + // data here corresponds to Unicode 13. + const ASCII_CONFUSABLE_CLOSURE: &[(u64, u64)] = &[(0x00, 0x7f), (0xba, 0xba), (0x2080, 0x2080)]; + let c = c as u64; + for &(range_start, range_end) in ASCII_CONFUSABLE_CLOSURE { + if c >= range_start && c <= range_end { + return true; + } + } + false +} + +fn is_in_ascii_confusable_closure_relevant_list(c: char) -> bool { + // FIXME: move this table to `unicode_security` crate. + // data here corresponds to Unicode 13. + const ASCII_CONFUSABLE_CLOSURE_RELEVANT_LIST: &[u64] = &[ + 0x22, 0x25, 0x27, 0x2f, 0x30, 0x31, 0x49, 0x4f, 0x60, 0x6c, 0x6d, 0x6e, 0x72, 0x7c, 0xba, + 0x2080, + ]; + let c = c as u64; + for &item in ASCII_CONFUSABLE_CLOSURE_RELEVANT_LIST { + if c == item { + return true; + } + } + false +} impl EarlyLintPass for NonAsciiIdents { + fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) { + use rustc_session::lint::Level; + if cx.builder.lint_level(CONFUSABLE_IDENTS).0 == Level::Allow { + return; + } + let symbols = cx.sess.parse_sess.symbol_gallery.symbols.lock(); + let mut symbol_strs_and_spans = Vec::with_capacity(symbols.len()); + let mut in_fast_path = true; + for (symbol, sp) in symbols.iter() { + // fast path + let symbol_str = symbol.as_str(); + if !symbol_str.chars().all(is_in_ascii_confusable_closure) { + // fallback to slow path. + symbol_strs_and_spans.clear(); + in_fast_path = false; + break; + } + if symbol_str.chars().any(is_in_ascii_confusable_closure_relevant_list) { + symbol_strs_and_spans.push((symbol_str, *sp)); + } + } + if !in_fast_path { + // slow path + for (symbol, sp) in symbols.iter() { + let symbol_str = symbol.as_str(); + symbol_strs_and_spans.push((symbol_str, *sp)); + } + } + drop(symbols); + symbol_strs_and_spans.sort_by_key(|x| x.0.clone()); + let mut skeleton_map = + FxHashMap::with_capacity_and_hasher(symbol_strs_and_spans.len(), Default::default()); + let mut str_buf = String::new(); + for (symbol_str, sp) in symbol_strs_and_spans { + let skeleton = calc_skeleton(symbol_str.clone(), &mut str_buf); + skeleton_map + .entry(skeleton) + .and_modify(|(existing_symbolstr, existing_span)| { + cx.struct_span_lint(CONFUSABLE_IDENTS, sp, |lint| { + lint.build(&format!( + "identifier pair considered confusable between `{}` and `{}`", + existing_symbolstr, symbol_str + )) + .span_label( + *existing_span, + "this is where the previous identifier occurred", + ) + .emit(); + }); + }) + .or_insert((symbol_str, sp)); + } + } fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: ast::Ident) { use unicode_security::GeneralSecurityProfile; let name_str = ident.name.as_str(); diff --git a/src/librustc_metadata/rmeta/decoder.rs b/src/librustc_metadata/rmeta/decoder.rs index 6a4b35ed3d7fa..04f17230717d9 100644 --- a/src/librustc_metadata/rmeta/decoder.rs +++ b/src/librustc_metadata/rmeta/decoder.rs @@ -1123,11 +1123,13 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { match self.kind(id) { EntryKind::Const(qualif, _) | EntryKind::AssocConst( - AssocContainer::ImplDefault | AssocContainer::ImplFinal, + AssocContainer::ImplDefault + | AssocContainer::ImplFinal + | AssocContainer::TraitWithDefault, qualif, _, ) => qualif, - _ => bug!(), + _ => bug!("mir_const_qualif: unexpected kind"), } } diff --git a/src/librustc_middle/ty/diagnostics.rs b/src/librustc_middle/ty/diagnostics.rs index 790eb8f49aff3..613d66d59c55b 100644 --- a/src/librustc_middle/ty/diagnostics.rs +++ b/src/librustc_middle/ty/diagnostics.rs @@ -2,7 +2,12 @@ use crate::ty::sty::InferTy; use crate::ty::TyKind::*; -use crate::ty::TyS; +use crate::ty::{TyCtxt, TyS}; +use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate}; +use rustc_span::{BytePos, Span}; impl<'tcx> TyS<'tcx> { /// Similar to `TyS::is_primitive`, but also considers inferred numeric values to be primitive. @@ -67,3 +72,180 @@ impl<'tcx> TyS<'tcx> { } } } + +/// Suggest restricting a type param with a new bound. +pub fn suggest_constraining_type_param( + tcx: TyCtxt<'_>, + generics: &hir::Generics<'_>, + err: &mut DiagnosticBuilder<'_>, + param_name: &str, + constraint: &str, + def_id: Option, +) -> bool { + let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name); + + let param = if let Some(param) = param { + param + } else { + return false; + }; + + const MSG_RESTRICT_BOUND_FURTHER: &str = "consider further restricting this bound"; + let msg_restrict_type = format!("consider restricting type parameter `{}`", param_name); + let msg_restrict_type_further = + format!("consider further restricting type parameter `{}`", param_name); + + if def_id == tcx.lang_items().sized_trait() { + // Type parameters are already `Sized` by default. + err.span_label(param.span, &format!("this type parameter needs to be `{}`", constraint)); + return true; + } + let mut suggest_restrict = |span| { + err.span_suggestion_verbose( + span, + MSG_RESTRICT_BOUND_FURTHER, + format!(" + {}", constraint), + Applicability::MachineApplicable, + ); + }; + + if param_name.starts_with("impl ") { + // If there's an `impl Trait` used in argument position, suggest + // restricting it: + // + // fn foo(t: impl Foo) { ... } + // -------- + // | + // help: consider further restricting this bound with `+ Bar` + // + // Suggestion for tools in this case is: + // + // fn foo(t: impl Foo) { ... } + // -------- + // | + // replace with: `impl Foo + Bar` + + suggest_restrict(param.span.shrink_to_hi()); + return true; + } + + if generics.where_clause.predicates.is_empty() + // Given `trait Base: Super` where `T: Copy`, suggest restricting in the + // `where` clause instead of `trait Base: Super`. + && !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) + { + if let Some(bounds_span) = param.bounds_span() { + // If user has provided some bounds, suggest restricting them: + // + // fn foo(t: T) { ... } + // --- + // | + // help: consider further restricting this bound with `+ Bar` + // + // Suggestion for tools in this case is: + // + // fn foo(t: T) { ... } + // -- + // | + // replace with: `T: Bar +` + suggest_restrict(bounds_span.shrink_to_hi()); + } else { + // If user hasn't provided any bounds, suggest adding a new one: + // + // fn foo(t: T) { ... } + // - help: consider restricting this type parameter with `T: Foo` + err.span_suggestion_verbose( + param.span.shrink_to_hi(), + &msg_restrict_type, + format!(": {}", constraint), + Applicability::MachineApplicable, + ); + } + + true + } else { + // This part is a bit tricky, because using the `where` clause user can + // provide zero, one or many bounds for the same type parameter, so we + // have following cases to consider: + // + // 1) When the type parameter has been provided zero bounds + // + // Message: + // fn foo(x: X, y: Y) where Y: Foo { ... } + // - help: consider restricting this type parameter with `where X: Bar` + // + // Suggestion: + // fn foo(x: X, y: Y) where Y: Foo { ... } + // - insert: `, X: Bar` + // + // + // 2) When the type parameter has been provided one bound + // + // Message: + // fn foo(t: T) where T: Foo { ... } + // ^^^^^^ + // | + // help: consider further restricting this bound with `+ Bar` + // + // Suggestion: + // fn foo(t: T) where T: Foo { ... } + // ^^ + // | + // replace with: `T: Bar +` + // + // + // 3) When the type parameter has been provided many bounds + // + // Message: + // fn foo(t: T) where T: Foo, T: Bar {... } + // - help: consider further restricting this type parameter with `where T: Zar` + // + // Suggestion: + // fn foo(t: T) where T: Foo, T: Bar {... } + // - insert: `, T: Zar` + + let mut param_spans = Vec::new(); + + for predicate in generics.where_clause.predicates { + if let WherePredicate::BoundPredicate(WhereBoundPredicate { + span, bounded_ty, .. + }) = predicate + { + if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind { + if let Some(segment) = path.segments.first() { + if segment.ident.to_string() == param_name { + param_spans.push(span); + } + } + } + } + } + + let where_clause_span = generics.where_clause.span_for_predicates_or_empty_place(); + // Account for `fn foo(t: T) where T: Foo,` so we don't suggest two trailing commas. + let mut trailing_comma = false; + if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(where_clause_span) { + trailing_comma = snippet.ends_with(','); + } + let where_clause_span = if trailing_comma { + let hi = where_clause_span.hi(); + Span::new(hi - BytePos(1), hi, where_clause_span.ctxt()) + } else { + where_clause_span.shrink_to_hi() + }; + + match ¶m_spans[..] { + &[¶m_span] => suggest_restrict(param_span.shrink_to_hi()), + _ => { + err.span_suggestion_verbose( + where_clause_span, + &msg_restrict_type_further, + format!(", {}: {}", param_name, constraint), + Applicability::MachineApplicable, + ); + } + } + + true + } +} diff --git a/src/librustc_middle/ty/error.rs b/src/librustc_middle/ty/error.rs index 78a94b62d4722..af9f8707c49d8 100644 --- a/src/librustc_middle/ty/error.rs +++ b/src/librustc_middle/ty/error.rs @@ -1,3 +1,4 @@ +use crate::ty::diagnostics::suggest_constraining_type_param; use crate::ty::{self, BoundRegion, Region, Ty, TyCtxt}; use rustc_ast::ast; use rustc_errors::{pluralize, Applicability, DiagnosticBuilder}; @@ -398,8 +399,43 @@ impl<'tcx> TyCtxt<'tcx> { (ty::Projection(_), ty::Projection(_)) => { db.note("an associated type was expected, but a different one was found"); } - (ty::Param(_), ty::Projection(_)) | (ty::Projection(_), ty::Param(_)) => { - db.note("you might be missing a type parameter or trait bound"); + (ty::Param(p), ty::Projection(proj)) | (ty::Projection(proj), ty::Param(p)) => { + let generics = self.generics_of(body_owner_def_id); + let p_span = self.def_span(generics.type_param(p, self).def_id); + if !sp.contains(p_span) { + db.span_label(p_span, "this type parameter"); + } + let hir = self.hir(); + let mut note = true; + if let Some(generics) = hir + .as_local_hir_id(generics.type_param(p, self).def_id) + .and_then(|id| self.hir().find(self.hir().get_parent_node(id))) + .as_ref() + .and_then(|node| node.generics()) + { + // Synthesize the associated type restriction `Add`. + // FIXME: extract this logic for use in other diagnostics. + let trait_ref = proj.trait_ref(self); + let path = + self.def_path_str_with_substs(trait_ref.def_id, trait_ref.substs); + let item_name = self.item_name(proj.item_def_id); + let path = if path.ends_with('>') { + format!("{}, {} = {}>", &path[..path.len() - 1], item_name, p) + } else { + format!("{}<{} = {}>", path, item_name, p) + }; + note = !suggest_constraining_type_param( + self, + generics, + db, + &format!("{}", proj.self_ty()), + &path, + None, + ); + } + if note { + db.note("you might be missing a type parameter or trait bound"); + } } (ty::Param(p), _) | (_, ty::Param(p)) => { let generics = self.generics_of(body_owner_def_id); diff --git a/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs b/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs index 5bc9f6df889c7..14a094b9d5273 100644 --- a/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs +++ b/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs @@ -10,10 +10,9 @@ use rustc_middle::mir::{ FakeReadCause, Local, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, TerminatorKind, VarBindingForm, }; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, suggest_constraining_type_param, Ty}; use rustc_span::source_map::DesugaringKind; use rustc_span::Span; -use rustc_trait_selection::traits::error_reporting::suggest_constraining_type_param; use crate::dataflow::drop_flag_effects; use crate::dataflow::indexes::{MoveOutIndex, MovePathIndex}; diff --git a/src/librustc_parse/lexer/mod.rs b/src/librustc_parse/lexer/mod.rs index 96321ef2145e5..f676a34a1d12b 100644 --- a/src/librustc_parse/lexer/mod.rs +++ b/src/librustc_parse/lexer/mod.rs @@ -222,8 +222,9 @@ impl<'a> StringReader<'a> { ident_start = ident_start + BytePos(2); } let sym = nfc_normalize(self.str_from(ident_start)); + let span = self.mk_sp(start, self.pos); + self.sess.symbol_gallery.insert(sym, span); if is_raw_ident { - let span = self.mk_sp(start, self.pos); if !sym.can_be_raw() { self.err_span(span, &format!("`{}` cannot be a raw identifier", sym)); } diff --git a/src/librustc_session/parse.rs b/src/librustc_session/parse.rs index 387d35422c43e..69d3e99b7458e 100644 --- a/src/librustc_session/parse.rs +++ b/src/librustc_session/parse.rs @@ -60,6 +60,20 @@ impl GatedSpans { } } +#[derive(Default)] +pub struct SymbolGallery { + /// All symbols occurred and their first occurrance span. + pub symbols: Lock>, +} + +impl SymbolGallery { + /// Insert a symbol and its span into symbol gallery. + /// If the symbol has occurred before, ignore the new occurance. + pub fn insert(&self, symbol: Symbol, span: Span) { + self.symbols.lock().entry(symbol).or_insert(span); + } +} + /// Construct a diagnostic for a language feature error due to the given `span`. /// The `feature`'s `Symbol` is the one you used in `active.rs` and `rustc_span::symbols`. pub fn feature_err<'a>( @@ -118,6 +132,7 @@ pub struct ParseSess { pub ambiguous_block_expr_parse: Lock>, pub injected_crate_name: Once, pub gated_spans: GatedSpans, + pub symbol_gallery: SymbolGallery, /// The parser has reached `Eof` due to an unclosed brace. Used to silence unnecessary errors. pub reached_eof: Lock, } @@ -143,6 +158,7 @@ impl ParseSess { ambiguous_block_expr_parse: Lock::new(FxHashMap::default()), injected_crate_name: Once::new(), gated_spans: GatedSpans::default(), + symbol_gallery: SymbolGallery::default(), reached_eof: Lock::new(false), } } diff --git a/src/librustc_trait_selection/traits/error_reporting/mod.rs b/src/librustc_trait_selection/traits/error_reporting/mod.rs index fa2af24c94534..19ed6b50f92a6 100644 --- a/src/librustc_trait_selection/traits/error_reporting/mod.rs +++ b/src/librustc_trait_selection/traits/error_reporting/mod.rs @@ -15,17 +15,16 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorReported}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_hir::{Node, QPath, TyKind, WhereBoundPredicate, WherePredicate}; +use rustc_hir::Node; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::error::ExpectedFound; -use rustc_middle::ty::fast_reject; use rustc_middle::ty::fold::TypeFolder; -use rustc_middle::ty::SubtypePredicate; use rustc_middle::ty::{ - self, AdtKind, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness, + self, fast_reject, AdtKind, SubtypePredicate, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, + TypeFoldable, WithConstness, }; use rustc_session::DiagnosticMessageId; -use rustc_span::{BytePos, ExpnKind, Span, DUMMY_SP}; +use rustc_span::{ExpnKind, Span, DUMMY_SP}; use std::fmt; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; @@ -1700,180 +1699,3 @@ impl ArgKind { } } } - -/// Suggest restricting a type param with a new bound. -pub fn suggest_constraining_type_param( - tcx: TyCtxt<'_>, - generics: &hir::Generics<'_>, - err: &mut DiagnosticBuilder<'_>, - param_name: &str, - constraint: &str, - def_id: Option, -) -> bool { - let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name); - - let param = if let Some(param) = param { - param - } else { - return false; - }; - - const MSG_RESTRICT_BOUND_FURTHER: &str = "consider further restricting this bound"; - let msg_restrict_type = format!("consider restricting type parameter `{}`", param_name); - let msg_restrict_type_further = - format!("consider further restricting type parameter `{}`", param_name); - - if def_id == tcx.lang_items().sized_trait() { - // Type parameters are already `Sized` by default. - err.span_label(param.span, &format!("this type parameter needs to be `{}`", constraint)); - return true; - } - let mut suggest_restrict = |span| { - err.span_suggestion_verbose( - span, - MSG_RESTRICT_BOUND_FURTHER, - format!(" + {}", constraint), - Applicability::MachineApplicable, - ); - }; - - if param_name.starts_with("impl ") { - // If there's an `impl Trait` used in argument position, suggest - // restricting it: - // - // fn foo(t: impl Foo) { ... } - // -------- - // | - // help: consider further restricting this bound with `+ Bar` - // - // Suggestion for tools in this case is: - // - // fn foo(t: impl Foo) { ... } - // -------- - // | - // replace with: `impl Foo + Bar` - - suggest_restrict(param.span.shrink_to_hi()); - return true; - } - - if generics.where_clause.predicates.is_empty() - // Given `trait Base: Super` where `T: Copy`, suggest restricting in the - // `where` clause instead of `trait Base: Super`. - && !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) - { - if let Some(bounds_span) = param.bounds_span() { - // If user has provided some bounds, suggest restricting them: - // - // fn foo(t: T) { ... } - // --- - // | - // help: consider further restricting this bound with `+ Bar` - // - // Suggestion for tools in this case is: - // - // fn foo(t: T) { ... } - // -- - // | - // replace with: `T: Bar +` - suggest_restrict(bounds_span.shrink_to_hi()); - } else { - // If user hasn't provided any bounds, suggest adding a new one: - // - // fn foo(t: T) { ... } - // - help: consider restricting this type parameter with `T: Foo` - err.span_suggestion_verbose( - param.span.shrink_to_hi(), - &msg_restrict_type, - format!(": {}", constraint), - Applicability::MachineApplicable, - ); - } - - true - } else { - // This part is a bit tricky, because using the `where` clause user can - // provide zero, one or many bounds for the same type parameter, so we - // have following cases to consider: - // - // 1) When the type parameter has been provided zero bounds - // - // Message: - // fn foo(x: X, y: Y) where Y: Foo { ... } - // - help: consider restricting this type parameter with `where X: Bar` - // - // Suggestion: - // fn foo(x: X, y: Y) where Y: Foo { ... } - // - insert: `, X: Bar` - // - // - // 2) When the type parameter has been provided one bound - // - // Message: - // fn foo(t: T) where T: Foo { ... } - // ^^^^^^ - // | - // help: consider further restricting this bound with `+ Bar` - // - // Suggestion: - // fn foo(t: T) where T: Foo { ... } - // ^^ - // | - // replace with: `T: Bar +` - // - // - // 3) When the type parameter has been provided many bounds - // - // Message: - // fn foo(t: T) where T: Foo, T: Bar {... } - // - help: consider further restricting this type parameter with `where T: Zar` - // - // Suggestion: - // fn foo(t: T) where T: Foo, T: Bar {... } - // - insert: `, T: Zar` - - let mut param_spans = Vec::new(); - - for predicate in generics.where_clause.predicates { - if let WherePredicate::BoundPredicate(WhereBoundPredicate { - span, bounded_ty, .. - }) = predicate - { - if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind { - if let Some(segment) = path.segments.first() { - if segment.ident.to_string() == param_name { - param_spans.push(span); - } - } - } - } - } - - let where_clause_span = generics.where_clause.span_for_predicates_or_empty_place(); - // Account for `fn foo(t: T) where T: Foo,` so we don't suggest two trailing commas. - let mut trailing_comma = false; - if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(where_clause_span) { - trailing_comma = snippet.ends_with(','); - } - let where_clause_span = if trailing_comma { - let hi = where_clause_span.hi(); - Span::new(hi - BytePos(1), hi, where_clause_span.ctxt()) - } else { - where_clause_span.shrink_to_hi() - }; - - match ¶m_spans[..] { - &[¶m_span] => suggest_restrict(param_span.shrink_to_hi()), - _ => { - err.span_suggestion_verbose( - where_clause_span, - &msg_restrict_type_further, - format!(", {}: {}", param_name, constraint), - Applicability::MachineApplicable, - ); - } - } - - true - } -} diff --git a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs index 5ec2d68ab2a7d..f0103d9b2712d 100644 --- a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs +++ b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs @@ -3,7 +3,6 @@ use super::{ }; use crate::infer::InferCtxt; -use crate::traits::error_reporting::suggest_constraining_type_param; use rustc_errors::{error_code, struct_span_err, Applicability, DiagnosticBuilder, Style}; use rustc_hir as hir; @@ -13,7 +12,8 @@ use rustc_hir::intravisit::Visitor; use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node}; use rustc_middle::ty::TypeckTables; use rustc_middle::ty::{ - self, AdtKind, DefIdTree, Infer, InferTy, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness, + self, suggest_constraining_type_param, AdtKind, DefIdTree, Infer, InferTy, ToPredicate, Ty, + TyCtxt, TypeFoldable, WithConstness, }; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::{MultiSpan, Span, DUMMY_SP}; diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index cac9113fd5d30..04267ccba669f 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -10,7 +10,7 @@ use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, }; use rustc_middle::ty::TyKind::{Adt, Array, Char, FnDef, Never, Ref, Str, Tuple, Uint}; -use rustc_middle::ty::{self, Ty, TypeFoldable}; +use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt, TypeFoldable}; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; @@ -253,6 +253,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // error types are considered "builtin" if !lhs_ty.references_error() { let source_map = self.tcx.sess.source_map(); + match is_assign { IsAssign::Yes => { let mut err = struct_span_err!( @@ -317,12 +318,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // This has nothing here because it means we did string // concatenation (e.g., "Hello " += "World!"). This means // we don't want the note in the else clause to be emitted - } else if let ty::Param(_) = lhs_ty.kind { - // FIXME: point to span of param - err.note(&format!( - "`{}` might need a bound for `{}`", - lhs_ty, missing_trait - )); + } else if let ty::Param(p) = lhs_ty.kind { + suggest_constraining_param( + self.tcx, + self.body_id, + &mut err, + lhs_ty, + rhs_ty, + missing_trait, + p, + false, + ); } else if !suggested_deref { suggest_impl_missing(&mut err, lhs_ty, &missing_trait); } @@ -330,46 +336,56 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.emit(); } IsAssign::No => { - let (message, missing_trait) = match op.node { + let (message, missing_trait, use_output) = match op.node { hir::BinOpKind::Add => ( format!("cannot add `{}` to `{}`", rhs_ty, lhs_ty), Some("std::ops::Add"), + true, ), hir::BinOpKind::Sub => ( format!("cannot subtract `{}` from `{}`", rhs_ty, lhs_ty), Some("std::ops::Sub"), + true, ), hir::BinOpKind::Mul => ( format!("cannot multiply `{}` to `{}`", rhs_ty, lhs_ty), Some("std::ops::Mul"), + true, ), hir::BinOpKind::Div => ( format!("cannot divide `{}` by `{}`", lhs_ty, rhs_ty), Some("std::ops::Div"), + true, ), hir::BinOpKind::Rem => ( format!("cannot mod `{}` by `{}`", lhs_ty, rhs_ty), Some("std::ops::Rem"), + true, ), hir::BinOpKind::BitAnd => ( format!("no implementation for `{} & {}`", lhs_ty, rhs_ty), Some("std::ops::BitAnd"), + true, ), hir::BinOpKind::BitXor => ( format!("no implementation for `{} ^ {}`", lhs_ty, rhs_ty), Some("std::ops::BitXor"), + true, ), hir::BinOpKind::BitOr => ( format!("no implementation for `{} | {}`", lhs_ty, rhs_ty), Some("std::ops::BitOr"), + true, ), hir::BinOpKind::Shl => ( format!("no implementation for `{} << {}`", lhs_ty, rhs_ty), Some("std::ops::Shl"), + true, ), hir::BinOpKind::Shr => ( format!("no implementation for `{} >> {}`", lhs_ty, rhs_ty), Some("std::ops::Shr"), + true, ), hir::BinOpKind::Eq | hir::BinOpKind::Ne => ( format!( @@ -378,6 +394,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lhs_ty ), Some("std::cmp::PartialEq"), + false, ), hir::BinOpKind::Lt | hir::BinOpKind::Le @@ -389,6 +406,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lhs_ty ), Some("std::cmp::PartialOrd"), + false, ), _ => ( format!( @@ -397,6 +415,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lhs_ty ), None, + false, ), }; let mut err = struct_span_err!( @@ -459,12 +478,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // This has nothing here because it means we did string // concatenation (e.g., "Hello " + "World!"). This means // we don't want the note in the else clause to be emitted - } else if let ty::Param(_) = lhs_ty.kind { - // FIXME: point to span of param - err.note(&format!( - "`{}` might need a bound for `{}`", - lhs_ty, missing_trait - )); + } else if let ty::Param(p) = lhs_ty.kind { + suggest_constraining_param( + self.tcx, + self.body_id, + &mut err, + lhs_ty, + rhs_ty, + missing_trait, + p, + use_output, + ); } else if !suggested_deref && !involves_fn { suggest_impl_missing(&mut err, lhs_ty, &missing_trait); } @@ -911,3 +935,42 @@ fn suggest_impl_missing(err: &mut DiagnosticBuilder<'_>, ty: Ty<'_>, missing_tra } } } + +fn suggest_constraining_param( + tcx: TyCtxt<'_>, + body_id: hir::HirId, + mut err: &mut DiagnosticBuilder<'_>, + lhs_ty: Ty<'_>, + rhs_ty: Ty<'_>, + missing_trait: &str, + p: ty::ParamTy, + set_output: bool, +) { + let hir = tcx.hir(); + let msg = &format!("`{}` might need a bound for `{}`", lhs_ty, missing_trait); + // Try to find the def-id and details for the parameter p. We have only the index, + // so we have to find the enclosing function's def-id, then look through its declared + // generic parameters to get the declaration. + let def_id = hir.body_owner_def_id(hir::BodyId { hir_id: body_id }); + let generics = tcx.generics_of(def_id); + let param_def_id = generics.type_param(&p, tcx).def_id; + if let Some(generics) = hir + .as_local_hir_id(param_def_id) + .and_then(|id| hir.find(hir.get_parent_item(id))) + .as_ref() + .and_then(|node| node.generics()) + { + let output = if set_output { format!("", rhs_ty) } else { String::new() }; + suggest_constraining_type_param( + tcx, + generics, + &mut err, + &format!("{}", lhs_ty), + &format!("{}{}", missing_trait, output), + None, + ); + } else { + let span = tcx.def_span(param_def_id); + err.span_label(span, msg); + } +} diff --git a/src/test/ui/consts/const_in_pattern/auxiliary/consts.rs b/src/test/ui/consts/const_in_pattern/auxiliary/consts.rs index 303c2f12bbce9..b438bcd9fb5eb 100644 --- a/src/test/ui/consts/const_in_pattern/auxiliary/consts.rs +++ b/src/test/ui/consts/const_in_pattern/auxiliary/consts.rs @@ -9,3 +9,8 @@ impl PartialEq for CustomEq { pub const NONE: Option = None; pub const SOME: Option = Some(CustomEq); + +pub trait AssocConst { + const NONE: Option = None; + const SOME: Option = Some(CustomEq); +} diff --git a/src/test/ui/consts/const_in_pattern/cross-crate-fail.rs b/src/test/ui/consts/const_in_pattern/cross-crate-fail.rs index c9e6050fdc508..05c53e5edccc5 100644 --- a/src/test/ui/consts/const_in_pattern/cross-crate-fail.rs +++ b/src/test/ui/consts/const_in_pattern/cross-crate-fail.rs @@ -4,7 +4,11 @@ extern crate consts; +struct Defaulted; +impl consts::AssocConst for Defaulted {} + fn main() { + let _ = Defaulted; match None { consts::SOME => panic!(), //~^ must be annotated with `#[derive(PartialEq, Eq)]` @@ -12,4 +16,12 @@ fn main() { _ => {} } + + match None { + ::SOME => panic!(), + //~^ must be annotated with `#[derive(PartialEq, Eq)]` + //~| must be annotated with `#[derive(PartialEq, Eq)]` + + _ => {} + } } diff --git a/src/test/ui/consts/const_in_pattern/cross-crate-fail.stderr b/src/test/ui/consts/const_in_pattern/cross-crate-fail.stderr index c97298f66e677..5d147e32f5a86 100644 --- a/src/test/ui/consts/const_in_pattern/cross-crate-fail.stderr +++ b/src/test/ui/consts/const_in_pattern/cross-crate-fail.stderr @@ -1,14 +1,26 @@ error: to use a constant of type `consts::CustomEq` in a pattern, `consts::CustomEq` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/cross-crate-fail.rs:9:9 + --> $DIR/cross-crate-fail.rs:13:9 | LL | consts::SOME => panic!(), | ^^^^^^^^^^^^ error: to use a constant of type `consts::CustomEq` in a pattern, `consts::CustomEq` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/cross-crate-fail.rs:9:9 + --> $DIR/cross-crate-fail.rs:21:9 + | +LL | ::SOME => panic!(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: to use a constant of type `consts::CustomEq` in a pattern, `consts::CustomEq` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/cross-crate-fail.rs:13:9 | LL | consts::SOME => panic!(), | ^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: to use a constant of type `consts::CustomEq` in a pattern, `consts::CustomEq` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/cross-crate-fail.rs:21:9 + | +LL | ::SOME => panic!(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors diff --git a/src/test/ui/consts/const_in_pattern/cross-crate-pass.rs b/src/test/ui/consts/const_in_pattern/cross-crate-pass.rs index ccf655c11cf8a..1d8ecf8ae6640 100644 --- a/src/test/ui/consts/const_in_pattern/cross-crate-pass.rs +++ b/src/test/ui/consts/const_in_pattern/cross-crate-pass.rs @@ -6,9 +6,18 @@ extern crate consts; use consts::CustomEq; +struct Defaulted; +impl consts::AssocConst for Defaulted {} + fn main() { + let _ = Defaulted; match Some(CustomEq) { consts::NONE => panic!(), _ => {} } + + match Some(CustomEq) { + ::NONE => panic!(), + _ => {} + } } diff --git a/src/test/ui/generic-associated-types/construct_with_other_type.stderr b/src/test/ui/generic-associated-types/construct_with_other_type.stderr index bad746f7ef121..b9468b3330b44 100644 --- a/src/test/ui/generic-associated-types/construct_with_other_type.stderr +++ b/src/test/ui/generic-associated-types/construct_with_other_type.stderr @@ -2,11 +2,16 @@ error[E0271]: type mismatch resolving `for<'a> <::Baa<'a> as std::ops: --> $DIR/construct_with_other_type.rs:19:9 | LL | impl Baz for T where T: Foo { - | ^^^ expected type parameter `T`, found associated type + | - ^^^ expected type parameter `T`, found associated type + | | + | this type parameter | = note: expected associated type `::Bar<'_, 'static>` found associated type `<::Quux<'_> as Foo>::Bar<'_, 'static>` - = note: you might be missing a type parameter or trait bound +help: consider further restricting this bound + | +LL | impl Baz for T where T: Foo + Baz { + | ^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/generic-associated-types/missing-bounds.fixed b/src/test/ui/generic-associated-types/missing-bounds.fixed new file mode 100644 index 0000000000000..364d2388741b0 --- /dev/null +++ b/src/test/ui/generic-associated-types/missing-bounds.fixed @@ -0,0 +1,46 @@ +// run-rustfix + +use std::ops::Add; + +struct A(B); + +impl Add for A where B: Add + std::ops::Add { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + A(self.0 + rhs.0) //~ ERROR mismatched types + } +} + +struct C(B); + +impl> Add for C { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Self(self.0 + rhs.0) //~ ERROR mismatched types + } +} + +struct D(B); + +impl> Add for D { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Self(self.0 + rhs.0) //~ ERROR cannot add `B` to `B` + } +} + +struct E(B); + +impl Add for E where B: Add, B: std::ops::Add { + //~^ ERROR equality constraints are not yet supported in `where` clauses + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Self(self.0 + rhs.0) //~ ERROR mismatched types + } +} + +fn main() {} diff --git a/src/test/ui/generic-associated-types/missing-bounds.rs b/src/test/ui/generic-associated-types/missing-bounds.rs new file mode 100644 index 0000000000000..ffafff5e9f586 --- /dev/null +++ b/src/test/ui/generic-associated-types/missing-bounds.rs @@ -0,0 +1,46 @@ +// run-rustfix + +use std::ops::Add; + +struct A(B); + +impl Add for A where B: Add { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + A(self.0 + rhs.0) //~ ERROR mismatched types + } +} + +struct C(B); + +impl Add for C { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Self(self.0 + rhs.0) //~ ERROR mismatched types + } +} + +struct D(B); + +impl Add for D { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Self(self.0 + rhs.0) //~ ERROR cannot add `B` to `B` + } +} + +struct E(B); + +impl Add for E where ::Output = B { + //~^ ERROR equality constraints are not yet supported in `where` clauses + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Self(self.0 + rhs.0) //~ ERROR mismatched types + } +} + +fn main() {} diff --git a/src/test/ui/generic-associated-types/missing-bounds.stderr b/src/test/ui/generic-associated-types/missing-bounds.stderr new file mode 100644 index 0000000000000..50536fdaca96e --- /dev/null +++ b/src/test/ui/generic-associated-types/missing-bounds.stderr @@ -0,0 +1,77 @@ +error: equality constraints are not yet supported in `where` clauses + --> $DIR/missing-bounds.rs:37:33 + | +LL | impl Add for E where ::Output = B { + | ^^^^^^^^^^^^^^^^^^^^^^ not supported + | + = note: see issue #20041 for more information +help: if `Output` is an associated type you're trying to set, use the associated type binding syntax + | +LL | impl Add for E where B: Add { + | ^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/missing-bounds.rs:11:11 + | +LL | impl Add for A where B: Add { + | - this type parameter +... +LL | A(self.0 + rhs.0) + | ^^^^^^^^^^^^^^ expected type parameter `B`, found associated type + | + = note: expected type parameter `B` + found associated type `::Output` +help: consider further restricting this bound + | +LL | impl Add for A where B: Add + std::ops::Add { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/missing-bounds.rs:21:14 + | +LL | impl Add for C { + | - this type parameter +... +LL | Self(self.0 + rhs.0) + | ^^^^^^^^^^^^^^ expected type parameter `B`, found associated type + | + = note: expected type parameter `B` + found associated type `::Output` +help: consider further restricting this bound + | +LL | impl> Add for C { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0369]: cannot add `B` to `B` + --> $DIR/missing-bounds.rs:31:21 + | +LL | Self(self.0 + rhs.0) + | ------ ^ ----- B + | | + | B + | +help: consider restricting type parameter `B` + | +LL | impl> Add for D { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/missing-bounds.rs:42:14 + | +LL | impl Add for E where ::Output = B { + | - this type parameter +... +LL | Self(self.0 + rhs.0) + | ^^^^^^^^^^^^^^ expected type parameter `B`, found associated type + | + = note: expected type parameter `B` + found associated type `::Output` +help: consider further restricting type parameter `B` + | +LL | impl Add for E where ::Output = B, B: std::ops::Add { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0308, E0369. +For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/issues/issue-20005.stderr b/src/test/ui/issues/issue-20005.stderr index 19ccf7076199a..f53489a99f3b4 100644 --- a/src/test/ui/issues/issue-20005.stderr +++ b/src/test/ui/issues/issue-20005.stderr @@ -5,12 +5,18 @@ LL | trait From { | --- required by this bound in `From` ... LL | ) -> >::Result where Dst: From { - | ^^^^^^^^^^- help: consider further restricting `Self`: `, Self: std::marker::Sized` - | | - | doesn't have a size known at compile-time + | ^^^^^^^^^^ doesn't have a size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `Self` = note: to learn more, visit +help: consider further restricting `Self` + | +LL | ) -> >::Result where Dst: From, Self: std::marker::Sized { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider relaxing the implicit `Sized` restriction + | +LL | trait From { + | ^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-24204.stderr b/src/test/ui/issues/issue-24204.stderr index d69efc860059b..d5cbcf786bf1a 100644 --- a/src/test/ui/issues/issue-24204.stderr +++ b/src/test/ui/issues/issue-24204.stderr @@ -7,7 +7,9 @@ LL | type A: MultiDispatch; | -------- required by this bound in `Trait` ... LL | fn test>(b: i32) -> T where T::A: MultiDispatch { T::new(b) } - | ^^^^^^^^^^^^ expected type parameter `T`, found associated type + | - ^^^^^^^^^^^^ expected type parameter `T`, found associated type + | | + | this type parameter | = note: expected type parameter `T` found associated type `<::A as MultiDispatch>::O` diff --git a/src/test/ui/issues/issue-6738.stderr b/src/test/ui/issues/issue-6738.stderr index 82b670bd03bc5..a428ff7e91fad 100644 --- a/src/test/ui/issues/issue-6738.stderr +++ b/src/test/ui/issues/issue-6738.stderr @@ -6,7 +6,10 @@ LL | self.x += v.x; | | | cannot use `+=` on type `T` | - = note: `T` might need a bound for `std::ops::AddAssign` +help: consider restricting type parameter `T` + | +LL | impl Foo { + | ^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.rs b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.rs new file mode 100644 index 0000000000000..12093837d2630 --- /dev/null +++ b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.rs @@ -0,0 +1,9 @@ +#![feature(non_ascii_idents)] +#![deny(confusable_idents)] +#![allow(uncommon_codepoints, non_upper_case_globals)] + +const s: usize = 42; //~ ERROR identifier pair considered confusable + +fn main() { + let s = "rust"; +} diff --git a/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.stderr b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.stderr new file mode 100644 index 0000000000000..40ee18acb3cd4 --- /dev/null +++ b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.stderr @@ -0,0 +1,17 @@ +error: identifier pair considered confusable between `s` and `s` + --> $DIR/lint-confusable-idents.rs:5:7 + | +LL | const s: usize = 42; + | ^^ +... +LL | let s = "rust"; + | - this is where the previous identifier occurred + | +note: the lint level is defined here + --> $DIR/lint-confusable-idents.rs:2:9 + | +LL | #![deny(confusable_idents)] + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/type/type-check/missing_trait_impl.stderr b/src/test/ui/type/type-check/missing_trait_impl.stderr index 7186d6a542dc9..30df1261cefa1 100644 --- a/src/test/ui/type/type-check/missing_trait_impl.stderr +++ b/src/test/ui/type/type-check/missing_trait_impl.stderr @@ -6,7 +6,10 @@ LL | let z = x + y; | | | T | - = note: `T` might need a bound for `std::ops::Add` +help: consider restricting type parameter `T` + | +LL | fn foo>(x: T, y: T) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0368]: binary assignment operation `+=` cannot be applied to type `T` --> $DIR/missing_trait_impl.rs:9:5 @@ -16,7 +19,10 @@ LL | x += x; | | | cannot use `+=` on type `T` | - = note: `T` might need a bound for `std::ops::AddAssign` +help: consider restricting type parameter `T` + | +LL | fn bar(x: T) { + | ^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors