Auto merge of #96745 - ehuss:even-more-attribute-validation, r=cjgillot

Visit attributes in more places.

This adds 3 loosely related changes (I can split PRs if desired):

- Attribute checking on pattern struct fields.
- Attribute checking on struct expression fields.
- Lint level visiting on pattern struct fields, struct expression fields, and generic parameters.

There are still some lints which ignore lint levels in various positions. This is a consequence of how the lints themselves are implemented. For example, lint levels on associated consts don't work with `unused_braces`.
This commit is contained in:
bors 2022-08-15 05:50:54 +00:00
commit 6ce76091c7
23 changed files with 1520 additions and 141 deletions

View file

@ -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),

View file

@ -199,6 +199,13 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
});
}
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);
@ -225,6 +232,13 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
});
}
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));

View file

@ -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);
}

View file

@ -3332,12 +3332,14 @@ 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>),
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 +3390,8 @@ impl<'hir> Node<'hir> {
| Node::Block(..)
| Node::Ctor(..)
| Node::Pat(..)
| Node::PatField(..)
| Node::ExprField(..)
| Node::Arm(..)
| Node::Local(..)
| Node::Crate(..)

View file

@ -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) => {

View file

@ -56,6 +56,8 @@ pub enum Target {
GenericParam(GenericParamKind),
MacroDef,
Param,
PatField,
ExprField,
}
impl Display for Target {
@ -183,6 +185,8 @@ impl Target {
},
Target::MacroDef => "macro def",
Target::Param => "function param",
Target::PatField => "pattern field",
Target::ExprField => "struct field",
}
}
}

View file

@ -83,12 +83,14 @@ 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),
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) => {
@ -1127,20 +1129,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() {
@ -1157,6 +1146,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);
@ -1803,20 +1806,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(",");
@ -1911,6 +1901,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);

View file

@ -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);
@ -219,9 +225,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, &param.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) {

View file

@ -766,6 +766,12 @@ impl<'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'tcx> {
})
}
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>) {
self.with_lint_attrs(s.hir_id, |builder| {
intravisit::walk_field_def(builder, s);
@ -801,6 +807,18 @@ impl<'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'tcx> {
intravisit::walk_impl_item(builder, impl_item);
});
}
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>) {
self.with_lint_attrs(p.hir_id, |builder| {
intravisit::walk_generic_param(builder, p);
});
}
}
pub fn provide(providers: &mut Providers) {

View file

@ -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);
}

View file

@ -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,

View file

@ -297,6 +297,8 @@ impl<'hir> Map<'hir> {
| Node::Infer(_)
| Node::TraitRef(_)
| Node::Pat(_)
| Node::PatField(_)
| Node::ExprField(_)
| Node::Local(_)
| Node::Param(_)
| Node::Arm(_)
@ -1020,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;
@ -1030,6 +1033,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)),
@ -1241,12 +1245,14 @@ 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"),
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"),

View file

@ -652,7 +652,9 @@ impl CheckAttrVisitor<'_> {
| Target::ForeignStatic
| Target::ForeignTy
| Target::GenericParam(..)
| Target::MacroDef => None,
| Target::MacroDef
| Target::PatField
| Target::ExprField => None,
} {
tcx.sess.emit_err(errors::DocAliasBadLocation { span, attr_str, location });
return false;
@ -2066,6 +2068,11 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
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)
@ -2076,6 +2083,11 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
intravisit::walk_param(self, param);
}
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);
}
}
fn is_c_like_enum(item: &Item<'_>) -> bool {

View file

@ -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;
}
};

View file

@ -256,6 +256,8 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
| hir::Node::TypeBinding(..)
| hir::Node::TraitRef(..)
| hir::Node::Pat(..)
| hir::Node::PatField(..)
| hir::Node::ExprField(..)
| hir::Node::Arm(..)
| hir::Node::Local(..)
| hir::Node::Ctor(..)

View file

@ -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",

View file

@ -0,0 +1,176 @@
// 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<foo>() {} // 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() {
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() {}

View file

@ -0,0 +1,486 @@
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: `...` 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 <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html>
error: aborting due to 36 previous errors

View file

@ -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>) {} // dyn_drop
fn verify_no_warnings() {
discriminant::<i32>(&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<dyn Drop>) {} // 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<dyn Drop>) {} //~ ERROR types that do not implement `Drop`
#[deny(enum_intrinsics_non_enums)]
fn assoc_fn() { discriminant::<i32>(&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::<i32>(&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::<i32>(&123); //~ ERROR the return value of
}
}
match f {
#[deny(enum_intrinsics_non_enums)]
Match{f1} => {
discriminant::<i32>(&123); //~ ERROR the return value of
}
}
// Statement Block
{
#![deny(enum_intrinsics_non_enums)]
discriminant::<i32>(&123); //~ ERROR the return value of
}
let block_tail = {
#[deny(enum_intrinsics_non_enums)]
discriminant::<i32>(&123); //~ ERROR the return value of
};
// Before expression as a statement.
#[deny(enum_intrinsics_non_enums)]
discriminant::<i32>(&123); //~ ERROR the return value of
[#[deny(enum_intrinsics_non_enums)] discriminant::<i32>(&123)]; //~ ERROR the return value of
(#[deny(enum_intrinsics_non_enums)] discriminant::<i32>(&123),); //~ ERROR the return value of
fn call(p: Discriminant<i32>) {}
call(#[deny(enum_intrinsics_non_enums)] discriminant::<i32>(&123)); //~ ERROR the return value of
struct TupleStruct(Discriminant<i32>);
TupleStruct(#[deny(enum_intrinsics_non_enums)] discriminant::<i32>(&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() {}

View file

@ -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: 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: 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<dyn Drop>) {}
| ^^^^
|
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::<i32>(&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::<i32>(&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
|
LL | let _ = discriminant::<i32>(&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::<i32>(&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
|
LL | discriminant::<i32>(&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::<i32>(&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::<i32>(&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::<i32>(&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::<i32>(&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::<i32>(&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::<i32>(&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::<i32>(&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::<i32>(&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::<i32>(&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::<i32>(&123)];
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/lint-attr-everywhere-late.rs:179:13
|
LL | [#[deny(enum_intrinsics_non_enums)] discriminant::<i32>(&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::<i32>(&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::<i32>(&123),);
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/lint-attr-everywhere-late.rs:180:13
|
LL | (#[deny(enum_intrinsics_non_enums)] discriminant::<i32>(&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::<i32>(&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::<i32>(&123));
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/lint-attr-everywhere-late.rs:182:17
|
LL | call(#[deny(enum_intrinsics_non_enums)] discriminant::<i32>(&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::<i32>(&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::<i32>(&123));
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/lint-attr-everywhere-late.rs:184:24
|
LL | TupleStruct(#[deny(enum_intrinsics_non_enums)] discriminant::<i32>(&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::<i32>(&123));
| ^^^^
error: aborting due to 31 previous errors

View file

@ -122,4 +122,10 @@ fn main() {
Some(res) => res,
None => 0,
};
struct PatternField {
foo: i32,
}
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
}

View file

@ -105,6 +105,18 @@ 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 struct 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
|
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 +183,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 28 previous errors

View file

@ -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)