From 1b464c73b7f5223d85eb2380af7f448c4be6baf4 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 3 May 2022 11:52:53 -0700 Subject: [PATCH 1/8] Check attributes on pattern fields. Attributes on pattern struct fields were not being checked for validity. This adds the fields as HIR nodes so that the `CheckAttrVisitor` can visit those nodes to check their attributes. --- compiler/rustc_ast_lowering/src/index.rs | 5 ++++ compiler/rustc_ast_lowering/src/pat.rs | 17 +++++++---- compiler/rustc_hir/src/hir.rs | 2 ++ compiler/rustc_hir/src/target.rs | 2 ++ compiler/rustc_hir_pretty/src/lib.rs | 30 ++++++++++--------- compiler/rustc_middle/src/hir/map/mod.rs | 3 ++ compiler/rustc_passes/src/check_attr.rs | 12 +++++++- .../drop_ranges/cfg_build.rs | 1 + .../lint/unused/unused_attributes-must_use.rs | 6 ++++ .../unused/unused_attributes-must_use.stderr | 8 ++++- 10 files changed, 64 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index e08c1d063c105..ecc26faf20da6 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -193,6 +193,11 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { fn visit_pat(&mut self, pat: &'hir Pat<'hir>) { self.insert(pat.span, pat.hir_id, Node::Pat(pat)); + if let PatKind::Struct(_, fields, _) = pat.kind { + for field in fields { + self.insert(field.span, field.hir_id, Node::PatField(field)); + } + } self.with_parent(pat.hir_id, |this| { intravisit::walk_pat(this, pat); diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index bd2e76e5528da..51f67e505f4ee 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -64,12 +64,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ImplTraitContext::Disallowed(ImplTraitPosition::Path), ); - let fs = self.arena.alloc_from_iter(fields.iter().map(|f| hir::PatField { - hir_id: self.next_id(), - ident: self.lower_ident(f.ident), - pat: self.lower_pat(&f.pat), - is_shorthand: f.is_shorthand, - span: self.lower_span(f.span), + let fs = self.arena.alloc_from_iter(fields.iter().map(|f| { + let hir_id = self.lower_node_id(f.id); + self.lower_attrs(hir_id, &f.attrs); + + hir::PatField { + hir_id, + ident: self.lower_ident(f.ident), + pat: self.lower_pat(&f.pat), + is_shorthand: f.is_shorthand, + span: self.lower_span(f.span), + } })); break hir::PatKind::Struct(qpath, fs, etc); } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 7a87a3e488260..9885d33d44420 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -3338,6 +3338,7 @@ pub enum Node<'hir> { TypeBinding(&'hir TypeBinding<'hir>), TraitRef(&'hir TraitRef<'hir>), Pat(&'hir Pat<'hir>), + PatField(&'hir PatField<'hir>), Arm(&'hir Arm<'hir>), Block(&'hir Block<'hir>), Local(&'hir Local<'hir>), @@ -3388,6 +3389,7 @@ impl<'hir> Node<'hir> { | Node::Block(..) | Node::Ctor(..) | Node::Pat(..) + | Node::PatField(..) | Node::Arm(..) | Node::Local(..) | Node::Crate(..) diff --git a/compiler/rustc_hir/src/target.rs b/compiler/rustc_hir/src/target.rs index 6236dea10c88f..1b05c82eade87 100644 --- a/compiler/rustc_hir/src/target.rs +++ b/compiler/rustc_hir/src/target.rs @@ -56,6 +56,7 @@ pub enum Target { GenericParam(GenericParamKind), MacroDef, Param, + PatField, } impl Display for Target { @@ -183,6 +184,7 @@ impl Target { }, Target::MacroDef => "macro def", Target::Param => "function param", + Target::PatField => "pattern field", } } } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index e0179bd3ed1e8..641175d452973 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -89,6 +89,7 @@ impl<'a> State<'a> { Node::TypeBinding(a) => self.print_type_binding(a), Node::TraitRef(a) => self.print_trait_ref(a), Node::Pat(a) => self.print_pat(a), + Node::PatField(a) => self.print_patfield(&a), Node::Arm(a) => self.print_arm(a), Node::Infer(_) => self.word("_"), Node::Block(a) => { @@ -1799,20 +1800,7 @@ impl<'a> State<'a> { if !empty { self.space(); } - self.commasep_cmnt( - Consistent, - fields, - |s, f| { - s.cbox(INDENT_UNIT); - if !f.is_shorthand { - s.print_ident(f.ident); - s.word_nbsp(":"); - } - s.print_pat(f.pat); - s.end() - }, - |f| f.pat.span, - ); + self.commasep_cmnt(Consistent, &fields, |s, f| s.print_patfield(f), |f| f.pat.span); if etc { if !fields.is_empty() { self.word_space(","); @@ -1907,6 +1895,20 @@ impl<'a> State<'a> { self.ann.post(self, AnnNode::Pat(pat)) } + pub fn print_patfield(&mut self, field: &hir::PatField<'_>) { + if self.attrs(field.hir_id).is_empty() { + self.space(); + } + self.cbox(INDENT_UNIT); + self.print_outer_attributes(&self.attrs(field.hir_id)); + if !field.is_shorthand { + self.print_ident(field.ident); + self.word_nbsp(":"); + } + self.print_pat(field.pat); + self.end(); + } + pub fn print_param(&mut self, arg: &hir::Param<'_>) { self.print_outer_attributes(self.attrs(arg.hir_id)); self.print_pat(arg.pat); diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 47b04c33ec1cd..06fdef4142ee1 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -297,6 +297,7 @@ impl<'hir> Map<'hir> { | Node::Infer(_) | Node::TraitRef(_) | Node::Pat(_) + | Node::PatField(_) | Node::Local(_) | Node::Param(_) | Node::Arm(_) @@ -1030,6 +1031,7 @@ impl<'hir> Map<'hir> { Node::TypeBinding(tb) => tb.span, Node::TraitRef(tr) => tr.path.span, Node::Pat(pat) => pat.span, + Node::PatField(field) => field.span, Node::Arm(arm) => arm.span, Node::Block(block) => block.span, Node::Ctor(..) => self.span_with_body(self.get_parent_node(hir_id)), @@ -1247,6 +1249,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String { Some(Node::TypeBinding(_)) => node_str("type binding"), Some(Node::TraitRef(_)) => node_str("trait ref"), Some(Node::Pat(_)) => node_str("pat"), + Some(Node::PatField(_)) => node_str("pattern field"), Some(Node::Param(_)) => node_str("param"), Some(Node::Arm(_)) => node_str("arm"), Some(Node::Block(_)) => node_str("block"), diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index f75fffb6871f7..42f5806c1f7c0 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -652,7 +652,8 @@ impl CheckAttrVisitor<'_> { | Target::ForeignStatic | Target::ForeignTy | Target::GenericParam(..) - | Target::MacroDef => None, + | Target::MacroDef + | Target::PatField => None, } { tcx.sess.emit_err(errors::DocAliasBadLocation { span, attr_str, location }); return false; @@ -2076,6 +2077,15 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { intravisit::walk_param(self, param); } + + fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) { + if let hir::PatKind::Struct(_, fields, _) = p.kind { + for field in fields { + self.check_attributes(field.hir_id, field.span, Target::PatField, None); + } + } + intravisit::walk_pat(self, p); + } } fn is_c_like_enum(item: &Item<'_>) -> bool { diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs index a2c23db162b03..f1f4b05b33bcc 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs +++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs @@ -256,6 +256,7 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> { | hir::Node::TypeBinding(..) | hir::Node::TraitRef(..) | hir::Node::Pat(..) + | hir::Node::PatField(..) | hir::Node::Arm(..) | hir::Node::Local(..) | hir::Node::Ctor(..) diff --git a/src/test/ui/lint/unused/unused_attributes-must_use.rs b/src/test/ui/lint/unused/unused_attributes-must_use.rs index 1c4abb9491e00..87f498c0aeeae 100644 --- a/src/test/ui/lint/unused/unused_attributes-must_use.rs +++ b/src/test/ui/lint/unused/unused_attributes-must_use.rs @@ -122,4 +122,10 @@ fn main() { Some(res) => res, None => 0, }; + + struct PatternField { + foo: i32, + } + let s = PatternField { foo: 123 }; + let PatternField { #[must_use] foo } = s; //~ ERROR `#[must_use]` has no effect } diff --git a/src/test/ui/lint/unused/unused_attributes-must_use.stderr b/src/test/ui/lint/unused/unused_attributes-must_use.stderr index 317d81c591d55..3d2672687f896 100644 --- a/src/test/ui/lint/unused/unused_attributes-must_use.stderr +++ b/src/test/ui/lint/unused/unused_attributes-must_use.stderr @@ -105,6 +105,12 @@ error: `#[must_use]` has no effect when applied to an match arm LL | #[must_use] | ^^^^^^^^^^^ +error: `#[must_use]` has no effect when applied to a pattern field + --> $DIR/unused_attributes-must_use.rs:130:24 + | +LL | let PatternField { #[must_use] foo } = s; + | ^^^^^^^^^^^ + error: `#[must_use]` has no effect when applied to an associated const --> $DIR/unused_attributes-must_use.rs:68:5 | @@ -171,5 +177,5 @@ error: unused return value of `Use::get_four` that must be used LL | ().get_four(); | ^^^^^^^^^^^^^^ -error: aborting due to 26 previous errors +error: aborting due to 27 previous errors From b651c1cebe5f365201d568507ddb49ca343f2e7d Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 3 May 2022 13:23:03 -0700 Subject: [PATCH 2/8] Check attributes on struct expression fields. Attributes on struct expression fields were not being checked for validity. This adds the fields as HIR nodes so that `CheckAttrVisitor` can visit those nodes to check their attributes. --- compiler/rustc_ast_lowering/src/expr.rs | 4 ++- compiler/rustc_ast_lowering/src/index.rs | 5 ++++ compiler/rustc_hir/src/hir.rs | 2 ++ compiler/rustc_hir/src/target.rs | 2 ++ compiler/rustc_hir_pretty/src/lib.rs | 30 ++++++++++--------- compiler/rustc_middle/src/hir/map/mod.rs | 3 ++ compiler/rustc_passes/src/check_attr.rs | 8 ++++- .../drop_ranges/cfg_build.rs | 1 + .../lint/unused/unused_attributes-must_use.rs | 2 +- .../unused/unused_attributes-must_use.stderr | 8 ++++- 10 files changed, 47 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 92e6bc6013dc2..32dbd2ff47d6b 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1406,8 +1406,10 @@ impl<'hir> LoweringContext<'_, 'hir> { } fn lower_expr_field(&mut self, f: &ExprField) -> hir::ExprField<'hir> { + let hir_id = self.lower_node_id(f.id); + self.lower_attrs(hir_id, &f.attrs); hir::ExprField { - hir_id: self.next_id(), + hir_id, ident: self.lower_ident(f.ident), expr: self.lower_expr(&f.expr), span: self.lower_span(f.span), diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index ecc26faf20da6..ea35cf3de0455 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -224,6 +224,11 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { fn visit_expr(&mut self, expr: &'hir Expr<'hir>) { self.insert(expr.span, expr.hir_id, Node::Expr(expr)); + if let ExprKind::Struct(_, fields, _) = expr.kind { + for field in fields { + self.insert(field.span, field.hir_id, Node::ExprField(field)); + } + } self.with_parent(expr.hir_id, |this| { intravisit::walk_expr(this, expr); diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 9885d33d44420..2610d0b92d8f3 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -3332,6 +3332,7 @@ pub enum Node<'hir> { Field(&'hir FieldDef<'hir>), AnonConst(&'hir AnonConst), Expr(&'hir Expr<'hir>), + ExprField(&'hir ExprField<'hir>), Stmt(&'hir Stmt<'hir>), PathSegment(&'hir PathSegment<'hir>), Ty(&'hir Ty<'hir>), @@ -3390,6 +3391,7 @@ impl<'hir> Node<'hir> { | Node::Ctor(..) | Node::Pat(..) | Node::PatField(..) + | Node::ExprField(..) | Node::Arm(..) | Node::Local(..) | Node::Crate(..) diff --git a/compiler/rustc_hir/src/target.rs b/compiler/rustc_hir/src/target.rs index 1b05c82eade87..78bfd7191dba9 100644 --- a/compiler/rustc_hir/src/target.rs +++ b/compiler/rustc_hir/src/target.rs @@ -57,6 +57,7 @@ pub enum Target { MacroDef, Param, PatField, + ExprField, } impl Display for Target { @@ -185,6 +186,7 @@ impl Target { Target::MacroDef => "macro def", Target::Param => "function param", Target::PatField => "pattern field", + Target::ExprField => "struct field", } } } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 641175d452973..fde073296f999 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -83,6 +83,7 @@ impl<'a> State<'a> { Node::Variant(a) => self.print_variant(a), Node::AnonConst(a) => self.print_anon_const(a), Node::Expr(a) => self.print_expr(a), + Node::ExprField(a) => self.print_expr_field(&a), Node::Stmt(a) => self.print_stmt(a), Node::PathSegment(a) => self.print_path_segment(a), Node::Ty(a) => self.print_type(a), @@ -1124,20 +1125,7 @@ impl<'a> State<'a> { ) { self.print_qpath(qpath, true); self.word("{"); - self.commasep_cmnt( - Consistent, - fields, - |s, field| { - s.ibox(INDENT_UNIT); - if !field.is_shorthand { - s.print_ident(field.ident); - s.word_space(":"); - } - s.print_expr(field.expr); - s.end() - }, - |f| f.span, - ); + self.commasep_cmnt(Consistent, fields, |s, field| s.print_expr_field(field), |f| f.span); if let Some(expr) = wth { self.ibox(INDENT_UNIT); if !fields.is_empty() { @@ -1154,6 +1142,20 @@ impl<'a> State<'a> { self.word("}"); } + fn print_expr_field(&mut self, field: &hir::ExprField<'_>) { + if self.attrs(field.hir_id).is_empty() { + self.space(); + } + self.cbox(INDENT_UNIT); + self.print_outer_attributes(&self.attrs(field.hir_id)); + if !field.is_shorthand { + self.print_ident(field.ident); + self.word_space(":"); + } + self.print_expr(&field.expr); + self.end() + } + fn print_expr_tup(&mut self, exprs: &[hir::Expr<'_>]) { self.popen(); self.commasep_exprs(Inconsistent, exprs); diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 06fdef4142ee1..79e6804a28992 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -298,6 +298,7 @@ impl<'hir> Map<'hir> { | Node::TraitRef(_) | Node::Pat(_) | Node::PatField(_) + | Node::ExprField(_) | Node::Local(_) | Node::Param(_) | Node::Arm(_) @@ -1021,6 +1022,7 @@ impl<'hir> Map<'hir> { Node::Field(field) => field.span, Node::AnonConst(constant) => self.body(constant.body).value.span, Node::Expr(expr) => expr.span, + Node::ExprField(field) => field.span, Node::Stmt(stmt) => stmt.span, Node::PathSegment(seg) => { let ident_span = seg.ident.span; @@ -1243,6 +1245,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String { } Some(Node::AnonConst(_)) => node_str("const"), Some(Node::Expr(_)) => node_str("expr"), + Some(Node::ExprField(_)) => node_str("expr field"), Some(Node::Stmt(_)) => node_str("stmt"), Some(Node::PathSegment(_)) => node_str("path segment"), Some(Node::Ty(_)) => node_str("type"), diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 42f5806c1f7c0..0eb6f401b3817 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -653,7 +653,8 @@ impl CheckAttrVisitor<'_> { | Target::ForeignTy | Target::GenericParam(..) | Target::MacroDef - | Target::PatField => None, + | Target::PatField + | Target::ExprField => None, } { tcx.sess.emit_err(errors::DocAliasBadLocation { span, attr_str, location }); return false; @@ -2064,6 +2065,11 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { }; self.check_attributes(expr.hir_id, expr.span, target, None); + if let hir::ExprKind::Struct(_, fields, _) = expr.kind { + for field in fields { + self.check_attributes(field.hir_id, field.span, Target::PatField, None); + } + } intravisit::walk_expr(self, expr) } diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs index f1f4b05b33bcc..3e96b3ffb0940 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs +++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs @@ -257,6 +257,7 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> { | hir::Node::TraitRef(..) | hir::Node::Pat(..) | hir::Node::PatField(..) + | hir::Node::ExprField(..) | hir::Node::Arm(..) | hir::Node::Local(..) | hir::Node::Ctor(..) diff --git a/src/test/ui/lint/unused/unused_attributes-must_use.rs b/src/test/ui/lint/unused/unused_attributes-must_use.rs index 87f498c0aeeae..51f868706b69b 100644 --- a/src/test/ui/lint/unused/unused_attributes-must_use.rs +++ b/src/test/ui/lint/unused/unused_attributes-must_use.rs @@ -126,6 +126,6 @@ fn main() { struct PatternField { foo: i32, } - let s = PatternField { foo: 123 }; + let s = PatternField { #[must_use] foo: 123 }; //~ ERROR `#[must_use]` has no effect let PatternField { #[must_use] foo } = s; //~ ERROR `#[must_use]` has no effect } diff --git a/src/test/ui/lint/unused/unused_attributes-must_use.stderr b/src/test/ui/lint/unused/unused_attributes-must_use.stderr index 3d2672687f896..69cd5302c5c34 100644 --- a/src/test/ui/lint/unused/unused_attributes-must_use.stderr +++ b/src/test/ui/lint/unused/unused_attributes-must_use.stderr @@ -105,6 +105,12 @@ error: `#[must_use]` has no effect when applied to an match arm LL | #[must_use] | ^^^^^^^^^^^ +error: `#[must_use]` has no effect when applied to a pattern field + --> $DIR/unused_attributes-must_use.rs:129:28 + | +LL | let s = PatternField { #[must_use] foo: 123 }; + | ^^^^^^^^^^^ + error: `#[must_use]` has no effect when applied to a pattern field --> $DIR/unused_attributes-must_use.rs:130:24 | @@ -177,5 +183,5 @@ error: unused return value of `Use::get_four` that must be used LL | ().get_four(); | ^^^^^^^^^^^^^^ -error: aborting due to 27 previous errors +error: aborting due to 28 previous errors From 6c7cb2bb770d43d1078bfbf3d14e7b4197b23900 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 5 May 2022 13:44:12 -0700 Subject: [PATCH 3/8] Honor lint level attributes in more places. This extends the LintLevelBuilder to handle lint level attributes on struct expression fields and pattern fields. This also updates the early lints to honor lint levels on generic parameters. --- compiler/rustc_lint/src/early.rs | 7 +- compiler/rustc_lint/src/levels.rs | 45 +- .../ui/lint/lint-attr-everywhere-early.rs | 169 +++++++ .../ui/lint/lint-attr-everywhere-early.stderr | 472 ++++++++++++++++++ src/test/ui/lint/lint-attr-everywhere-late.rs | 197 ++++++++ .../ui/lint/lint-attr-everywhere-late.stderr | 428 ++++++++++++++++ 6 files changed, 1312 insertions(+), 6 deletions(-) create mode 100644 src/test/ui/lint/lint-attr-everywhere-early.rs create mode 100644 src/test/ui/lint/lint-attr-everywhere-early.stderr create mode 100644 src/test/ui/lint/lint-attr-everywhere-late.rs create mode 100644 src/test/ui/lint/lint-attr-everywhere-late.stderr diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index cbf1a67755501..d94c5d034a653 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -219,9 +219,10 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> } fn visit_generic_param(&mut self, param: &'a ast::GenericParam) { - run_early_pass!(self, check_generic_param, param); - self.check_id(param.id); - ast_visit::walk_generic_param(self, param); + self.with_lint_attrs(param.id, ¶m.attrs, |cx| { + run_early_pass!(cx, check_generic_param, param); + ast_visit::walk_generic_param(cx, param); + }); } fn visit_generics(&mut self, g: &'a ast::Generics) { diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 7ab9302d835a2..bae3664799231 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -761,9 +761,26 @@ impl<'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'tcx> { } fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) { - self.with_lint_attrs(e.hir_id, |builder| { - intravisit::walk_expr(builder, e); - }) + match e.kind { + hir::ExprKind::Struct(qpath, fields, base_expr) => { + self.with_lint_attrs(e.hir_id, |builder| { + builder.visit_qpath(qpath, e.hir_id, e.span); + for field in fields { + builder.with_lint_attrs(field.hir_id, |field_builder| { + field_builder.visit_id(field.hir_id); + field_builder.visit_ident(field.ident); + field_builder.visit_expr(field.expr); + }); + } + if let Some(base_expr) = base_expr { + builder.visit_expr(base_expr); + } + }); + } + _ => self.with_lint_attrs(e.hir_id, |builder| { + intravisit::walk_expr(builder, e); + }), + } } fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) { @@ -801,6 +818,28 @@ impl<'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'tcx> { intravisit::walk_impl_item(builder, impl_item); }); } + + fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) { + match &p.kind { + hir::PatKind::Struct(qpath, fields, _) => { + self.visit_qpath(&qpath, p.hir_id, p.span); + for field in *fields { + self.with_lint_attrs(field.hir_id, |builder| { + builder.visit_id(field.hir_id); + builder.visit_ident(field.ident); + builder.visit_pat(field.pat); + }) + } + } + _ => intravisit::walk_pat(self, p), + } + } + + fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) { + self.with_lint_attrs(p.hir_id, |builder| { + intravisit::walk_generic_param(builder, p); + }); + } } pub fn provide(providers: &mut Providers) { diff --git a/src/test/ui/lint/lint-attr-everywhere-early.rs b/src/test/ui/lint/lint-attr-everywhere-early.rs new file mode 100644 index 0000000000000..9ac0e45c6da0c --- /dev/null +++ b/src/test/ui/lint/lint-attr-everywhere-early.rs @@ -0,0 +1,169 @@ +// Tests that lint levels can be set for early lints. +#![allow(non_camel_case_types, unsafe_code, while_true, unused_parens)] + +// The following is a check of the lints used here to verify they do not warn +// when allowed. +fn verify_no_warnings() { + type non_camel_type = i32; // non_camel_case_types + struct NON_CAMEL_IS_ALLOWED; // non_camel_case_types + unsafe {} // unsafe_code + enum Enum { + VARIANT_CAMEL // non_camel_case_types + } + fn generics() {} // non_camel_case_types + while true {} // while_true + type T = (i32); // unused_parens +} + + +// ################## Types + +#[deny(non_camel_case_types)] +type type_outer = i32; //~ ERROR type `type_outer` should have an upper camel case name + +type BareFnPtr = fn(#[deny(unused_parens)](i32)); //~ ERROR unnecessary parentheses around type +// There aren't any early lints that currently apply to the variadic spot. +// type BareFnPtrVariadic = extern "C" fn(i32, #[deny()]...); + +// ################## Items +#[deny(non_camel_case_types)] +struct ITEM_OUTER; //~ ERROR type `ITEM_OUTER` should have an upper camel case name + +mod module_inner { + #![deny(unsafe_code)] + fn f() { + unsafe {} //~ ERROR usage of an `unsafe` block + } +} + +struct Associated; +impl Associated { + #![deny(unsafe_code)] + + fn inherent_denied_from_inner() { unsafe {} } //~ usage of an `unsafe` block + + #[deny(while_true)] + fn inherent_fn() { while true {} } //~ ERROR denote infinite loops with + + #[deny(while_true)] + const INHERENT_CONST: i32 = {while true {} 1}; //~ ERROR denote infinite loops with +} + +trait trait_inner { //~ ERROR trait `trait_inner` should have an upper camel case name + #![deny(non_camel_case_types)] +} + +trait AssociatedTrait { + #![deny(unsafe_code)] + + fn denied_from_inner() { unsafe {} } //~ ERROR usage of an `unsafe` block + + #[deny(while_true)] + fn assoc_fn() { while true {} } //~ ERROR denote infinite loops with + + #[deny(while_true)] + const ASSOC_CONST: i32 = {while true {} 1}; //~ ERROR denote infinite loops with + + #[deny(non_camel_case_types)] + type assoc_type; //~ ERROR associated type `assoc_type` should have an upper camel case name +} + +impl AssociatedTrait for Associated { + #![deny(unsafe_code)] + + fn denied_from_inner() { unsafe {} } //~ ERROR usage of an `unsafe` block + + #[deny(while_true)] + fn assoc_fn() { while true {} } //~ ERROR denote infinite loops with + + #[deny(while_true)] + const ASSOC_CONST: i32 = {while true {} 1}; //~ ERROR denote infinite loops with + + #[deny(unused_parens)] + type assoc_type = (i32); //~ ERROR unnecessary parentheses around type +} + +struct StructFields { + #[deny(unused_parens)]f1: (i32), //~ ERROR unnecessary parentheses around type +} +struct StructTuple(#[deny(unused_parens)](i32)); //~ ERROR unnecessary parentheses around type + +enum Enum { + #[deny(non_camel_case_types)] + VARIANT_CAMEL, //~ ERROR variant `VARIANT_CAMEL` should have an upper camel case name +} + +extern "C" { + #![deny(unused_parens)] + + fn foreign_denied_from_inner(x: (i32)); //~ ERROR unnecessary parentheses around type +} + +extern "C" { + #[deny(unused_parens)] + fn foreign_denied_from_outer(x: (i32)); //~ ERROR unnecessary parentheses around type +} + +fn function(#[deny(unused_parens)] param: (i32)) {} //~ ERROR unnecessary parentheses around type + +fn generics<#[deny(non_camel_case_types)]foo>() {} //~ ERROR type parameter `foo` should have an upper camel case name + + +// ################## Statements +fn statements() { + #[deny(unused_parens)] + let x = (1); //~ ERROR unnecessary parentheses around assigned value +} + + +// ################## Expressions +fn expressions() { + let closure = |#[deny(unused_parens)] param: (i32)| {}; //~ ERROR unnecessary parentheses around type + + struct Match{f1: i32} + // Strangely unused_parens doesn't fire with {f1: (123)} + let f = Match{#[deny(unused_parens)]f1: {(123)}}; //~ ERROR unnecessary parentheses around block return value + + match f { + #![deny(unsafe_code)] + + #[deny(while_true)] + Match{f1} => { + unsafe {} //~ ERROR usage of an `unsafe` block + while true {} //~ ERROR denote infinite loops with + } + } + + // Statement Block + { + #![deny(unsafe_code)] + unsafe {} //~ ERROR usage of an `unsafe` block + } + let block_tail = { + #[deny(unsafe_code)] + unsafe {} //~ ERROR usage of an `unsafe` block + }; + + // Before expression as a statement. + #[deny(unsafe_code)] + unsafe {}; //~ ERROR usage of an `unsafe` block + + [#[deny(unsafe_code)] unsafe {123}]; //~ ERROR usage of an `unsafe` block + (#[deny(unsafe_code)] unsafe {123},); //~ ERROR usage of an `unsafe` block + fn call(p: i32) {} + call(#[deny(unsafe_code)] unsafe {123}); //~ ERROR usage of an `unsafe` block + struct TupleStruct(i32); + TupleStruct(#[deny(unsafe_code)] unsafe {123}); //~ ERROR usage of an `unsafe` block +} + + +// ################## Patterns +fn patterns() { + // There aren't any early lints that I can find that apply to pattern fields. + // + // struct PatField{f1: i32, f2: i32}; + // let f = PatField{f1: 1, f2: 2}; + // let PatField{#[deny()]f1, #[deny()]..} = f; +} + +fn main() {} diff --git a/src/test/ui/lint/lint-attr-everywhere-early.stderr b/src/test/ui/lint/lint-attr-everywhere-early.stderr new file mode 100644 index 0000000000000..f65d811105fab --- /dev/null +++ b/src/test/ui/lint/lint-attr-everywhere-early.stderr @@ -0,0 +1,472 @@ +error: type `type_outer` should have an upper camel case name + --> $DIR/lint-attr-everywhere-early.rs:22:6 + | +LL | type type_outer = i32; + | ^^^^^^^^^^ help: convert the identifier to upper camel case: `TypeOuter` + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:21:8 + | +LL | #[deny(non_camel_case_types)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: unnecessary parentheses around type + --> $DIR/lint-attr-everywhere-early.rs:24:43 + | +LL | type BareFnPtr = fn(#[deny(unused_parens)](i32)); + | ^ ^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:24:28 + | +LL | type BareFnPtr = fn(#[deny(unused_parens)](i32)); + | ^^^^^^^^^^^^^ +help: remove these parentheses + | +LL - type BareFnPtr = fn(#[deny(unused_parens)](i32)); +LL + type BareFnPtr = fn(#[deny(unused_parens)]i32); + | + +error: type `ITEM_OUTER` should have an upper camel case name + --> $DIR/lint-attr-everywhere-early.rs:30:8 + | +LL | struct ITEM_OUTER; + | ^^^^^^^^^^ help: convert the identifier to upper camel case: `ItemOuter` + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:29:8 + | +LL | #[deny(non_camel_case_types)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: usage of an `unsafe` block + --> $DIR/lint-attr-everywhere-early.rs:35:9 + | +LL | unsafe {} + | ^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:33:13 + | +LL | #![deny(unsafe_code)] + | ^^^^^^^^^^^ + +error: usage of an `unsafe` block + --> $DIR/lint-attr-everywhere-early.rs:43:39 + | +LL | fn inherent_denied_from_inner() { unsafe {} } + | ^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:41:13 + | +LL | #![deny(unsafe_code)] + | ^^^^^^^^^^^ + +error: denote infinite loops with `loop { ... }` + --> $DIR/lint-attr-everywhere-early.rs:46:24 + | +LL | fn inherent_fn() { while true {} } + | ^^^^^^^^^^ help: use `loop` + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:45:12 + | +LL | #[deny(while_true)] + | ^^^^^^^^^^ + +error: denote infinite loops with `loop { ... }` + --> $DIR/lint-attr-everywhere-early.rs:49:34 + | +LL | const INHERENT_CONST: i32 = {while true {} 1}; + | ^^^^^^^^^^ help: use `loop` + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:48:12 + | +LL | #[deny(while_true)] + | ^^^^^^^^^^ + +error: trait `trait_inner` should have an upper camel case name + --> $DIR/lint-attr-everywhere-early.rs:52:7 + | +LL | trait trait_inner { + | ^^^^^^^^^^^ help: convert the identifier to upper camel case: `TraitInner` + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:53:13 + | +LL | #![deny(non_camel_case_types)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: usage of an `unsafe` block + --> $DIR/lint-attr-everywhere-early.rs:59:30 + | +LL | fn denied_from_inner() { unsafe {} } + | ^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:57:13 + | +LL | #![deny(unsafe_code)] + | ^^^^^^^^^^^ + +error: denote infinite loops with `loop { ... }` + --> $DIR/lint-attr-everywhere-early.rs:62:21 + | +LL | fn assoc_fn() { while true {} } + | ^^^^^^^^^^ help: use `loop` + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:61:12 + | +LL | #[deny(while_true)] + | ^^^^^^^^^^ + +error: denote infinite loops with `loop { ... }` + --> $DIR/lint-attr-everywhere-early.rs:65:31 + | +LL | const ASSOC_CONST: i32 = {while true {} 1}; + | ^^^^^^^^^^ help: use `loop` + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:64:12 + | +LL | #[deny(while_true)] + | ^^^^^^^^^^ + +error: associated type `assoc_type` should have an upper camel case name + --> $DIR/lint-attr-everywhere-early.rs:68:10 + | +LL | type assoc_type; + | ^^^^^^^^^^ help: convert the identifier to upper camel case: `AssocType` + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:67:12 + | +LL | #[deny(non_camel_case_types)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: usage of an `unsafe` block + --> $DIR/lint-attr-everywhere-early.rs:74:30 + | +LL | fn denied_from_inner() { unsafe {} } + | ^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:72:13 + | +LL | #![deny(unsafe_code)] + | ^^^^^^^^^^^ + +error: denote infinite loops with `loop { ... }` + --> $DIR/lint-attr-everywhere-early.rs:77:21 + | +LL | fn assoc_fn() { while true {} } + | ^^^^^^^^^^ help: use `loop` + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:76:12 + | +LL | #[deny(while_true)] + | ^^^^^^^^^^ + +error: denote infinite loops with `loop { ... }` + --> $DIR/lint-attr-everywhere-early.rs:80:31 + | +LL | const ASSOC_CONST: i32 = {while true {} 1}; + | ^^^^^^^^^^ help: use `loop` + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:79:12 + | +LL | #[deny(while_true)] + | ^^^^^^^^^^ + +error: unnecessary parentheses around type + --> $DIR/lint-attr-everywhere-early.rs:83:23 + | +LL | type assoc_type = (i32); + | ^ ^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:82:12 + | +LL | #[deny(unused_parens)] + | ^^^^^^^^^^^^^ +help: remove these parentheses + | +LL - type assoc_type = (i32); +LL + type assoc_type = i32; + | + +error: unnecessary parentheses around type + --> $DIR/lint-attr-everywhere-early.rs:87:31 + | +LL | #[deny(unused_parens)]f1: (i32), + | ^ ^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:87:12 + | +LL | #[deny(unused_parens)]f1: (i32), + | ^^^^^^^^^^^^^ +help: remove these parentheses + | +LL - #[deny(unused_parens)]f1: (i32), +LL + #[deny(unused_parens)]f1: i32, + | + +error: unnecessary parentheses around type + --> $DIR/lint-attr-everywhere-early.rs:89:42 + | +LL | struct StructTuple(#[deny(unused_parens)](i32)); + | ^ ^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:89:27 + | +LL | struct StructTuple(#[deny(unused_parens)](i32)); + | ^^^^^^^^^^^^^ +help: remove these parentheses + | +LL - struct StructTuple(#[deny(unused_parens)](i32)); +LL + struct StructTuple(#[deny(unused_parens)]i32); + | + +error: variant `VARIANT_CAMEL` should have an upper camel case name + --> $DIR/lint-attr-everywhere-early.rs:93:5 + | +LL | VARIANT_CAMEL, + | ^^^^^^^^^^^^^ help: convert the identifier to upper camel case: `VariantCamel` + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:92:12 + | +LL | #[deny(non_camel_case_types)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: unnecessary parentheses around type + --> $DIR/lint-attr-everywhere-early.rs:99:37 + | +LL | fn foreign_denied_from_inner(x: (i32)); + | ^ ^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:97:13 + | +LL | #![deny(unused_parens)] + | ^^^^^^^^^^^^^ +help: remove these parentheses + | +LL - fn foreign_denied_from_inner(x: (i32)); +LL + fn foreign_denied_from_inner(x: i32); + | + +error: unnecessary parentheses around type + --> $DIR/lint-attr-everywhere-early.rs:104:37 + | +LL | fn foreign_denied_from_outer(x: (i32)); + | ^ ^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:103:12 + | +LL | #[deny(unused_parens)] + | ^^^^^^^^^^^^^ +help: remove these parentheses + | +LL - fn foreign_denied_from_outer(x: (i32)); +LL + fn foreign_denied_from_outer(x: i32); + | + +error: unnecessary parentheses around type + --> $DIR/lint-attr-everywhere-early.rs:107:43 + | +LL | fn function(#[deny(unused_parens)] param: (i32)) {} + | ^ ^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:107:20 + | +LL | fn function(#[deny(unused_parens)] param: (i32)) {} + | ^^^^^^^^^^^^^ +help: remove these parentheses + | +LL - fn function(#[deny(unused_parens)] param: (i32)) {} +LL + fn function(#[deny(unused_parens)] param: i32) {} + | + +error: type parameter `foo` should have an upper camel case name + --> $DIR/lint-attr-everywhere-early.rs:109:42 + | +LL | fn generics<#[deny(non_camel_case_types)]foo>() {} + | ^^^ help: convert the identifier to upper camel case (notice the capitalization): `Foo` + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:109:20 + | +LL | fn generics<#[deny(non_camel_case_types)]foo>() {} + | ^^^^^^^^^^^^^^^^^^^^ + +error: unnecessary parentheses around assigned value + --> $DIR/lint-attr-everywhere-early.rs:115:13 + | +LL | let x = (1); + | ^ ^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:114:12 + | +LL | #[deny(unused_parens)] + | ^^^^^^^^^^^^^ +help: remove these parentheses + | +LL - let x = (1); +LL + let x = 1; + | + +error: unnecessary parentheses around type + --> $DIR/lint-attr-everywhere-early.rs:121:50 + | +LL | let closure = |#[deny(unused_parens)] param: (i32)| {}; + | ^ ^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:121:27 + | +LL | let closure = |#[deny(unused_parens)] param: (i32)| {}; + | ^^^^^^^^^^^^^ +help: remove these parentheses + | +LL - let closure = |#[deny(unused_parens)] param: (i32)| {}; +LL + let closure = |#[deny(unused_parens)] param: i32| {}; + | + +error: unnecessary parentheses around block return value + --> $DIR/lint-attr-everywhere-early.rs:125:46 + | +LL | let f = Match{#[deny(unused_parens)]f1: {(123)}}; + | ^ ^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:125:26 + | +LL | let f = Match{#[deny(unused_parens)]f1: {(123)}}; + | ^^^^^^^^^^^^^ +help: remove these parentheses + | +LL - let f = Match{#[deny(unused_parens)]f1: {(123)}}; +LL + let f = Match{#[deny(unused_parens)]f1: {123}}; + | + +error: usage of an `unsafe` block + --> $DIR/lint-attr-everywhere-early.rs:132:13 + | +LL | unsafe {} + | ^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:128:17 + | +LL | #![deny(unsafe_code)] + | ^^^^^^^^^^^ + +error: denote infinite loops with `loop { ... }` + --> $DIR/lint-attr-everywhere-early.rs:133:13 + | +LL | while true {} + | ^^^^^^^^^^ help: use `loop` + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:130:16 + | +LL | #[deny(while_true)] + | ^^^^^^^^^^ + +error: usage of an `unsafe` block + --> $DIR/lint-attr-everywhere-early.rs:140:9 + | +LL | unsafe {} + | ^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:139:17 + | +LL | #![deny(unsafe_code)] + | ^^^^^^^^^^^ + +error: usage of an `unsafe` block + --> $DIR/lint-attr-everywhere-early.rs:144:9 + | +LL | unsafe {} + | ^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:143:16 + | +LL | #[deny(unsafe_code)] + | ^^^^^^^^^^^ + +error: usage of an `unsafe` block + --> $DIR/lint-attr-everywhere-early.rs:149:5 + | +LL | unsafe {}; + | ^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:148:12 + | +LL | #[deny(unsafe_code)] + | ^^^^^^^^^^^ + +error: usage of an `unsafe` block + --> $DIR/lint-attr-everywhere-early.rs:151:27 + | +LL | [#[deny(unsafe_code)] unsafe {123}]; + | ^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:151:13 + | +LL | [#[deny(unsafe_code)] unsafe {123}]; + | ^^^^^^^^^^^ + +error: usage of an `unsafe` block + --> $DIR/lint-attr-everywhere-early.rs:152:27 + | +LL | (#[deny(unsafe_code)] unsafe {123},); + | ^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:152:13 + | +LL | (#[deny(unsafe_code)] unsafe {123},); + | ^^^^^^^^^^^ + +error: usage of an `unsafe` block + --> $DIR/lint-attr-everywhere-early.rs:154:31 + | +LL | call(#[deny(unsafe_code)] unsafe {123}); + | ^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:154:17 + | +LL | call(#[deny(unsafe_code)] unsafe {123}); + | ^^^^^^^^^^^ + +error: usage of an `unsafe` block + --> $DIR/lint-attr-everywhere-early.rs:156:38 + | +LL | TupleStruct(#[deny(unsafe_code)] unsafe {123}); + | ^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:156:24 + | +LL | TupleStruct(#[deny(unsafe_code)] unsafe {123}); + | ^^^^^^^^^^^ + +error: aborting due to 35 previous errors + diff --git a/src/test/ui/lint/lint-attr-everywhere-late.rs b/src/test/ui/lint/lint-attr-everywhere-late.rs new file mode 100644 index 0000000000000..1055157d6020c --- /dev/null +++ b/src/test/ui/lint/lint-attr-everywhere-late.rs @@ -0,0 +1,197 @@ +// Tests that lint levels can be set for late lints. +#![allow( + non_snake_case, + overflowing_literals, + missing_docs, + dyn_drop, + enum_intrinsics_non_enums, + clashing_extern_declarations +)] + +extern crate core; +use core::mem::{Discriminant, discriminant}; + +// The following is a check of the lints used here to verify they do not warn +// when allowed. +pub fn missing_docs_allowed() {} // missing_docs +fn dyn_drop_allowed(_x: Box) {} // dyn_drop +fn verify_no_warnings() { + discriminant::(&123); // enum_intrinsics_non_enums + let x: u8 = 1000; // overflowing_literals + let NON_SNAKE_CASE = 1; // non_snake_case +} +mod clashing_extern_allowed { + extern "C" { + fn extern_allowed(); + } +} +extern "C" { + fn extern_allowed(_: i32); // clashing_extern_declarations +} + +// ################## Types + +#[deny(missing_docs)] +pub type MissingDocType = i32; //~ ERROR missing documentation for a type alias + +// There aren't any late lints that I can find that can be easily used with types. +// type BareFnPtr = fn(#[deny()]i32); +// type BareFnPtrVariadic = extern "C" fn(i32, #[deny()]...); + +// ################## Items +#[deny(missing_docs)] +pub struct ItemOuter; //~ ERROR missing documentation for a struct + +pub mod module_inner { //~ ERROR missing documentation for a module + #![deny(missing_docs)] + pub fn missing_inner() {} //~ ERROR missing documentation for a function +} + +pub struct Associated; +impl Associated { + #![deny(missing_docs)] + + pub fn inherent_denied_from_inner() {} //~ ERROR missing documentation for an associated function +} + +impl Associated { + #[deny(missing_docs)] + pub fn inherent_fn() {} //~ ERROR missing documentation for an associated function + + #[deny(missing_docs)] + pub const INHERENT_CONST: i32 = 1; //~ ERROR missing documentation for an associated constant +} + +pub trait TraitInner { //~ ERROR missing documentation for a trait + #![deny(missing_docs)] +} + +pub trait AssociatedTraitInner { //~ ERROR missing documentation for a trait + #![deny(missing_docs)] + + fn denied_from_inner() {} //~ ERROR missing documentation for an associated function +} + +pub trait AssociatedTrait { + fn denied_from_inner(_x: Box) {} // Used below + + #[deny(missing_docs)] + fn assoc_fn() {} //~ ERROR missing documentation for an associated function + + #[deny(missing_docs)] + const ASSOC_CONST: u8 = 1; //~ ERROR missing documentation for an associated constant + + #[deny(missing_docs)] + type AssocType; //~ ERROR missing documentation for an associated type +} + +struct Foo; + +impl AssociatedTrait for Associated { + #![deny(dyn_drop)] + + fn denied_from_inner(_x: Box) {} //~ ERROR types that do not implement `Drop` + + #[deny(enum_intrinsics_non_enums)] + fn assoc_fn() { discriminant::(&123); } //~ ERROR the return value of + + #[deny(overflowing_literals)] const ASSOC_CONST: u8 = 1000; //~ ERROR literal out of range + type AssocType = i32; +} + + +// There aren't any late lints that can apply to a field that I can find. +// non_snake_case doesn't work on fields +// struct StructFields { +// #[deny()]f1: i32, +// } +// struct StructTuple(#[deny()]i32); + +pub enum Enum { + #[deny(missing_docs)] + Variant1, //~ ERROR missing documentation for a variant +} + +mod clashing_extern { + extern "C" { + fn clashing1(); + fn clashing2(); + } +} +extern "C" { + #![deny(clashing_extern_declarations)] + fn clashing1(_: i32); //~ ERROR `clashing1` redeclared with a different signature +} + +extern "C" { + #[deny(clashing_extern_declarations)] + fn clashing2(_: i32); //~ ERROR `clashing2` redeclared with a different signature +} + +fn function(#[deny(non_snake_case)] PARAM: i32) {} //~ ERROR variable `PARAM` should have a snake case name +// There aren't any late lints that can apply to generics that I can find. +// fn generics<#[deny()]T>() {} + + +// ################## Statements +fn statements() { + #[deny(enum_intrinsics_non_enums)] + let _ = discriminant::(&123); //~ ERROR the return value of +} + + +// ################## Expressions +fn expressions() { + let closure = |#[deny(non_snake_case)] PARAM: i32| {}; //~ ERROR variable `PARAM` should have a snake case name + + struct Match{f1: i32} + // I can't find any late lints for patterns. + // let f = Match{#[deny()]f1: 123}; + + let f = Match{f1: 123}; + match f { + #![deny(enum_intrinsics_non_enums)] + Match{f1} => { + discriminant::(&123); //~ ERROR the return value of + } + } + match f { + #[deny(enum_intrinsics_non_enums)] + Match{f1} => { + discriminant::(&123); //~ ERROR the return value of + } + } + + // Statement Block + { + #![deny(enum_intrinsics_non_enums)] + discriminant::(&123); //~ ERROR the return value of + } + let block_tail = { + #[deny(enum_intrinsics_non_enums)] + discriminant::(&123); //~ ERROR the return value of + }; + + // Before expression as a statement. + #[deny(enum_intrinsics_non_enums)] + discriminant::(&123); //~ ERROR the return value of + + [#[deny(enum_intrinsics_non_enums)] discriminant::(&123)]; //~ ERROR the return value of + (#[deny(enum_intrinsics_non_enums)] discriminant::(&123),); //~ ERROR the return value of + fn call(p: Discriminant) {} + call(#[deny(enum_intrinsics_non_enums)] discriminant::(&123)); //~ ERROR the return value of + struct TupleStruct(Discriminant); + TupleStruct(#[deny(enum_intrinsics_non_enums)] discriminant::(&123)); //~ ERROR the return value of +} + + +// ################## Patterns +fn patterns() { + // There aren't any late lints that I can find that apply to pattern fields. + // + // struct PatField{f1: i32, f2: i32}; + // let f = PatField{f1: 1, f2: 2}; + // let PatField{#[deny()]f1, #[deny()]..} = f; +} + +fn main() {} diff --git a/src/test/ui/lint/lint-attr-everywhere-late.stderr b/src/test/ui/lint/lint-attr-everywhere-late.stderr new file mode 100644 index 0000000000000..d302798d65007 --- /dev/null +++ b/src/test/ui/lint/lint-attr-everywhere-late.stderr @@ -0,0 +1,428 @@ +error: missing documentation for a type alias + --> $DIR/lint-attr-everywhere-late.rs:35:1 + | +LL | pub type MissingDocType = i32; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:34:8 + | +LL | #[deny(missing_docs)] + | ^^^^^^^^^^^^ + +error: missing documentation for a struct + --> $DIR/lint-attr-everywhere-late.rs:43:1 + | +LL | pub struct ItemOuter; + | ^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:42:8 + | +LL | #[deny(missing_docs)] + | ^^^^^^^^^^^^ + +error: missing documentation for a module + --> $DIR/lint-attr-everywhere-late.rs:45:1 + | +LL | pub mod module_inner { + | ^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:46:13 + | +LL | #![deny(missing_docs)] + | ^^^^^^^^^^^^ + +error: missing documentation for a function + --> $DIR/lint-attr-everywhere-late.rs:47:5 + | +LL | pub fn missing_inner() {} + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for an associated function + --> $DIR/lint-attr-everywhere-late.rs:54:5 + | +LL | pub fn inherent_denied_from_inner() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:52:13 + | +LL | #![deny(missing_docs)] + | ^^^^^^^^^^^^ + +error: missing documentation for an associated function + --> $DIR/lint-attr-everywhere-late.rs:59:5 + | +LL | pub fn inherent_fn() {} + | ^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:58:12 + | +LL | #[deny(missing_docs)] + | ^^^^^^^^^^^^ + +error: missing documentation for an associated constant + --> $DIR/lint-attr-everywhere-late.rs:62:5 + | +LL | pub const INHERENT_CONST: i32 = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:61:12 + | +LL | #[deny(missing_docs)] + | ^^^^^^^^^^^^ + +error: missing documentation for a trait + --> $DIR/lint-attr-everywhere-late.rs:65:1 + | +LL | pub trait TraitInner { + | ^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:66:13 + | +LL | #![deny(missing_docs)] + | ^^^^^^^^^^^^ + +error: missing documentation for a trait + --> $DIR/lint-attr-everywhere-late.rs:69:1 + | +LL | pub trait AssociatedTraitInner { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:70:13 + | +LL | #![deny(missing_docs)] + | ^^^^^^^^^^^^ + +error: missing documentation for an associated function + --> $DIR/lint-attr-everywhere-late.rs:72:5 + | +LL | fn denied_from_inner() {} + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for an associated function + --> $DIR/lint-attr-everywhere-late.rs:79:5 + | +LL | fn assoc_fn() {} + | ^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:78:12 + | +LL | #[deny(missing_docs)] + | ^^^^^^^^^^^^ + +error: missing documentation for an associated constant + --> $DIR/lint-attr-everywhere-late.rs:82:5 + | +LL | const ASSOC_CONST: u8 = 1; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:81:12 + | +LL | #[deny(missing_docs)] + | ^^^^^^^^^^^^ + +error: missing documentation for an associated type + --> $DIR/lint-attr-everywhere-late.rs:85:5 + | +LL | type AssocType; + | ^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:84:12 + | +LL | #[deny(missing_docs)] + | ^^^^^^^^^^^^ + +error: types that do not implement `Drop` can still have drop glue, consider instead using `std::mem::needs_drop` to detect whether a type is trivially dropped + --> $DIR/lint-attr-everywhere-late.rs:93:38 + | +LL | fn denied_from_inner(_x: Box) {} + | ^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:91:13 + | +LL | #![deny(dyn_drop)] + | ^^^^^^^^ + +error: the return value of `mem::discriminant` is unspecified when called with a non-enum type + --> $DIR/lint-attr-everywhere-late.rs:96:21 + | +LL | fn assoc_fn() { discriminant::(&123); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:95:12 + | +LL | #[deny(enum_intrinsics_non_enums)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `i32`, which is not an enum. + --> $DIR/lint-attr-everywhere-late.rs:96:41 + | +LL | fn assoc_fn() { discriminant::(&123); } + | ^^^^ + +error: missing documentation for a variant + --> $DIR/lint-attr-everywhere-late.rs:112:5 + | +LL | Variant1, + | ^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:111:12 + | +LL | #[deny(missing_docs)] + | ^^^^^^^^^^^^ + +error: `clashing1` redeclared with a different signature + --> $DIR/lint-attr-everywhere-late.rs:123:5 + | +LL | fn clashing1(); + | --------------- `clashing1` previously declared here +... +LL | fn clashing1(_: i32); + | ^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:122:13 + | +LL | #![deny(clashing_extern_declarations)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected `unsafe extern "C" fn()` + found `unsafe extern "C" fn(i32)` + +error: `clashing2` redeclared with a different signature + --> $DIR/lint-attr-everywhere-late.rs:128:5 + | +LL | fn clashing2(); + | --------------- `clashing2` previously declared here +... +LL | fn clashing2(_: i32); + | ^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:127:12 + | +LL | #[deny(clashing_extern_declarations)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected `unsafe extern "C" fn()` + found `unsafe extern "C" fn(i32)` + +error: the return value of `mem::discriminant` is unspecified when called with a non-enum type + --> $DIR/lint-attr-everywhere-late.rs:139:13 + | +LL | let _ = discriminant::(&123); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:138:12 + | +LL | #[deny(enum_intrinsics_non_enums)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `i32`, which is not an enum. + --> $DIR/lint-attr-everywhere-late.rs:139:33 + | +LL | let _ = discriminant::(&123); + | ^^^^ + +error: the return value of `mem::discriminant` is unspecified when called with a non-enum type + --> $DIR/lint-attr-everywhere-late.rs:155:13 + | +LL | discriminant::(&123); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:153:17 + | +LL | #![deny(enum_intrinsics_non_enums)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `i32`, which is not an enum. + --> $DIR/lint-attr-everywhere-late.rs:155:33 + | +LL | discriminant::(&123); + | ^^^^ + +error: the return value of `mem::discriminant` is unspecified when called with a non-enum type + --> $DIR/lint-attr-everywhere-late.rs:161:13 + | +LL | discriminant::(&123); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:159:16 + | +LL | #[deny(enum_intrinsics_non_enums)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `i32`, which is not an enum. + --> $DIR/lint-attr-everywhere-late.rs:161:33 + | +LL | discriminant::(&123); + | ^^^^ + +error: the return value of `mem::discriminant` is unspecified when called with a non-enum type + --> $DIR/lint-attr-everywhere-late.rs:168:9 + | +LL | discriminant::(&123); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:167:17 + | +LL | #![deny(enum_intrinsics_non_enums)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `i32`, which is not an enum. + --> $DIR/lint-attr-everywhere-late.rs:168:29 + | +LL | discriminant::(&123); + | ^^^^ + +error: the return value of `mem::discriminant` is unspecified when called with a non-enum type + --> $DIR/lint-attr-everywhere-late.rs:172:9 + | +LL | discriminant::(&123); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:171:16 + | +LL | #[deny(enum_intrinsics_non_enums)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `i32`, which is not an enum. + --> $DIR/lint-attr-everywhere-late.rs:172:29 + | +LL | discriminant::(&123); + | ^^^^ + +error: the return value of `mem::discriminant` is unspecified when called with a non-enum type + --> $DIR/lint-attr-everywhere-late.rs:177:5 + | +LL | discriminant::(&123); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:176:12 + | +LL | #[deny(enum_intrinsics_non_enums)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `i32`, which is not an enum. + --> $DIR/lint-attr-everywhere-late.rs:177:25 + | +LL | discriminant::(&123); + | ^^^^ + +error: the return value of `mem::discriminant` is unspecified when called with a non-enum type + --> $DIR/lint-attr-everywhere-late.rs:179:41 + | +LL | [#[deny(enum_intrinsics_non_enums)] discriminant::(&123)]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:179:13 + | +LL | [#[deny(enum_intrinsics_non_enums)] discriminant::(&123)]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `i32`, which is not an enum. + --> $DIR/lint-attr-everywhere-late.rs:179:61 + | +LL | [#[deny(enum_intrinsics_non_enums)] discriminant::(&123)]; + | ^^^^ + +error: the return value of `mem::discriminant` is unspecified when called with a non-enum type + --> $DIR/lint-attr-everywhere-late.rs:180:41 + | +LL | (#[deny(enum_intrinsics_non_enums)] discriminant::(&123),); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:180:13 + | +LL | (#[deny(enum_intrinsics_non_enums)] discriminant::(&123),); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `i32`, which is not an enum. + --> $DIR/lint-attr-everywhere-late.rs:180:61 + | +LL | (#[deny(enum_intrinsics_non_enums)] discriminant::(&123),); + | ^^^^ + +error: the return value of `mem::discriminant` is unspecified when called with a non-enum type + --> $DIR/lint-attr-everywhere-late.rs:182:45 + | +LL | call(#[deny(enum_intrinsics_non_enums)] discriminant::(&123)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:182:17 + | +LL | call(#[deny(enum_intrinsics_non_enums)] discriminant::(&123)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `i32`, which is not an enum. + --> $DIR/lint-attr-everywhere-late.rs:182:65 + | +LL | call(#[deny(enum_intrinsics_non_enums)] discriminant::(&123)); + | ^^^^ + +error: the return value of `mem::discriminant` is unspecified when called with a non-enum type + --> $DIR/lint-attr-everywhere-late.rs:184:52 + | +LL | TupleStruct(#[deny(enum_intrinsics_non_enums)] discriminant::(&123)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:184:24 + | +LL | TupleStruct(#[deny(enum_intrinsics_non_enums)] discriminant::(&123)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `i32`, which is not an enum. + --> $DIR/lint-attr-everywhere-late.rs:184:72 + | +LL | TupleStruct(#[deny(enum_intrinsics_non_enums)] discriminant::(&123)); + | ^^^^ + +error: literal out of range for `u8` + --> $DIR/lint-attr-everywhere-late.rs:98:59 + | +LL | #[deny(overflowing_literals)] const ASSOC_CONST: u8 = 1000; + | ^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:98:12 + | +LL | #[deny(overflowing_literals)] const ASSOC_CONST: u8 = 1000; + | ^^^^^^^^^^^^^^^^^^^^ + = note: the literal `1000` does not fit into the type `u8` whose range is `0..=255` + +error: variable `PARAM` should have a snake case name + --> $DIR/lint-attr-everywhere-late.rs:131:37 + | +LL | fn function(#[deny(non_snake_case)] PARAM: i32) {} + | ^^^^^ help: convert the identifier to snake case: `param` + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:131:20 + | +LL | fn function(#[deny(non_snake_case)] PARAM: i32) {} + | ^^^^^^^^^^^^^^ + +error: variable `PARAM` should have a snake case name + --> $DIR/lint-attr-everywhere-late.rs:145:44 + | +LL | let closure = |#[deny(non_snake_case)] PARAM: i32| {}; + | ^^^^^ help: convert the identifier to snake case: `param` + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:145:27 + | +LL | let closure = |#[deny(non_snake_case)] PARAM: i32| {}; + | ^^^^^^^^^^^^^^ + +error: aborting due to 31 previous errors + From dcd5177fd43d5fdd9b89a7700fe3c283a9a52a48 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sun, 22 May 2022 18:34:37 -0700 Subject: [PATCH 4/8] Add visitors for PatField and ExprField. This helps simplify the code. It also fixes it to use the correct parent when lowering. One consequence is the `non_snake_case` lint needed to change the way it looked for parent nodes in a struct pattern. This also includes a small fix to use the correct `Target` for expression field attribute validation. --- compiler/rustc_ast_lowering/src/index.rs | 12 +++-- compiler/rustc_hir/src/intravisit.rs | 30 ++++++++---- compiler/rustc_lint/src/levels.rs | 47 +++++-------------- compiler/rustc_lint/src/nonstandard_style.rs | 17 +++---- compiler/rustc_passes/src/check_attr.rs | 20 ++++---- .../unused/unused_attributes-must_use.stderr | 2 +- 6 files changed, 55 insertions(+), 73 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index ea35cf3de0455..899e0dd5cc3b9 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -193,17 +193,19 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { fn visit_pat(&mut self, pat: &'hir Pat<'hir>) { self.insert(pat.span, pat.hir_id, Node::Pat(pat)); - if let PatKind::Struct(_, fields, _) = pat.kind { - for field in fields { - self.insert(field.span, field.hir_id, Node::PatField(field)); - } - } self.with_parent(pat.hir_id, |this| { intravisit::walk_pat(this, pat); }); } + fn visit_pat_field(&mut self, field: &'hir PatField<'hir>) { + self.insert(field.span, field.hir_id, Node::PatField(field)); + self.with_parent(field.hir_id, |this| { + intravisit::walk_pat_field(this, field); + }); + } + fn visit_arm(&mut self, arm: &'hir Arm<'hir>) { let node = Node::Arm(arm); diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 51b1bfad8a0d8..900370937f79a 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -325,6 +325,9 @@ pub trait Visitor<'v>: Sized { fn visit_pat(&mut self, p: &'v Pat<'v>) { walk_pat(self, p) } + fn visit_pat_field(&mut self, f: &'v PatField<'v>) { + walk_pat_field(self, f) + } fn visit_array_length(&mut self, len: &'v ArrayLen) { walk_array_len(self, len) } @@ -337,6 +340,9 @@ pub trait Visitor<'v>: Sized { fn visit_let_expr(&mut self, lex: &'v Let<'v>) { walk_let_expr(self, lex) } + fn visit_expr_field(&mut self, field: &'v ExprField<'v>) { + walk_expr_field(self, field) + } fn visit_ty(&mut self, t: &'v Ty<'v>) { walk_ty(self, t) } @@ -761,11 +767,7 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) { } PatKind::Struct(ref qpath, fields, _) => { visitor.visit_qpath(qpath, pattern.hir_id, pattern.span); - for field in fields { - visitor.visit_id(field.hir_id); - visitor.visit_ident(field.ident); - visitor.visit_pat(&field.pat) - } + walk_list!(visitor, visit_pat_field, fields); } PatKind::Or(pats) => walk_list!(visitor, visit_pat, pats), PatKind::Tuple(tuple_elements, _) => { @@ -792,6 +794,12 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) { } } +pub fn walk_pat_field<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v PatField<'v>) { + visitor.visit_id(field.hir_id); + visitor.visit_ident(field.ident); + visitor.visit_pat(&field.pat) +} + pub fn walk_foreign_item<'v, V: Visitor<'v>>(visitor: &mut V, foreign_item: &'v ForeignItem<'v>) { visitor.visit_id(foreign_item.hir_id()); visitor.visit_ident(foreign_item.ident); @@ -1059,6 +1067,12 @@ pub fn walk_let_expr<'v, V: Visitor<'v>>(visitor: &mut V, let_expr: &'v Let<'v>) walk_list!(visitor, visit_ty, let_expr.ty); } +pub fn walk_expr_field<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v ExprField<'v>) { + visitor.visit_id(field.hir_id); + visitor.visit_ident(field.ident); + visitor.visit_expr(&field.expr) +} + pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) { visitor.visit_id(expression.hir_id); match expression.kind { @@ -1073,11 +1087,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) } ExprKind::Struct(ref qpath, fields, ref optional_base) => { visitor.visit_qpath(qpath, expression.hir_id, expression.span); - for field in fields { - visitor.visit_id(field.hir_id); - visitor.visit_ident(field.ident); - visitor.visit_expr(&field.expr) - } + walk_list!(visitor, visit_expr_field, fields); walk_list!(visitor, visit_expr, optional_base); } ExprKind::Tup(subexpressions) => { diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index bae3664799231..1cabb58bbebfd 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -761,26 +761,15 @@ impl<'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'tcx> { } fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) { - match e.kind { - hir::ExprKind::Struct(qpath, fields, base_expr) => { - self.with_lint_attrs(e.hir_id, |builder| { - builder.visit_qpath(qpath, e.hir_id, e.span); - for field in fields { - builder.with_lint_attrs(field.hir_id, |field_builder| { - field_builder.visit_id(field.hir_id); - field_builder.visit_ident(field.ident); - field_builder.visit_expr(field.expr); - }); - } - if let Some(base_expr) = base_expr { - builder.visit_expr(base_expr); - } - }); - } - _ => self.with_lint_attrs(e.hir_id, |builder| { - intravisit::walk_expr(builder, e); - }), - } + self.with_lint_attrs(e.hir_id, |builder| { + intravisit::walk_expr(builder, e); + }) + } + + fn visit_expr_field(&mut self, field: &'tcx hir::ExprField<'tcx>) { + self.with_lint_attrs(field.hir_id, |builder| { + intravisit::walk_expr_field(builder, field); + }) } fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) { @@ -819,20 +808,10 @@ impl<'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'tcx> { }); } - fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) { - match &p.kind { - hir::PatKind::Struct(qpath, fields, _) => { - self.visit_qpath(&qpath, p.hir_id, p.span); - for field in *fields { - self.with_lint_attrs(field.hir_id, |builder| { - builder.visit_id(field.hir_id); - builder.visit_ident(field.ident); - builder.visit_pat(field.pat); - }) - } - } - _ => intravisit::walk_pat(self, p), - } + fn visit_pat_field(&mut self, field: &'tcx hir::PatField<'tcx>) { + self.with_lint_attrs(field.hir_id, |builder| { + intravisit::walk_pat_field(builder, field); + }) } fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) { diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 8d04d68bf1c2d..2868aabfa07e2 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -437,19 +437,14 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase { fn check_pat(&mut self, cx: &LateContext<'_>, p: &hir::Pat<'_>) { if let PatKind::Binding(_, hid, ident, _) = p.kind { - if let hir::Node::Pat(parent_pat) = cx.tcx.hir().get(cx.tcx.hir().get_parent_node(hid)) + if let hir::Node::PatField(field) = cx.tcx.hir().get(cx.tcx.hir().get_parent_node(hid)) { - if let PatKind::Struct(_, field_pats, _) = &parent_pat.kind { - if field_pats - .iter() - .any(|field| !field.is_shorthand && field.pat.hir_id == p.hir_id) - { - // Only check if a new name has been introduced, to avoid warning - // on both the struct definition and this pattern. - self.check_snake_case(cx, "variable", &ident); - } - return; + if !field.is_shorthand { + // Only check if a new name has been introduced, to avoid warning + // on both the struct definition and this pattern. + self.check_snake_case(cx, "variable", &ident); } + return; } self.check_snake_case(cx, "variable", &ident); } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 0eb6f401b3817..5789531d2ff5a 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -2065,14 +2065,14 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { }; self.check_attributes(expr.hir_id, expr.span, target, None); - if let hir::ExprKind::Struct(_, fields, _) = expr.kind { - for field in fields { - self.check_attributes(field.hir_id, field.span, Target::PatField, None); - } - } intravisit::walk_expr(self, expr) } + fn visit_expr_field(&mut self, field: &'tcx hir::ExprField<'tcx>) { + self.check_attributes(field.hir_id, field.span, Target::ExprField, None); + intravisit::walk_expr_field(self, field) + } + fn visit_variant(&mut self, variant: &'tcx hir::Variant<'tcx>) { self.check_attributes(variant.id, variant.span, Target::Variant, None); intravisit::walk_variant(self, variant) @@ -2084,13 +2084,9 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { intravisit::walk_param(self, param); } - fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) { - if let hir::PatKind::Struct(_, fields, _) = p.kind { - for field in fields { - self.check_attributes(field.hir_id, field.span, Target::PatField, None); - } - } - intravisit::walk_pat(self, p); + fn visit_pat_field(&mut self, field: &'tcx hir::PatField<'tcx>) { + self.check_attributes(field.hir_id, field.span, Target::PatField, None); + intravisit::walk_pat_field(self, field); } } diff --git a/src/test/ui/lint/unused/unused_attributes-must_use.stderr b/src/test/ui/lint/unused/unused_attributes-must_use.stderr index 69cd5302c5c34..dd112c23e5d9e 100644 --- a/src/test/ui/lint/unused/unused_attributes-must_use.stderr +++ b/src/test/ui/lint/unused/unused_attributes-must_use.stderr @@ -105,7 +105,7 @@ error: `#[must_use]` has no effect when applied to an match arm LL | #[must_use] | ^^^^^^^^^^^ -error: `#[must_use]` has no effect when applied to a pattern field +error: `#[must_use]` has no effect when applied to a struct field --> $DIR/unused_attributes-must_use.rs:129:28 | LL | let s = PatternField { #[must_use] foo: 123 }; From 7b36047239f50df16e6dbd1ab0ca8575e7db1a55 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 5 Jul 2022 17:42:39 -0700 Subject: [PATCH 5/8] Make Node::ExprField a child of Node::Expr. This was incorrectly inserting the ExprField as a sibling of the struct expression. This required adjusting various parts which were looking at parent node of a field expression to find the struct. --- compiler/rustc_ast_lowering/src/index.rs | 12 +- compiler/rustc_lint/src/types.rs | 98 +++++++------ compiler/rustc_typeck/src/check/demand.rs | 25 ++-- .../ui/lint/lint-attr-everywhere-early.stderr | 20 +-- .../ui/lint/lint-attr-everywhere-late.stderr | 132 +++++++++--------- 5 files changed, 139 insertions(+), 148 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index 899e0dd5cc3b9..2b7431f099057 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -226,17 +226,19 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { fn visit_expr(&mut self, expr: &'hir Expr<'hir>) { self.insert(expr.span, expr.hir_id, Node::Expr(expr)); - if let ExprKind::Struct(_, fields, _) = expr.kind { - for field in fields { - self.insert(field.span, field.hir_id, Node::ExprField(field)); - } - } self.with_parent(expr.hir_id, |this| { intravisit::walk_expr(this, expr); }); } + fn visit_expr_field(&mut self, field: &'hir ExprField<'hir>) { + self.insert(field.span, field.hir_id, Node::ExprField(field)); + self.with_parent(field.hir_id, |this| { + intravisit::walk_expr_field(this, field); + }); + } + fn visit_stmt(&mut self, stmt: &'hir Stmt<'hir>) { self.insert(stmt.span, stmt.hir_id, Node::Stmt(stmt)); diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 5c07afeb7aa8f..cafd2c6e67994 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -125,45 +125,51 @@ fn lint_overflowing_range_endpoint<'tcx>( lit_val: u128, max: u128, expr: &'tcx hir::Expr<'tcx>, - parent_expr: &'tcx hir::Expr<'tcx>, ty: &str, ) -> bool { // We only want to handle exclusive (`..`) ranges, // which are represented as `ExprKind::Struct`. + let par_id = cx.tcx.hir().get_parent_node(expr.hir_id); + let Node::ExprField(field) = cx.tcx.hir().get(par_id) else { return false }; + let field_par_id = cx.tcx.hir().get_parent_node(field.hir_id); + let Node::Expr(struct_expr) = cx.tcx.hir().get(field_par_id) else { return false }; + if !is_range_literal(struct_expr) { + return false; + }; + let ExprKind::Struct(_, eps, _) = &struct_expr.kind else { return false }; + if eps.len() != 2 { + return false; + } + let mut overwritten = false; - if let ExprKind::Struct(_, eps, _) = &parent_expr.kind { - if eps.len() != 2 { - return false; - } - // We can suggest using an inclusive range - // (`..=`) instead only if it is the `end` that is - // overflowing and only by 1. - if eps[1].expr.hir_id == expr.hir_id && lit_val - 1 == max { - cx.struct_span_lint(OVERFLOWING_LITERALS, parent_expr.span, |lint| { - let mut err = lint.build(fluent::lint::range_endpoint_out_of_range); - err.set_arg("ty", ty); - if let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) { - use ast::{LitIntType, LitKind}; - // We need to preserve the literal's suffix, - // as it may determine typing information. - let suffix = match lit.node { - LitKind::Int(_, LitIntType::Signed(s)) => s.name_str(), - LitKind::Int(_, LitIntType::Unsigned(s)) => s.name_str(), - LitKind::Int(_, LitIntType::Unsuffixed) => "", - _ => bug!(), - }; - let suggestion = format!("{}..={}{}", start, lit_val - 1, suffix); - err.span_suggestion( - parent_expr.span, - fluent::lint::suggestion, - suggestion, - Applicability::MachineApplicable, - ); - err.emit(); - overwritten = true; - } - }); - } + // We can suggest using an inclusive range + // (`..=`) instead only if it is the `end` that is + // overflowing and only by 1. + if eps[1].expr.hir_id == expr.hir_id && lit_val - 1 == max { + cx.struct_span_lint(OVERFLOWING_LITERALS, struct_expr.span, |lint| { + let mut err = lint.build(fluent::lint::range_endpoint_out_of_range); + err.set_arg("ty", ty); + if let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) { + use ast::{LitIntType, LitKind}; + // We need to preserve the literal's suffix, + // as it may determine typing information. + let suffix = match lit.node { + LitKind::Int(_, LitIntType::Signed(s)) => s.name_str(), + LitKind::Int(_, LitIntType::Unsigned(s)) => s.name_str(), + LitKind::Int(_, LitIntType::Unsuffixed) => "", + _ => bug!(), + }; + let suggestion = format!("{}..={}{}", start, lit_val - 1, suffix); + err.span_suggestion( + struct_expr.span, + fluent::lint::suggestion, + suggestion, + Applicability::MachineApplicable, + ); + err.emit(); + overwritten = true; + } + }); } overwritten } @@ -339,16 +345,9 @@ fn lint_int_literal<'tcx>( return; } - let par_id = cx.tcx.hir().get_parent_node(e.hir_id); - if let Node::Expr(par_e) = cx.tcx.hir().get(par_id) { - if let hir::ExprKind::Struct(..) = par_e.kind { - if is_range_literal(par_e) - && lint_overflowing_range_endpoint(cx, lit, v, max, e, par_e, t.name_str()) - { - // The overflowing literal lint was overridden. - return; - } - } + if lint_overflowing_range_endpoint(cx, lit, v, max, e, t.name_str()) { + // The overflowing literal lint was overridden. + return; } cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, |lint| { @@ -408,16 +407,13 @@ fn lint_uint_literal<'tcx>( return; } } - hir::ExprKind::Struct(..) if is_range_literal(par_e) => { - let t = t.name_str(); - if lint_overflowing_range_endpoint(cx, lit, lit_val, max, e, par_e, t) { - // The overflowing literal lint was overridden. - return; - } - } _ => {} } } + if lint_overflowing_range_endpoint(cx, lit, lit_val, max, e, t.name_str()) { + // The overflowing literal lint was overridden. + return; + } if let Some(repr_str) = get_bin_hex_repr(cx, lit) { report_bin_hex_error( cx, diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index bac0be44aa9a2..07046f3f0326b 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -638,11 +638,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }?; match hir.find(hir.get_parent_node(expr.hir_id))? { - Node::Expr(hir::Expr { kind: hir::ExprKind::Struct(_, fields, ..), .. }) => { - for field in *fields { - if field.ident.name == local.name && field.is_shorthand { - return Some(local.name); - } + Node::ExprField(field) => { + if field.ident.name == local.name && field.is_shorthand { + return Some(local.name); } } _ => {} @@ -1073,21 +1071,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut sugg = vec![]; - if let Some(hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Struct(_, fields, _), .. - })) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id)) + if let Some(hir::Node::ExprField(field)) = + self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id)) { // `expr` is a literal field for a struct, only suggest if appropriate - match (*fields) - .iter() - .find(|field| field.expr.hir_id == expr.hir_id && field.is_shorthand) - { + if field.is_shorthand { // This is a field literal - Some(field) => { - sugg.push((field.ident.span.shrink_to_lo(), format!("{}: ", field.ident))); - } + sugg.push((field.ident.span.shrink_to_lo(), format!("{}: ", field.ident))); + } else { // Likely a field was meant, but this field wasn't found. Do not suggest anything. - None => return false, + return false; } }; diff --git a/src/test/ui/lint/lint-attr-everywhere-early.stderr b/src/test/ui/lint/lint-attr-everywhere-early.stderr index f65d811105fab..9b54b033c88af 100644 --- a/src/test/ui/lint/lint-attr-everywhere-early.stderr +++ b/src/test/ui/lint/lint-attr-everywhere-early.stderr @@ -25,7 +25,7 @@ help: remove these parentheses | LL - type BareFnPtr = fn(#[deny(unused_parens)](i32)); LL + type BareFnPtr = fn(#[deny(unused_parens)]i32); - | + | error: type `ITEM_OUTER` should have an upper camel case name --> $DIR/lint-attr-everywhere-early.rs:30:8 @@ -198,7 +198,7 @@ help: remove these parentheses | LL - type assoc_type = (i32); LL + type assoc_type = i32; - | + | error: unnecessary parentheses around type --> $DIR/lint-attr-everywhere-early.rs:87:31 @@ -215,7 +215,7 @@ help: remove these parentheses | LL - #[deny(unused_parens)]f1: (i32), LL + #[deny(unused_parens)]f1: i32, - | + | error: unnecessary parentheses around type --> $DIR/lint-attr-everywhere-early.rs:89:42 @@ -232,7 +232,7 @@ help: remove these parentheses | LL - struct StructTuple(#[deny(unused_parens)](i32)); LL + struct StructTuple(#[deny(unused_parens)]i32); - | + | error: variant `VARIANT_CAMEL` should have an upper camel case name --> $DIR/lint-attr-everywhere-early.rs:93:5 @@ -261,7 +261,7 @@ help: remove these parentheses | LL - fn foreign_denied_from_inner(x: (i32)); LL + fn foreign_denied_from_inner(x: i32); - | + | error: unnecessary parentheses around type --> $DIR/lint-attr-everywhere-early.rs:104:37 @@ -278,7 +278,7 @@ help: remove these parentheses | LL - fn foreign_denied_from_outer(x: (i32)); LL + fn foreign_denied_from_outer(x: i32); - | + | error: unnecessary parentheses around type --> $DIR/lint-attr-everywhere-early.rs:107:43 @@ -295,7 +295,7 @@ help: remove these parentheses | LL - fn function(#[deny(unused_parens)] param: (i32)) {} LL + fn function(#[deny(unused_parens)] param: i32) {} - | + | error: type parameter `foo` should have an upper camel case name --> $DIR/lint-attr-everywhere-early.rs:109:42 @@ -324,7 +324,7 @@ help: remove these parentheses | LL - let x = (1); LL + let x = 1; - | + | error: unnecessary parentheses around type --> $DIR/lint-attr-everywhere-early.rs:121:50 @@ -341,7 +341,7 @@ help: remove these parentheses | LL - let closure = |#[deny(unused_parens)] param: (i32)| {}; LL + let closure = |#[deny(unused_parens)] param: i32| {}; - | + | error: unnecessary parentheses around block return value --> $DIR/lint-attr-everywhere-early.rs:125:46 @@ -358,7 +358,7 @@ help: remove these parentheses | LL - let f = Match{#[deny(unused_parens)]f1: {(123)}}; LL + let f = Match{#[deny(unused_parens)]f1: {123}}; - | + | error: usage of an `unsafe` block --> $DIR/lint-attr-everywhere-early.rs:132:13 diff --git a/src/test/ui/lint/lint-attr-everywhere-late.stderr b/src/test/ui/lint/lint-attr-everywhere-late.stderr index d302798d65007..977843997c6dc 100644 --- a/src/test/ui/lint/lint-attr-everywhere-late.stderr +++ b/src/test/ui/lint/lint-attr-everywhere-late.stderr @@ -142,35 +142,6 @@ note: the lint level is defined here LL | #[deny(missing_docs)] | ^^^^^^^^^^^^ -error: types that do not implement `Drop` can still have drop glue, consider instead using `std::mem::needs_drop` to detect whether a type is trivially dropped - --> $DIR/lint-attr-everywhere-late.rs:93:38 - | -LL | fn denied_from_inner(_x: Box) {} - | ^^^^ - | -note: the lint level is defined here - --> $DIR/lint-attr-everywhere-late.rs:91:13 - | -LL | #![deny(dyn_drop)] - | ^^^^^^^^ - -error: the return value of `mem::discriminant` is unspecified when called with a non-enum type - --> $DIR/lint-attr-everywhere-late.rs:96:21 - | -LL | fn assoc_fn() { discriminant::(&123); } - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: the lint level is defined here - --> $DIR/lint-attr-everywhere-late.rs:95:12 - | -LL | #[deny(enum_intrinsics_non_enums)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `i32`, which is not an enum. - --> $DIR/lint-attr-everywhere-late.rs:96:41 - | -LL | fn assoc_fn() { discriminant::(&123); } - | ^^^^ - error: missing documentation for a variant --> $DIR/lint-attr-everywhere-late.rs:112:5 | @@ -217,6 +188,60 @@ LL | #[deny(clashing_extern_declarations)] = note: expected `unsafe extern "C" fn()` found `unsafe extern "C" fn(i32)` +error: types that do not implement `Drop` can still have drop glue, consider instead using `std::mem::needs_drop` to detect whether a type is trivially dropped + --> $DIR/lint-attr-everywhere-late.rs:93:38 + | +LL | fn denied_from_inner(_x: Box) {} + | ^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:91:13 + | +LL | #![deny(dyn_drop)] + | ^^^^^^^^ + +error: the return value of `mem::discriminant` is unspecified when called with a non-enum type + --> $DIR/lint-attr-everywhere-late.rs:96:21 + | +LL | fn assoc_fn() { discriminant::(&123); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:95:12 + | +LL | #[deny(enum_intrinsics_non_enums)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `i32`, which is not an enum. + --> $DIR/lint-attr-everywhere-late.rs:96:41 + | +LL | fn assoc_fn() { discriminant::(&123); } + | ^^^^ + +error: literal out of range for `u8` + --> $DIR/lint-attr-everywhere-late.rs:98:59 + | +LL | #[deny(overflowing_literals)] const ASSOC_CONST: u8 = 1000; + | ^^^^ + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:98:12 + | +LL | #[deny(overflowing_literals)] const ASSOC_CONST: u8 = 1000; + | ^^^^^^^^^^^^^^^^^^^^ + = note: the literal `1000` does not fit into the type `u8` whose range is `0..=255` + +error: variable `PARAM` should have a snake case name + --> $DIR/lint-attr-everywhere-late.rs:131:37 + | +LL | fn function(#[deny(non_snake_case)] PARAM: i32) {} + | ^^^^^ help: convert the identifier to snake case: `param` + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:131:20 + | +LL | fn function(#[deny(non_snake_case)] PARAM: i32) {} + | ^^^^^^^^^^^^^^ + error: the return value of `mem::discriminant` is unspecified when called with a non-enum type --> $DIR/lint-attr-everywhere-late.rs:139:13 | @@ -234,6 +259,18 @@ note: the argument to `discriminant` should be a reference to an enum, but it wa LL | let _ = discriminant::(&123); | ^^^^ +error: variable `PARAM` should have a snake case name + --> $DIR/lint-attr-everywhere-late.rs:145:44 + | +LL | let closure = |#[deny(non_snake_case)] PARAM: i32| {}; + | ^^^^^ help: convert the identifier to snake case: `param` + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-late.rs:145:27 + | +LL | let closure = |#[deny(non_snake_case)] PARAM: i32| {}; + | ^^^^^^^^^^^^^^ + error: the return value of `mem::discriminant` is unspecified when called with a non-enum type --> $DIR/lint-attr-everywhere-late.rs:155:13 | @@ -387,42 +424,5 @@ note: the argument to `discriminant` should be a reference to an enum, but it wa LL | TupleStruct(#[deny(enum_intrinsics_non_enums)] discriminant::(&123)); | ^^^^ -error: literal out of range for `u8` - --> $DIR/lint-attr-everywhere-late.rs:98:59 - | -LL | #[deny(overflowing_literals)] const ASSOC_CONST: u8 = 1000; - | ^^^^ - | -note: the lint level is defined here - --> $DIR/lint-attr-everywhere-late.rs:98:12 - | -LL | #[deny(overflowing_literals)] const ASSOC_CONST: u8 = 1000; - | ^^^^^^^^^^^^^^^^^^^^ - = note: the literal `1000` does not fit into the type `u8` whose range is `0..=255` - -error: variable `PARAM` should have a snake case name - --> $DIR/lint-attr-everywhere-late.rs:131:37 - | -LL | fn function(#[deny(non_snake_case)] PARAM: i32) {} - | ^^^^^ help: convert the identifier to snake case: `param` - | -note: the lint level is defined here - --> $DIR/lint-attr-everywhere-late.rs:131:20 - | -LL | fn function(#[deny(non_snake_case)] PARAM: i32) {} - | ^^^^^^^^^^^^^^ - -error: variable `PARAM` should have a snake case name - --> $DIR/lint-attr-everywhere-late.rs:145:44 - | -LL | let closure = |#[deny(non_snake_case)] PARAM: i32| {}; - | ^^^^^ help: convert the identifier to snake case: `param` - | -note: the lint level is defined here - --> $DIR/lint-attr-everywhere-late.rs:145:27 - | -LL | let closure = |#[deny(non_snake_case)] PARAM: i32| {}; - | ^^^^^^^^^^^^^^ - error: aborting due to 31 previous errors From c655f17bcec83e8c2c2577cb99caa26385329956 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 5 Jul 2022 18:27:21 -0700 Subject: [PATCH 6/8] Add missing visit_pat_field in early lint visitor. This ensures that lint attributes on pattern fields can control early lints. --- compiler/rustc_lint/src/early.rs | 6 ++++++ src/test/ui/lint/lint-attr-everywhere-early.rs | 17 ++++++++++++----- .../ui/lint/lint-attr-everywhere-early.stderr | 16 +++++++++++++++- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index d94c5d034a653..580a4566869f0 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -101,6 +101,12 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> run_early_pass!(self, check_pat_post, p); } + fn visit_pat_field(&mut self, field: &'a ast::PatField) { + self.with_lint_attrs(field.id, &field.attrs, |cx| { + ast_visit::walk_pat_field(cx, field); + }); + } + fn visit_anon_const(&mut self, c: &'a ast::AnonConst) { self.check_id(c.id); ast_visit::walk_anon_const(self, c); diff --git a/src/test/ui/lint/lint-attr-everywhere-early.rs b/src/test/ui/lint/lint-attr-everywhere-early.rs index 9ac0e45c6da0c..fd0c4b43e05c6 100644 --- a/src/test/ui/lint/lint-attr-everywhere-early.rs +++ b/src/test/ui/lint/lint-attr-everywhere-early.rs @@ -159,11 +159,18 @@ fn expressions() { // ################## Patterns fn patterns() { - // There aren't any early lints that I can find that apply to pattern fields. - // - // struct PatField{f1: i32, f2: i32}; - // let f = PatField{f1: 1, f2: 2}; - // let PatField{#[deny()]f1, #[deny()]..} = f; + struct PatField{f1: i32, f2: i32}; + let f = PatField{f1: 1, f2: 2}; + match f { + PatField { + #[deny(ellipsis_inclusive_range_patterns)] + f1: 0...100, + //~^ ERROR range patterns are deprecated + //~| WARNING this is accepted in the current edition + .. + } => {} + _ => {} + } } fn main() {} diff --git a/src/test/ui/lint/lint-attr-everywhere-early.stderr b/src/test/ui/lint/lint-attr-everywhere-early.stderr index 9b54b033c88af..1d6e3cda4e533 100644 --- a/src/test/ui/lint/lint-attr-everywhere-early.stderr +++ b/src/test/ui/lint/lint-attr-everywhere-early.stderr @@ -468,5 +468,19 @@ note: the lint level is defined here LL | TupleStruct(#[deny(unsafe_code)] unsafe {123}); | ^^^^^^^^^^^ -error: aborting due to 35 previous errors +error: `...` range patterns are deprecated + --> $DIR/lint-attr-everywhere-early.rs:167:18 + | +LL | f1: 0...100, + | ^^^ help: use `..=` for an inclusive range + | +note: the lint level is defined here + --> $DIR/lint-attr-everywhere-early.rs:166:20 + | +LL | #[deny(ellipsis_inclusive_range_patterns)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = note: for more information, see + +error: aborting due to 36 previous errors From 1c70b8669ad728313049dd5548ab892f8e21cde7 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 30 Jul 2022 19:30:15 -0700 Subject: [PATCH 7/8] Fix diagnostic that was looking for a PatKind::Struct Now that fields are first-class HIR nodes, they appear before the struct pat. --- compiler/rustc_typeck/src/check/pat.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index a13c715258207..6dcc364bb50f7 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -927,7 +927,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), ); match self.tcx.hir().get(self.tcx.hir().get_parent_node(pat.hir_id)) { - hir::Node::Pat(Pat { kind: hir::PatKind::Struct(..), .. }) => { + hir::Node::PatField(..) => { e.span_suggestion_verbose( ident.span.shrink_to_hi(), "bind the struct field to a different name instead", From 900a9d3f4e80315b1fccd909336febede92503ed Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sun, 31 Jul 2022 14:22:27 -0700 Subject: [PATCH 8/8] Update clippy for introduction of Node::ExprField --- .../clippy/clippy_lints/src/dereference.rs | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 5d41c63928dfb..59f10247a11d4 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::ty::{expr_sig, peel_mid_ty_refs, ty_sig, variant_of_res}; -use clippy_utils::{get_parent_expr, is_lint_allowed, path_to_local, walk_to_expr_usage}; +use clippy_utils::{get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, path_to_local, walk_to_expr_usage}; use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; @@ -699,6 +699,19 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, & Some(ty_auto_deref_stability(cx, output, precedence).position_for_result(cx)) }, + Node::ExprField(field) if field.span.ctxt() == ctxt => match get_parent_expr_for_hir(cx, field.hir_id) { + Some(Expr { + hir_id, + kind: ExprKind::Struct(path, ..), + .. + }) => variant_of_res(cx, cx.qpath_res(path, *hir_id)) + .and_then(|variant| variant.fields.iter().find(|f| f.name == field.ident.name)) + .map(|field_def| { + ty_auto_deref_stability(cx, cx.tcx.type_of(field_def.did), precedence).position_for_arg() + }), + _ => None, + }, + Node::Expr(parent) if parent.span.ctxt() == ctxt => match parent.kind { ExprKind::Ret(_) => { let owner_id = cx.tcx.hir().body_owner(cx.enclosing_body.unwrap()); @@ -788,17 +801,6 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, & } }) }, - ExprKind::Struct(path, fields, _) => { - let variant = variant_of_res(cx, cx.qpath_res(path, parent.hir_id)); - fields - .iter() - .find(|f| f.expr.hir_id == child_id) - .zip(variant) - .and_then(|(field, variant)| variant.fields.iter().find(|f| f.name == field.ident.name)) - .map(|field| { - ty_auto_deref_stability(cx, cx.tcx.type_of(field.did), precedence).position_for_arg() - }) - }, ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(Position::FieldAccess(name.name)), ExprKind::Unary(UnOp::Deref, child) if child.hir_id == e.hir_id => Some(Position::Deref), ExprKind::Match(child, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)