-use crate::utils::{snippet_opt, span_lint_and_then};
+use clippy_utils::attrs::is_doc_hidden;
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::{meets_msrv, msrvs};
use if_chain::if_chain;
-use rustc_ast::ast::{Attribute, Item, ItemKind, StructField, Variant, VariantData, VisibilityKind};
-use rustc_attr as attr;
+use rustc_ast::ast::{FieldDef, Item, ItemKind, Variant, VariantData, VisibilityKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::Span;
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::{sym, Span};
declare_clippy_lint! {
/// **What it does:** Checks for manual implementations of the non-exhaustive pattern.
"manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]"
}
-declare_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]);
+#[derive(Clone)]
+pub struct ManualNonExhaustive {
+ msrv: Option<RustcVersion>,
+}
+
+impl ManualNonExhaustive {
+ #[must_use]
+ pub fn new(msrv: Option<RustcVersion>) -> Self {
+ Self { msrv }
+ }
+}
+
+impl_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]);
impl EarlyLintPass for ManualNonExhaustive {
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
+ if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) {
+ return;
+ }
+
match &item.kind {
ItemKind::Enum(def, _) => {
check_manual_non_exhaustive_enum(cx, item, &def.variants);
_ => {},
}
}
+
+ extract_msrv_attr!(EarlyContext);
}
fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants: &[Variant]) {
fn is_non_exhaustive_marker(variant: &Variant) -> bool {
matches!(variant.data, VariantData::Unit(_))
&& variant.ident.as_str().starts_with('_')
- && variant.attrs.iter().any(|a| is_doc_hidden(a))
- }
-
- fn is_doc_hidden(attr: &Attribute) -> bool {
- attr.has_name(sym!(doc))
- && match attr.meta_item_list() {
- Some(l) => attr::list_contains_name(&l, sym!(hidden)),
- None => false,
- }
+ && is_doc_hidden(&variant.attrs)
}
+ let mut markers = variants.iter().filter(|v| is_non_exhaustive_marker(v));
if_chain! {
- let mut markers = variants.iter().filter(|v| is_non_exhaustive_marker(v));
if let Some(marker) = markers.next();
if markers.count() == 0 && variants.len() > 1;
then {
"this seems like a manual implementation of the non-exhaustive pattern",
|diag| {
if_chain! {
- if !item.attrs.iter().any(|attr| attr.has_name(sym!(non_exhaustive)));
+ if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive));
let header_span = cx.sess.source_map().span_until_char(item.span, '{');
if let Some(snippet) = snippet_opt(cx, header_span);
then {
}
fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) {
- fn is_private(field: &StructField) -> bool {
- matches!(field.vis.node, VisibilityKind::Inherited)
+ fn is_private(field: &FieldDef) -> bool {
+ matches!(field.vis.kind, VisibilityKind::Inherited)
}
- fn is_non_exhaustive_marker(field: &StructField) -> bool {
+ fn is_non_exhaustive_marker(field: &FieldDef) -> bool {
is_private(field) && field.ty.kind.is_unit() && field.ident.map_or(true, |n| n.as_str().starts_with('_'))
}
let fields = data.fields();
let private_fields = fields.iter().filter(|f| is_private(f)).count();
- let public_fields = fields.iter().filter(|f| f.vis.node.is_pub()).count();
+ let public_fields = fields.iter().filter(|f| f.vis.kind.is_pub()).count();
if_chain! {
if private_fields == 1 && public_fields >= 1 && public_fields == fields.len() - 1;
"this seems like a manual implementation of the non-exhaustive pattern",
|diag| {
if_chain! {
- if !item.attrs.iter().any(|attr| attr.has_name(sym!(non_exhaustive)));
+ if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive));
let header_span = find_header_span(cx, item, data);
if let Some(snippet) = snippet_opt(cx, header_span);
then {