about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-11-11 12:38:04 +0000
committerbors <bors@rust-lang.org>2021-11-11 12:38:04 +0000
commit3bfe98d372fc4dacd12a7f0c06d44d72e2e96cd2 (patch)
tree2f390f1637b1797ed060c1c65970811e42456a7f
parent8389df9db7833c73c9bfd5445718260d1d5e9842 (diff)
parent8c45fd88d0ca43775b215e1b94b2175e8f4c05d2 (diff)
downloadrust-3bfe98d372fc4dacd12a7f0c06d44d72e2e96cd2.tar.gz
rust-3bfe98d372fc4dacd12a7f0c06d44d72e2e96cd2.zip
Auto merge of #7813 - xFrednet:6492-lint-version, r=flip1995
Add Clippy version to Clippy's lint list

Hey, hey, the semester is finally over, and I wanted to get back into hacking on Clippy. It has also been some time since our metadata collection monster has been feed. So, this PR adds a new attribute `clippy::version` to document which version a lint was stabilized. I considered using `git blame` but that would be very hacky and probably not accurate.

I'm also thinking that this attribute can be used to have a `clippy::nightly` lint group which is allow-by-default that delays setting the actual lint group until the defined version is reached. Just something to consider regarding #6623 :upside_down_face:

This PR only adds the version to 4 lints to keep it reviewable. I'll do a followup PR to add the version to other lints if the implementation is accepted :upside_down_face:

![image](https://user-images.githubusercontent.com/17087237/137118859-0aafdfdf-7595-4289-8ba4-33d58eb6991d.png)

Also, mobile approved xD

![image](https://user-images.githubusercontent.com/17087237/137118944-833cf7fb-a4a1-45d6-9af8-32c951822360.png)

---

r? `@flip1995`

cc: #7172

closes: #6492

changelog: [Clippy's lint list](https://rust-lang.github.io/rust-clippy/master/index.html) now displays the version a lint was added. :tada:

---

Example lint declaration after this update:

```rs
declare_clippy_lint! {
    /// [...]
    ///
    /// ### Example
    /// ```rust
    /// // Bad
    /// let x = 3.14;
    /// // Good
    /// let x = std::f32::consts::PI;
    /// ```
    #[clippy::version = "pre 1.29.0"]
    pub APPROX_CONSTANT,
    correctness,
    "the approximate of a known float constant (in `std::fXX::consts`)"
}
```
-rw-r--r--clippy_dev/Cargo.toml1
-rw-r--r--clippy_dev/src/new_lint.rs21
-rw-r--r--clippy_dev/src/update_lints.rs5
-rw-r--r--clippy_lints/src/absurd_extreme_comparisons.rs1
-rw-r--r--clippy_lints/src/approx_const.rs1
-rw-r--r--clippy_lints/src/arithmetic.rs2
-rw-r--r--clippy_lints/src/as_conversions.rs1
-rw-r--r--clippy_lints/src/asm_syntax.rs2
-rw-r--r--clippy_lints/src/assertions_on_constants.rs1
-rw-r--r--clippy_lints/src/assign_ops.rs2
-rw-r--r--clippy_lints/src/async_yields_async.rs1
-rw-r--r--clippy_lints/src/attrs.rs7
-rw-r--r--clippy_lints/src/await_holding_invalid.rs2
-rw-r--r--clippy_lints/src/bit_mask.rs3
-rw-r--r--clippy_lints/src/blacklisted_name.rs1
-rw-r--r--clippy_lints/src/blocks_in_if_conditions.rs1
-rw-r--r--clippy_lints/src/bool_assert_comparison.rs1
-rw-r--r--clippy_lints/src/booleans.rs2
-rw-r--r--clippy_lints/src/bytecount.rs1
-rw-r--r--clippy_lints/src/cargo_common_metadata.rs1
-rw-r--r--clippy_lints/src/case_sensitive_file_extension_comparisons.rs1
-rw-r--r--clippy_lints/src/casts/mod.rs13
-rw-r--r--clippy_lints/src/checked_conversions.rs1
-rw-r--r--clippy_lints/src/cognitive_complexity.rs1
-rw-r--r--clippy_lints/src/collapsible_if.rs2
-rw-r--r--clippy_lints/src/collapsible_match.rs1
-rw-r--r--clippy_lints/src/comparison_chain.rs1
-rw-r--r--clippy_lints/src/copies.rs4
-rw-r--r--clippy_lints/src/copy_iterator.rs1
-rw-r--r--clippy_lints/src/create_dir.rs1
-rw-r--r--clippy_lints/src/dbg_macro.rs1
-rw-r--r--clippy_lints/src/default.rs2
-rw-r--r--clippy_lints/src/default_numeric_fallback.rs1
-rw-r--r--clippy_lints/src/deprecated_lints.rs16
-rw-r--r--clippy_lints/src/dereference.rs1
-rw-r--r--clippy_lints/src/derivable_impls.rs1
-rw-r--r--clippy_lints/src/derive.rs4
-rw-r--r--clippy_lints/src/disallowed_method.rs1
-rw-r--r--clippy_lints/src/disallowed_script_idents.rs1
-rw-r--r--clippy_lints/src/disallowed_type.rs1
-rw-r--r--clippy_lints/src/doc.rs5
-rw-r--r--clippy_lints/src/double_comparison.rs1
-rw-r--r--clippy_lints/src/double_parens.rs1
-rw-r--r--clippy_lints/src/drop_forget_ref.rs4
-rw-r--r--clippy_lints/src/duration_subsec.rs1
-rw-r--r--clippy_lints/src/else_if_without_else.rs1
-rw-r--r--clippy_lints/src/empty_enum.rs1
-rw-r--r--clippy_lints/src/entry.rs1
-rw-r--r--clippy_lints/src/enum_clike.rs1
-rw-r--r--clippy_lints/src/enum_variants.rs3
-rw-r--r--clippy_lints/src/eq_op.rs2
-rw-r--r--clippy_lints/src/equatable_if_let.rs1
-rw-r--r--clippy_lints/src/erasing_op.rs1
-rw-r--r--clippy_lints/src/escape.rs1
-rw-r--r--clippy_lints/src/eta_reduction.rs2
-rw-r--r--clippy_lints/src/eval_order_dependence.rs2
-rw-r--r--clippy_lints/src/excessive_bools.rs2
-rw-r--r--clippy_lints/src/exhaustive_items.rs2
-rw-r--r--clippy_lints/src/exit.rs1
-rw-r--r--clippy_lints/src/explicit_write.rs1
-rw-r--r--clippy_lints/src/fallible_impl_from.rs1
-rw-r--r--clippy_lints/src/feature_name.rs2
-rw-r--r--clippy_lints/src/float_equality_without_abs.rs1
-rw-r--r--clippy_lints/src/float_literal.rs2
-rw-r--r--clippy_lints/src/floating_point_arithmetic.rs2
-rw-r--r--clippy_lints/src/format.rs1
-rw-r--r--clippy_lints/src/format_args.rs2
-rw-r--r--clippy_lints/src/formatting.rs4
-rw-r--r--clippy_lints/src/from_over_into.rs1
-rw-r--r--clippy_lints/src/from_str_radix_10.rs1
-rw-r--r--clippy_lints/src/functions/mod.rs7
-rw-r--r--clippy_lints/src/future_not_send.rs1
-rw-r--r--clippy_lints/src/get_last_with_len.rs1
-rw-r--r--clippy_lints/src/identity_op.rs1
-rw-r--r--clippy_lints/src/if_let_mutex.rs1
-rw-r--r--clippy_lints/src/if_not_else.rs1
-rw-r--r--clippy_lints/src/if_then_some_else_none.rs1
-rw-r--r--clippy_lints/src/implicit_hasher.rs1
-rw-r--r--clippy_lints/src/implicit_return.rs1
-rw-r--r--clippy_lints/src/implicit_saturating_sub.rs1
-rw-r--r--clippy_lints/src/inconsistent_struct_constructor.rs1
-rw-r--r--clippy_lints/src/indexing_slicing.rs2
-rw-r--r--clippy_lints/src/infinite_iter.rs2
-rw-r--r--clippy_lints/src/inherent_impl.rs1
-rw-r--r--clippy_lints/src/inherent_to_string.rs2
-rw-r--r--clippy_lints/src/inline_fn_without_body.rs1
-rw-r--r--clippy_lints/src/int_plus_one.rs1
-rw-r--r--clippy_lints/src/integer_division.rs1
-rw-r--r--clippy_lints/src/invalid_upcast_comparisons.rs1
-rw-r--r--clippy_lints/src/items_after_statements.rs1
-rw-r--r--clippy_lints/src/iter_not_returning_iterator.rs1
-rw-r--r--clippy_lints/src/large_const_arrays.rs1
-rw-r--r--clippy_lints/src/large_enum_variant.rs1
-rw-r--r--clippy_lints/src/large_stack_arrays.rs1
-rw-r--r--clippy_lints/src/len_zero.rs3
-rw-r--r--clippy_lints/src/let_if_seq.rs1
-rw-r--r--clippy_lints/src/let_underscore.rs3
-rw-r--r--clippy_lints/src/lib.register_internal.rs2
-rw-r--r--clippy_lints/src/lib.register_lints.rs4
-rw-r--r--clippy_lints/src/lib.rs4
-rw-r--r--clippy_lints/src/lifetimes.rs2
-rw-r--r--clippy_lints/src/literal_representation.rs6
-rw-r--r--clippy_lints/src/loops/mod.rs18
-rw-r--r--clippy_lints/src/macro_use.rs1
-rw-r--r--clippy_lints/src/main_recursion.rs1
-rw-r--r--clippy_lints/src/manual_assert.rs1
-rw-r--r--clippy_lints/src/manual_async_fn.rs1
-rw-r--r--clippy_lints/src/manual_map.rs1
-rw-r--r--clippy_lints/src/manual_non_exhaustive.rs1
-rw-r--r--clippy_lints/src/manual_ok_or.rs1
-rw-r--r--clippy_lints/src/manual_strip.rs1
-rw-r--r--clippy_lints/src/manual_unwrap_or.rs1
-rw-r--r--clippy_lints/src/map_clone.rs1
-rw-r--r--clippy_lints/src/map_err_ignore.rs1
-rw-r--r--clippy_lints/src/map_unit_fn.rs2
-rw-r--r--clippy_lints/src/match_on_vec_items.rs1
-rw-r--r--clippy_lints/src/match_result_ok.rs1
-rw-r--r--clippy_lints/src/match_str_case_mismatch.rs1
-rw-r--r--clippy_lints/src/matches.rs16
-rw-r--r--clippy_lints/src/mem_forget.rs1
-rw-r--r--clippy_lints/src/mem_replace.rs3
-rw-r--r--clippy_lints/src/methods/mod.rs63
-rw-r--r--clippy_lints/src/minmax.rs1
-rw-r--r--clippy_lints/src/misc.rs9
-rw-r--r--clippy_lints/src/misc_early/mod.rs10
-rw-r--r--clippy_lints/src/missing_const_for_fn.rs1
-rw-r--r--clippy_lints/src/missing_doc.rs1
-rw-r--r--clippy_lints/src/missing_enforced_import_rename.rs1
-rw-r--r--clippy_lints/src/missing_inline.rs1
-rw-r--r--clippy_lints/src/module_style.rs2
-rw-r--r--clippy_lints/src/modulo_arithmetic.rs1
-rw-r--r--clippy_lints/src/multiple_crate_versions.rs1
-rw-r--r--clippy_lints/src/mut_key.rs1
-rw-r--r--clippy_lints/src/mut_mut.rs1
-rw-r--r--clippy_lints/src/mut_mutex_lock.rs1
-rw-r--r--clippy_lints/src/mut_reference.rs1
-rw-r--r--clippy_lints/src/mutable_debug_assertion.rs1
-rw-r--r--clippy_lints/src/mutex_atomic.rs2
-rw-r--r--clippy_lints/src/needless_arbitrary_self_type.rs1
-rw-r--r--clippy_lints/src/needless_bitwise_bool.rs1
-rw-r--r--clippy_lints/src/needless_bool.rs2
-rw-r--r--clippy_lints/src/needless_borrow.rs2
-rw-r--r--clippy_lints/src/needless_borrowed_ref.rs1
-rw-r--r--clippy_lints/src/needless_continue.rs1
-rw-r--r--clippy_lints/src/needless_for_each.rs1
-rw-r--r--clippy_lints/src/needless_option_as_deref.rs1
-rw-r--r--clippy_lints/src/needless_pass_by_value.rs1
-rw-r--r--clippy_lints/src/needless_question_mark.rs1
-rw-r--r--clippy_lints/src/needless_update.rs1
-rw-r--r--clippy_lints/src/neg_cmp_op_on_partial_ord.rs1
-rw-r--r--clippy_lints/src/neg_multiply.rs1
-rw-r--r--clippy_lints/src/new_without_default.rs1
-rw-r--r--clippy_lints/src/no_effect.rs3
-rw-r--r--clippy_lints/src/non_copy_const.rs2
-rw-r--r--clippy_lints/src/non_expressive_names.rs3
-rw-r--r--clippy_lints/src/non_octal_unix_permissions.rs1
-rw-r--r--clippy_lints/src/non_send_fields_in_send_ty.rs1
-rw-r--r--clippy_lints/src/nonstandard_macro_braces.rs1
-rw-r--r--clippy_lints/src/open_options.rs1
-rw-r--r--clippy_lints/src/option_env_unwrap.rs1
-rw-r--r--clippy_lints/src/option_if_let_else.rs1
-rw-r--r--clippy_lints/src/overflow_check_conditional.rs1
-rw-r--r--clippy_lints/src/panic_in_result_fn.rs1
-rw-r--r--clippy_lints/src/panic_unimplemented.rs4
-rw-r--r--clippy_lints/src/partialeq_ne_impl.rs1
-rw-r--r--clippy_lints/src/pass_by_ref_or_value.rs2
-rw-r--r--clippy_lints/src/path_buf_push_overwrite.rs1
-rw-r--r--clippy_lints/src/pattern_type_mismatch.rs1
-rw-r--r--clippy_lints/src/precedence.rs1
-rw-r--r--clippy_lints/src/ptr.rs4
-rw-r--r--clippy_lints/src/ptr_eq.rs1
-rw-r--r--clippy_lints/src/ptr_offset_with_cast.rs1
-rw-r--r--clippy_lints/src/question_mark.rs1
-rw-r--r--clippy_lints/src/ranges.rs5
-rw-r--r--clippy_lints/src/redundant_clone.rs1
-rw-r--r--clippy_lints/src/redundant_closure_call.rs1
-rw-r--r--clippy_lints/src/redundant_else.rs1
-rw-r--r--clippy_lints/src/redundant_field_names.rs1
-rw-r--r--clippy_lints/src/redundant_pub_crate.rs1
-rw-r--r--clippy_lints/src/redundant_slicing.rs1
-rw-r--r--clippy_lints/src/redundant_static_lifetimes.rs1
-rw-r--r--clippy_lints/src/ref_option_ref.rs1
-rw-r--r--clippy_lints/src/reference.rs2
-rw-r--r--clippy_lints/src/regex.rs2
-rw-r--r--clippy_lints/src/repeat_once.rs1
-rw-r--r--clippy_lints/src/returns.rs2
-rw-r--r--clippy_lints/src/same_name_method.rs1
-rw-r--r--clippy_lints/src/self_assignment.rs1
-rw-r--r--clippy_lints/src/self_named_constructors.rs1
-rw-r--r--clippy_lints/src/semicolon_if_nothing_returned.rs1
-rw-r--r--clippy_lints/src/serde_api.rs1
-rw-r--r--clippy_lints/src/shadow.rs3
-rw-r--r--clippy_lints/src/single_component_path_imports.rs1
-rw-r--r--clippy_lints/src/size_of_in_element_count.rs1
-rw-r--r--clippy_lints/src/slow_vector_initialization.rs1
-rw-r--r--clippy_lints/src/stable_sort_primitive.rs1
-rw-r--r--clippy_lints/src/strings.rs7
-rw-r--r--clippy_lints/src/strlen_on_c_strings.rs1
-rw-r--r--clippy_lints/src/suspicious_operation_groupings.rs1
-rw-r--r--clippy_lints/src/suspicious_trait_impl.rs2
-rw-r--r--clippy_lints/src/swap.rs2
-rw-r--r--clippy_lints/src/tabs_in_doc_comments.rs1
-rw-r--r--clippy_lints/src/temporary_assignment.rs1
-rw-r--r--clippy_lints/src/to_digit_is_some.rs1
-rw-r--r--clippy_lints/src/to_string_in_display.rs1
-rw-r--r--clippy_lints/src/trailing_empty_array.rs1
-rw-r--r--clippy_lints/src/trait_bounds.rs2
-rw-r--r--clippy_lints/src/transmute/mod.rs13
-rw-r--r--clippy_lints/src/transmuting_null.rs1
-rw-r--r--clippy_lints/src/try_err.rs1
-rw-r--r--clippy_lints/src/types/mod.rs9
-rw-r--r--clippy_lints/src/undocumented_unsafe_blocks.rs1
-rw-r--r--clippy_lints/src/undropped_manually_drops.rs1
-rw-r--r--clippy_lints/src/unicode.rs3
-rw-r--r--clippy_lints/src/uninit_vec.rs1
-rw-r--r--clippy_lints/src/unit_hash.rs1
-rw-r--r--clippy_lints/src/unit_return_expecting_ord.rs1
-rw-r--r--clippy_lints/src/unit_types/mod.rs3
-rw-r--r--clippy_lints/src/unnamed_address.rs2
-rw-r--r--clippy_lints/src/unnecessary_self_imports.rs1
-rw-r--r--clippy_lints/src/unnecessary_sort_by.rs1
-rw-r--r--clippy_lints/src/unnecessary_wraps.rs1
-rw-r--r--clippy_lints/src/unnested_or_patterns.rs1
-rw-r--r--clippy_lints/src/unsafe_removed_from_name.rs1
-rw-r--r--clippy_lints/src/unused_async.rs1
-rw-r--r--clippy_lints/src/unused_io_amount.rs1
-rw-r--r--clippy_lints/src/unused_self.rs1
-rw-r--r--clippy_lints/src/unused_unit.rs1
-rw-r--r--clippy_lints/src/unwrap.rs2
-rw-r--r--clippy_lints/src/unwrap_in_result.rs1
-rw-r--r--clippy_lints/src/upper_case_acronyms.rs1
-rw-r--r--clippy_lints/src/use_self.rs1
-rw-r--r--clippy_lints/src/useless_conversion.rs1
-rw-r--r--clippy_lints/src/utils/internal_lints.rs80
-rw-r--r--clippy_lints/src/utils/internal_lints/metadata_collector.rs29
-rw-r--r--clippy_lints/src/vec.rs1
-rw-r--r--clippy_lints/src/vec_init_then_push.rs1
-rw-r--r--clippy_lints/src/vec_resize_to_zero.rs1
-rw-r--r--clippy_lints/src/verbose_file_reads.rs1
-rw-r--r--clippy_lints/src/wildcard_dependencies.rs1
-rw-r--r--clippy_lints/src/wildcard_imports.rs2
-rw-r--r--clippy_lints/src/write.rs9
-rw-r--r--clippy_lints/src/zero_div_zero.rs1
-rw-r--r--clippy_lints/src/zero_sized_map_values.rs1
-rw-r--r--clippy_utils/src/attrs.rs15
-rw-r--r--doc/adding_lints.md6
-rw-r--r--tests/ui-internal/check_clippy_version_attribute.rs87
-rw-r--r--tests/ui-internal/check_clippy_version_attribute.stderr73
-rw-r--r--tests/ui-internal/collapsible_span_lint_calls.fixed1
-rw-r--r--tests/ui-internal/collapsible_span_lint_calls.rs1
-rw-r--r--tests/ui-internal/collapsible_span_lint_calls.stderr10
-rw-r--r--tests/ui-internal/custom_ice_message.rs1
-rw-r--r--tests/ui-internal/default_lint.rs1
-rw-r--r--tests/ui-internal/default_lint.stderr2
-rw-r--r--tests/ui-internal/if_chain_style.rs2
-rw-r--r--tests/ui-internal/interning_defined_symbol.fixed1
-rw-r--r--tests/ui-internal/interning_defined_symbol.rs1
-rw-r--r--tests/ui-internal/interning_defined_symbol.stderr8
-rw-r--r--tests/ui-internal/invalid_paths.rs1
-rw-r--r--tests/ui-internal/invalid_paths.stderr4
-rw-r--r--tests/ui-internal/lint_without_lint_pass.rs1
-rw-r--r--tests/ui-internal/lint_without_lint_pass.stderr2
-rw-r--r--tests/ui-internal/match_type_on_diag_item.rs1
-rw-r--r--tests/ui-internal/match_type_on_diag_item.stderr6
-rw-r--r--tests/ui-internal/outer_expn_data.fixed1
-rw-r--r--tests/ui-internal/outer_expn_data.rs1
-rw-r--r--tests/ui-internal/outer_expn_data.stderr2
-rw-r--r--tests/ui-internal/unnecessary_symbol_str.fixed6
-rw-r--r--tests/ui-internal/unnecessary_symbol_str.rs6
-rw-r--r--tests/ui-internal/unnecessary_symbol_str.stderr10
-rw-r--r--util/gh-pages/index.html12
271 files changed, 873 insertions, 42 deletions
diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml
index affb283017c..d350d9a0018 100644
--- a/clippy_dev/Cargo.toml
+++ b/clippy_dev/Cargo.toml
@@ -12,6 +12,7 @@ opener = "0.5"
 regex = "1.5"
 shell-escape = "0.1"
 walkdir = "2.3"
+cargo_metadata = "0.14"
 
 [features]
 deny-warnings = []
diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs
index 43a478ee77d..59658b42c79 100644
--- a/clippy_dev/src/new_lint.rs
+++ b/clippy_dev/src/new_lint.rs
@@ -132,6 +132,18 @@ fn to_camel_case(name: &str) -> String {
         .collect()
 }
 
+fn get_stabilisation_version() -> String {
+    let mut command = cargo_metadata::MetadataCommand::new();
+    command.no_deps();
+    if let Ok(metadata) = command.exec() {
+        if let Some(pkg) = metadata.packages.iter().find(|pkg| pkg.name == "clippy") {
+            return format!("{}.{}.0", pkg.version.minor, pkg.version.patch);
+        }
+    }
+
+    String::from("<TODO set version(see doc/adding_lints.md)>")
+}
+
 fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String {
     let mut contents = format!(
         indoc! {"
@@ -178,6 +190,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
         },
     };
 
+    let version = get_stabilisation_version();
     let lint_name = lint.name;
     let category = lint.category;
     let name_camel = to_camel_case(lint.name);
@@ -212,7 +225,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
     });
 
     result.push_str(&format!(
-        indoc! {"
+        indoc! {r#"
             declare_clippy_lint! {{
                 /// ### What it does
                 ///
@@ -226,11 +239,13 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
                 /// ```rust
                 /// // example code which does not raise clippy warning
                 /// ```
+                #[clippy::version = "{version}"]
                 pub {name_upper},
                 {category},
-                \"default lint description\"
+                "default lint description"
             }}
-        "},
+        "#},
+        version = version,
         name_upper = name_upper,
         category = category,
     ));
diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs
index 23f58bc4915..8dd073ef405 100644
--- a/clippy_dev/src/update_lints.rs
+++ b/clippy_dev/src/update_lints.rs
@@ -18,6 +18,7 @@ static DEC_CLIPPY_LINT_RE: SyncLazy<Regex> = SyncLazy::new(|| {
         r#"(?x)
     declare_clippy_lint!\s*[\{(]
     (?:\s+///.*)*
+    (?:\s*\#\[clippy::version\s*=\s*"[^"]*"\])?
     \s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
     (?P<cat>[a-z_]+)\s*,\s*
     "(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
@@ -31,6 +32,7 @@ static DEC_DEPRECATED_LINT_RE: SyncLazy<Regex> = SyncLazy::new(|| {
         r#"(?x)
     declare_deprecated_lint!\s*[{(]\s*
     (?:\s+///.*)*
+    (?:\s*\#\[clippy::version\s*=\s*"[^"]*"\])?
     \s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
     "(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
 "#,
@@ -495,6 +497,7 @@ fn test_parse_contents() {
     let result: Vec<Lint> = parse_contents(
         r#"
 declare_clippy_lint! {
+    #[clippy::version = "Hello Clippy!"]
     pub PTR_ARG,
     style,
     "really long \
@@ -502,6 +505,7 @@ declare_clippy_lint! {
 }
 
 declare_clippy_lint!{
+    #[clippy::version = "Test version"]
     pub DOC_MARKDOWN,
     pedantic,
     "single line"
@@ -509,6 +513,7 @@ declare_clippy_lint!{
 
 /// some doc comment
 declare_deprecated_lint! {
+    #[clippy::version = "I'm a version"]
     pub SHOULD_ASSERT_EQ,
     "`assert!()` will be more flexible with RFC 2011"
 }
diff --git a/clippy_lints/src/absurd_extreme_comparisons.rs b/clippy_lints/src/absurd_extreme_comparisons.rs
index 1483f3f9185..7665aa8380b 100644
--- a/clippy_lints/src/absurd_extreme_comparisons.rs
+++ b/clippy_lints/src/absurd_extreme_comparisons.rs
@@ -36,6 +36,7 @@ declare_clippy_lint! {
     /// if vec.len() <= 0 {}
     /// if 100 > i32::MAX {}
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ABSURD_EXTREME_COMPARISONS,
     correctness,
     "a comparison with a maximum or minimum value that is always true or false"
diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs
index fb54ac1ec51..12435eefbc4 100644
--- a/clippy_lints/src/approx_const.rs
+++ b/clippy_lints/src/approx_const.rs
@@ -33,6 +33,7 @@ declare_clippy_lint! {
     /// let x = std::f32::consts::PI;
     /// let y = std::f64::consts::FRAC_1_PI;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub APPROX_CONSTANT,
     correctness,
     "the approximate of a known float constant (in `std::fXX::consts`)"
diff --git a/clippy_lints/src/arithmetic.rs b/clippy_lints/src/arithmetic.rs
index 36fe7b7a867..e0c1d6ab6e1 100644
--- a/clippy_lints/src/arithmetic.rs
+++ b/clippy_lints/src/arithmetic.rs
@@ -25,6 +25,7 @@ declare_clippy_lint! {
     /// # let a = 0;
     /// a + 1;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INTEGER_ARITHMETIC,
     restriction,
     "any integer arithmetic expression which could overflow or panic"
@@ -43,6 +44,7 @@ declare_clippy_lint! {
     /// # let a = 0.0;
     /// a + 1.0;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FLOAT_ARITHMETIC,
     restriction,
     "any floating-point arithmetic statement"
diff --git a/clippy_lints/src/as_conversions.rs b/clippy_lints/src/as_conversions.rs
index 0be460d67a7..53704da1046 100644
--- a/clippy_lints/src/as_conversions.rs
+++ b/clippy_lints/src/as_conversions.rs
@@ -38,6 +38,7 @@ declare_clippy_lint! {
     /// f(a.try_into().expect("Unexpected u16 overflow in f"));
     /// ```
     ///
+    #[clippy::version = "1.41.0"]
     pub AS_CONVERSIONS,
     restriction,
     "using a potentially dangerous silent `as` conversion"
diff --git a/clippy_lints/src/asm_syntax.rs b/clippy_lints/src/asm_syntax.rs
index 825832eb79d..0322698f029 100644
--- a/clippy_lints/src/asm_syntax.rs
+++ b/clippy_lints/src/asm_syntax.rs
@@ -75,6 +75,7 @@ declare_clippy_lint! {
     /// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax));
     /// # }
     /// ```
+    #[clippy::version = "1.49.0"]
     pub INLINE_ASM_X86_INTEL_SYNTAX,
     restriction,
     "prefer AT&T x86 assembly syntax"
@@ -111,6 +112,7 @@ declare_clippy_lint! {
     /// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr);
     /// # }
     /// ```
+    #[clippy::version = "1.49.0"]
     pub INLINE_ASM_X86_ATT_SYNTAX,
     restriction,
     "prefer Intel x86 assembly syntax"
diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs
index d834a1d317a..521fc84ee9c 100644
--- a/clippy_lints/src/assertions_on_constants.rs
+++ b/clippy_lints/src/assertions_on_constants.rs
@@ -26,6 +26,7 @@ declare_clippy_lint! {
     /// const B: bool = false;
     /// assert!(B)
     /// ```
+    #[clippy::version = "1.34.0"]
     pub ASSERTIONS_ON_CONSTANTS,
     style,
     "`assert!(true)` / `assert!(false)` will be optimized out by the compiler, and should probably be replaced by a `panic!()` or `unreachable!()`"
diff --git a/clippy_lints/src/assign_ops.rs b/clippy_lints/src/assign_ops.rs
index 2097a1feff9..e16f4369da9 100644
--- a/clippy_lints/src/assign_ops.rs
+++ b/clippy_lints/src/assign_ops.rs
@@ -34,6 +34,7 @@ declare_clippy_lint! {
     /// // Good
     /// a += b;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ASSIGN_OP_PATTERN,
     style,
     "assigning the result of an operation on a variable to that same variable"
@@ -60,6 +61,7 @@ declare_clippy_lint! {
     /// // ...
     /// a += a + b;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MISREFACTORED_ASSIGN_OP,
     suspicious,
     "having a variable on both sides of an assign op"
diff --git a/clippy_lints/src/async_yields_async.rs b/clippy_lints/src/async_yields_async.rs
index 182736a5a20..0619490e73c 100644
--- a/clippy_lints/src/async_yields_async.rs
+++ b/clippy_lints/src/async_yields_async.rs
@@ -34,6 +34,7 @@ declare_clippy_lint! {
     ///   };
     /// }
     /// ```
+    #[clippy::version = "1.48.0"]
     pub ASYNC_YIELDS_ASYNC,
     correctness,
     "async blocks that return a type that can be awaited"
diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs
index 9c5db18336f..1edb7c950e7 100644
--- a/clippy_lints/src/attrs.rs
+++ b/clippy_lints/src/attrs.rs
@@ -66,6 +66,7 @@ declare_clippy_lint! {
     /// #[inline(always)]
     /// fn not_quite_hot_code(..) { ... }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INLINE_ALWAYS,
     pedantic,
     "use of `#[inline(always)]`"
@@ -100,6 +101,7 @@ declare_clippy_lint! {
     /// #[macro_use]
     /// extern crate baz;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub USELESS_ATTRIBUTE,
     correctness,
     "use of lint attributes on `extern crate` items"
@@ -119,6 +121,7 @@ declare_clippy_lint! {
     /// #[deprecated(since = "forever")]
     /// fn something_else() { /* ... */ }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DEPRECATED_SEMVER,
     correctness,
     "use of `#[deprecated(since = \"x\")]` where x is not semver"
@@ -156,6 +159,7 @@ declare_clippy_lint! {
     /// #[allow(dead_code)]
     /// fn this_is_fine_too() { }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EMPTY_LINE_AFTER_OUTER_ATTR,
     nursery,
     "empty line after outer attribute"
@@ -179,6 +183,7 @@ declare_clippy_lint! {
     /// ```rust
     /// #![deny(clippy::as_conversions)]
     /// ```
+    #[clippy::version = "1.47.0"]
     pub BLANKET_CLIPPY_RESTRICTION_LINTS,
     suspicious,
     "enabling the complete restriction group"
@@ -210,6 +215,7 @@ declare_clippy_lint! {
     /// #[rustfmt::skip]
     /// fn main() { }
     /// ```
+    #[clippy::version = "1.32.0"]
     pub DEPRECATED_CFG_ATTR,
     complexity,
     "usage of `cfg_attr(rustfmt)` instead of tool attributes"
@@ -242,6 +248,7 @@ declare_clippy_lint! {
     /// fn conditional() { }
     /// ```
     /// Check the [Rust Reference](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) for more details.
+    #[clippy::version = "1.45.0"]
     pub MISMATCHED_TARGET_OS,
     correctness,
     "usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`"
diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs
index 28615b9217c..1cc3418d474 100644
--- a/clippy_lints/src/await_holding_invalid.rs
+++ b/clippy_lints/src/await_holding_invalid.rs
@@ -47,6 +47,7 @@ declare_clippy_lint! {
     ///   bar.await;
     /// }
     /// ```
+    #[clippy::version = "1.45.0"]
     pub AWAIT_HOLDING_LOCK,
     pedantic,
     "Inside an async function, holding a MutexGuard while calling await"
@@ -88,6 +89,7 @@ declare_clippy_lint! {
     ///   bar.await;
     /// }
     /// ```
+    #[clippy::version = "1.49.0"]
     pub AWAIT_HOLDING_REFCELL_REF,
     pedantic,
     "Inside an async function, holding a RefCell ref while calling await"
diff --git a/clippy_lints/src/bit_mask.rs b/clippy_lints/src/bit_mask.rs
index 11346e7c96a..0977cf22b2c 100644
--- a/clippy_lints/src/bit_mask.rs
+++ b/clippy_lints/src/bit_mask.rs
@@ -41,6 +41,7 @@ declare_clippy_lint! {
     /// # let x = 1;
     /// if (x & 1 == 2) { }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub BAD_BIT_MASK,
     correctness,
     "expressions of the form `_ & mask == select` that will only ever return `true` or `false`"
@@ -73,6 +74,7 @@ declare_clippy_lint! {
     /// # let x = 1;
     /// if (x | 1 > 3) {  }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INEFFECTIVE_BIT_MASK,
     correctness,
     "expressions where a bit mask will be rendered useless by a comparison, e.g., `(x | 1) > 2`"
@@ -95,6 +97,7 @@ declare_clippy_lint! {
     /// # let x = 1;
     /// if x & 0b1111 == 0 { }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub VERBOSE_BIT_MASK,
     pedantic,
     "expressions where a bit mask is less readable than the corresponding method call"
diff --git a/clippy_lints/src/blacklisted_name.rs b/clippy_lints/src/blacklisted_name.rs
index 916c78c982a..1600fb25d89 100644
--- a/clippy_lints/src/blacklisted_name.rs
+++ b/clippy_lints/src/blacklisted_name.rs
@@ -17,6 +17,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let foo = 3.14;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub BLACKLISTED_NAME,
     style,
     "usage of a blacklisted/placeholder name"
diff --git a/clippy_lints/src/blocks_in_if_conditions.rs b/clippy_lints/src/blocks_in_if_conditions.rs
index 47e5b0d583d..b59f49357df 100644
--- a/clippy_lints/src/blocks_in_if_conditions.rs
+++ b/clippy_lints/src/blocks_in_if_conditions.rs
@@ -41,6 +41,7 @@ declare_clippy_lint! {
     /// let res = { let x = somefunc(); x };
     /// if res { /* ... */ }
     /// ```
+    #[clippy::version = "1.45.0"]
     pub BLOCKS_IN_IF_CONDITIONS,
     style,
     "useless or complex blocks that can be eliminated in conditions"
diff --git a/clippy_lints/src/bool_assert_comparison.rs b/clippy_lints/src/bool_assert_comparison.rs
index cdc192a47e4..a59abc66306 100644
--- a/clippy_lints/src/bool_assert_comparison.rs
+++ b/clippy_lints/src/bool_assert_comparison.rs
@@ -23,6 +23,7 @@ declare_clippy_lint! {
     /// // Good
     /// assert!(!"a".is_empty());
     /// ```
+    #[clippy::version = "1.53.0"]
     pub BOOL_ASSERT_COMPARISON,
     style,
     "Using a boolean as comparison value in an assert_* macro when there is no need"
diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs
index a1e6b7224ff..51835ee7488 100644
--- a/clippy_lints/src/booleans.rs
+++ b/clippy_lints/src/booleans.rs
@@ -31,6 +31,7 @@ declare_clippy_lint! {
     /// if a && true  // should be: if a
     /// if !(a == b)  // should be: if a != b
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NONMINIMAL_BOOL,
     complexity,
     "boolean expressions that can be written more concisely"
@@ -52,6 +53,7 @@ declare_clippy_lint! {
     /// if a && b || a { ... }
     /// ```
     /// The `b` is unnecessary, the expression is equivalent to `if a`.
+    #[clippy::version = "pre 1.29.0"]
     pub LOGIC_BUG,
     correctness,
     "boolean expressions that contain terminals which can be eliminated"
diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs
index a07cd5e5f4e..a938ada9d2a 100644
--- a/clippy_lints/src/bytecount.rs
+++ b/clippy_lints/src/bytecount.rs
@@ -30,6 +30,7 @@ declare_clippy_lint! {
     /// # let vec = vec![1_u8];
     /// &vec.iter().filter(|x| **x == 0u8).count(); // use bytecount::count instead
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NAIVE_BYTECOUNT,
     pedantic,
     "use of naive `<slice>.filter(|&x| x == y).count()` to count byte values"
diff --git a/clippy_lints/src/cargo_common_metadata.rs b/clippy_lints/src/cargo_common_metadata.rs
index ff619c59b6e..23f79fdc682 100644
--- a/clippy_lints/src/cargo_common_metadata.rs
+++ b/clippy_lints/src/cargo_common_metadata.rs
@@ -42,6 +42,7 @@ declare_clippy_lint! {
     /// keywords = ["clippy", "lint", "plugin"]
     /// categories = ["development-tools", "development-tools::cargo-plugins"]
     /// ```
+    #[clippy::version = "1.32.0"]
     pub CARGO_COMMON_METADATA,
     cargo,
     "common metadata is defined in `Cargo.toml`"
diff --git a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs
index c876553c165..3f286dd9e2f 100644
--- a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs
+++ b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs
@@ -27,6 +27,7 @@ declare_clippy_lint! {
     ///     filename.rsplit('.').next().map(|ext| ext.eq_ignore_ascii_case("rs")) == Some(true)
     /// }
     /// ```
+    #[clippy::version = "1.51.0"]
     pub CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
     pedantic,
     "Checks for calls to ends_with with case-sensitive file extensions"
diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs
index 233abd17894..c674327486c 100644
--- a/clippy_lints/src/casts/mod.rs
+++ b/clippy_lints/src/casts/mod.rs
@@ -40,6 +40,7 @@ declare_clippy_lint! {
     /// let x = u64::MAX;
     /// x as f64;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CAST_PRECISION_LOSS,
     pedantic,
     "casts that cause loss of precision, e.g., `x as f32` where `x: u64`"
@@ -61,6 +62,7 @@ declare_clippy_lint! {
     /// let y: i8 = -1;
     /// y as u128; // will return 18446744073709551615
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CAST_SIGN_LOSS,
     pedantic,
     "casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`"
@@ -83,6 +85,7 @@ declare_clippy_lint! {
     ///     x as u8
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CAST_POSSIBLE_TRUNCATION,
     pedantic,
     "casts that may cause truncation of the value, e.g., `x as u8` where `x: u32`, or `x as i32` where `x: f32`"
@@ -106,6 +109,7 @@ declare_clippy_lint! {
     /// ```rust
     /// u32::MAX as i32; // will yield a value of `-1`
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CAST_POSSIBLE_WRAP,
     pedantic,
     "casts that may cause wrapping around the value, e.g., `x as i32` where `x: u32` and `x > i32::MAX`"
@@ -138,6 +142,7 @@ declare_clippy_lint! {
     ///     u64::from(x)
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CAST_LOSSLESS,
     pedantic,
     "casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`"
@@ -163,6 +168,7 @@ declare_clippy_lint! {
     /// let _ = 2_i32;
     /// let _ = 0.5_f32;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNNECESSARY_CAST,
     complexity,
     "cast to the same type, e.g., `x as i32` where `x: i32`"
@@ -190,6 +196,7 @@ declare_clippy_lint! {
     /// (&1u8 as *const u8).cast::<u16>();
     /// (&mut 1u8 as *mut u8).cast::<u16>();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CAST_PTR_ALIGNMENT,
     pedantic,
     "cast from a pointer to a more-strictly-aligned pointer"
@@ -217,6 +224,7 @@ declare_clippy_lint! {
     /// fn fun2() -> i32 { 1 }
     /// let a = fun2 as usize;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FN_TO_NUMERIC_CAST,
     style,
     "casting a function pointer to a numeric type other than usize"
@@ -247,6 +255,7 @@ declare_clippy_lint! {
     /// let fn_ptr = fn2 as usize;
     /// let fn_ptr_truncated = fn_ptr as i32;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
     style,
     "casting a function pointer to a numeric type not wide enough to store the address"
@@ -283,6 +292,7 @@ declare_clippy_lint! {
     /// }
     /// let _ = fn3 as fn() -> u16;
     /// ```
+    #[clippy::version = "1.58.0"]
     pub FN_TO_NUMERIC_CAST_ANY,
     restriction,
     "casting a function pointer to any integer type"
@@ -317,6 +327,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.33.0"]
     pub CAST_REF_TO_MUT,
     correctness,
     "a cast of reference to a mutable pointer"
@@ -344,6 +355,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// b'x'
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CHAR_LIT_AS_U8,
     complexity,
     "casting a character literal to `u8` truncates"
@@ -372,6 +384,7 @@ declare_clippy_lint! {
     /// let _ = ptr.cast::<i32>();
     /// let _ = mut_ptr.cast::<i32>();
     /// ```
+    #[clippy::version = "1.51.0"]
     pub PTR_AS_PTR,
     pedantic,
     "casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`"
diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs
index 842bbf006cc..ffe6340bd77 100644
--- a/clippy_lints/src/checked_conversions.rs
+++ b/clippy_lints/src/checked_conversions.rs
@@ -36,6 +36,7 @@ declare_clippy_lint! {
     /// i32::try_from(foo).is_ok()
     /// # ;
     /// ```
+    #[clippy::version = "1.37.0"]
     pub CHECKED_CONVERSIONS,
     pedantic,
     "`try_from` could replace manual bounds checking when casting"
diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs
index 1ccb8c5d880..84a2373efe1 100644
--- a/clippy_lints/src/cognitive_complexity.rs
+++ b/clippy_lints/src/cognitive_complexity.rs
@@ -27,6 +27,7 @@ declare_clippy_lint! {
     ///
     /// ### Example
     /// No. You'll see it when you get the warning.
+    #[clippy::version = "1.35.0"]
     pub COGNITIVE_COMPLEXITY,
     nursery,
     "functions that should be split up into multiple functions"
diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs
index 4aa87980715..f03f34e5a4b 100644
--- a/clippy_lints/src/collapsible_if.rs
+++ b/clippy_lints/src/collapsible_if.rs
@@ -47,6 +47,7 @@ declare_clippy_lint! {
     ///     …
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub COLLAPSIBLE_IF,
     style,
     "nested `if`s that can be collapsed (e.g., `if x { if y { ... } }`"
@@ -82,6 +83,7 @@ declare_clippy_lint! {
     ///     …
     /// }
     /// ```
+    #[clippy::version = "1.51.0"]
     pub COLLAPSIBLE_ELSE_IF,
     style,
     "nested `else`-`if` expressions that can be collapsed (e.g., `else { if x { ... } }`)"
diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs
index a4693fa213b..626f9971f01 100644
--- a/clippy_lints/src/collapsible_match.rs
+++ b/clippy_lints/src/collapsible_match.rs
@@ -41,6 +41,7 @@ declare_clippy_lint! {
     ///     };
     /// }
     /// ```
+    #[clippy::version = "1.50.0"]
     pub COLLAPSIBLE_MATCH,
     style,
     "Nested `match` or `if let` expressions where the patterns may be \"collapsed\" together."
diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs
index 597a3c67024..399d11472b0 100644
--- a/clippy_lints/src/comparison_chain.rs
+++ b/clippy_lints/src/comparison_chain.rs
@@ -49,6 +49,7 @@ declare_clippy_lint! {
     ///      }
     /// }
     /// ```
+    #[clippy::version = "1.40.0"]
     pub COMPARISON_CHAIN,
     style,
     "`if`s that can be rewritten with `match` and `cmp`"
diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs
index f57da62da5f..d07bc23235b 100644
--- a/clippy_lints/src/copies.rs
+++ b/clippy_lints/src/copies.rs
@@ -41,6 +41,7 @@ declare_clippy_lint! {
     ///     …
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub IFS_SAME_COND,
     correctness,
     "consecutive `if`s with the same condition"
@@ -88,6 +89,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.41.0"]
     pub SAME_FUNCTIONS_IN_IF_CONDITION,
     pedantic,
     "consecutive `if`s with the same function call"
@@ -109,6 +111,7 @@ declare_clippy_lint! {
     ///     42
     /// };
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub IF_SAME_THEN_ELSE,
     correctness,
     "`if` with the same `then` and `else` blocks"
@@ -147,6 +150,7 @@ declare_clippy_lint! {
     ///     42
     /// };
     /// ```
+    #[clippy::version = "1.53.0"]
     pub BRANCHES_SHARING_CODE,
     nursery,
     "`if` statement with shared code in all blocks"
diff --git a/clippy_lints/src/copy_iterator.rs b/clippy_lints/src/copy_iterator.rs
index c2e9e8b3ab7..026683f6006 100644
--- a/clippy_lints/src/copy_iterator.rs
+++ b/clippy_lints/src/copy_iterator.rs
@@ -28,6 +28,7 @@ declare_clippy_lint! {
     /// let a: Vec<_> = my_iterator.take(1).collect();
     /// let b: Vec<_> = my_iterator.collect();
     /// ```
+    #[clippy::version = "1.30.0"]
     pub COPY_ITERATOR,
     pedantic,
     "implementing `Iterator` on a `Copy` type"
diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs
index e4ee2772483..6bc4054a5ab 100644
--- a/clippy_lints/src/create_dir.rs
+++ b/clippy_lints/src/create_dir.rs
@@ -23,6 +23,7 @@ declare_clippy_lint! {
     /// ```rust
     /// std::fs::create_dir_all("foo");
     /// ```
+    #[clippy::version = "1.48.0"]
     pub CREATE_DIR,
     restriction,
     "calling `std::fs::create_dir` instead of `std::fs::create_dir_all`"
diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs
index bab4a696f83..1aae4c81c73 100644
--- a/clippy_lints/src/dbg_macro.rs
+++ b/clippy_lints/src/dbg_macro.rs
@@ -23,6 +23,7 @@ declare_clippy_lint! {
     /// // Good
     /// true
     /// ```
+    #[clippy::version = "1.34.0"]
     pub DBG_MACRO,
     restriction,
     "`dbg!` macro is intended as a debugging tool"
diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs
index 54647ba823e..a0b137efe22 100644
--- a/clippy_lints/src/default.rs
+++ b/clippy_lints/src/default.rs
@@ -29,6 +29,7 @@ declare_clippy_lint! {
     /// // Good
     /// let s = String::default();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DEFAULT_TRAIT_ACCESS,
     pedantic,
     "checks for literal calls to `Default::default()`"
@@ -62,6 +63,7 @@ declare_clippy_lint! {
     ///     .. Default::default()
     /// };
     /// ```
+    #[clippy::version = "1.49.0"]
     pub FIELD_REASSIGN_WITH_DEFAULT,
     style,
     "binding initialized with Default should have its fields set in the initializer"
diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs
index 3f1b7ea6214..3573ea5f026 100644
--- a/clippy_lints/src/default_numeric_fallback.rs
+++ b/clippy_lints/src/default_numeric_fallback.rs
@@ -46,6 +46,7 @@ declare_clippy_lint! {
     /// let i = 10i32;
     /// let f = 1.23f64;
     /// ```
+    #[clippy::version = "1.52.0"]
     pub DEFAULT_NUMERIC_FALLBACK,
     restriction,
     "usage of unconstrained numeric literals which may cause default numeric fallback."
diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs
index 9d8524ec91c..bba27576c89 100644
--- a/clippy_lints/src/deprecated_lints.rs
+++ b/clippy_lints/src/deprecated_lints.rs
@@ -21,6 +21,7 @@ declare_deprecated_lint! {
     /// ### Deprecation reason
     /// This used to check for `assert!(a == b)` and recommend
     /// replacement with `assert_eq!(a, b)`, but this is no longer needed after RFC 2011.
+    #[clippy::version = "pre 1.29.0"]
     pub SHOULD_ASSERT_EQ,
     "`assert!()` will be more flexible with RFC 2011"
 }
@@ -32,6 +33,7 @@ declare_deprecated_lint! {
     /// ### Deprecation reason
     /// This used to check for `Vec::extend`, which was slower than
     /// `Vec::extend_from_slice`. Thanks to specialization, this is no longer true.
+    #[clippy::version = "pre 1.29.0"]
     pub EXTEND_FROM_SLICE,
     "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice"
 }
@@ -45,6 +47,7 @@ declare_deprecated_lint! {
     /// an infinite iterator, which is better expressed by `iter::repeat`,
     /// but the method has been removed for `Iterator::step_by` which panics
     /// if given a zero
+    #[clippy::version = "pre 1.29.0"]
     pub RANGE_STEP_BY_ZERO,
     "`iterator.step_by(0)` panics nowadays"
 }
@@ -56,6 +59,7 @@ declare_deprecated_lint! {
     /// ### Deprecation reason
     /// This used to check for `Vec::as_slice`, which was unstable with good
     /// stable alternatives. `Vec::as_slice` has now been stabilized.
+    #[clippy::version = "pre 1.29.0"]
     pub UNSTABLE_AS_SLICE,
     "`Vec::as_slice` has been stabilized in 1.7"
 }
@@ -67,6 +71,7 @@ declare_deprecated_lint! {
     /// ### Deprecation reason
     /// This used to check for `Vec::as_mut_slice`, which was unstable with good
     /// stable alternatives. `Vec::as_mut_slice` has now been stabilized.
+    #[clippy::version = "pre 1.29.0"]
     pub UNSTABLE_AS_MUT_SLICE,
     "`Vec::as_mut_slice` has been stabilized in 1.7"
 }
@@ -80,6 +85,7 @@ declare_deprecated_lint! {
     /// between non-pointer types of differing alignment is well-defined behavior (it's semantically
     /// equivalent to a memcpy). This lint has thus been refactored into two separate lints:
     /// cast_ptr_alignment and transmute_ptr_to_ptr.
+    #[clippy::version = "pre 1.29.0"]
     pub MISALIGNED_TRANSMUTE,
     "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr"
 }
@@ -92,6 +98,7 @@ declare_deprecated_lint! {
     /// This lint is too subjective, not having a good reason for being in clippy.
     /// Additionally, compound assignment operators may be overloaded separately from their non-assigning
     /// counterparts, so this lint may suggest a change in behavior or the code may not compile.
+    #[clippy::version = "1.30.0"]
     pub ASSIGN_OPS,
     "using compound assignment operators (e.g., `+=`) is harmless"
 }
@@ -104,6 +111,7 @@ declare_deprecated_lint! {
     /// The original rule will only lint for `if let`. After
     /// making it support to lint `match`, naming as `if let` is not suitable for it.
     /// So, this lint is deprecated.
+    #[clippy::version = "pre 1.29.0"]
     pub IF_LET_REDUNDANT_PATTERN_MATCHING,
     "this lint has been changed to redundant_pattern_matching"
 }
@@ -117,6 +125,7 @@ declare_deprecated_lint! {
     /// Vec::with_capacity(n); vec.set_len(n);` with `let vec = vec![0; n];`. The
     /// replacement has very different performance characteristics so the lint is
     /// deprecated.
+    #[clippy::version = "pre 1.29.0"]
     pub UNSAFE_VECTOR_INITIALIZATION,
     "the replacement suggested by this lint had substantially different behavior"
 }
@@ -127,6 +136,7 @@ declare_deprecated_lint! {
     ///
     /// ### Deprecation reason
     /// This lint has been superseded by #[must_use] in rustc.
+    #[clippy::version = "1.39.0"]
     pub UNUSED_COLLECT,
     "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint"
 }
@@ -137,6 +147,7 @@ declare_deprecated_lint! {
     ///
     /// ### Deprecation reason
     /// Associated-constants are now preferred.
+    #[clippy::version = "1.44.0"]
     pub REPLACE_CONSTS,
     "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants"
 }
@@ -147,6 +158,7 @@ declare_deprecated_lint! {
     ///
     /// ### Deprecation reason
     /// The regex! macro does not exist anymore.
+    #[clippy::version = "1.47.0"]
     pub REGEX_MACRO,
     "the regex! macro has been removed from the regex crate in 2018"
 }
@@ -158,6 +170,7 @@ declare_deprecated_lint! {
     /// ### Deprecation reason
     /// This lint has been replaced by `manual_find_map`, a
     /// more specific lint.
+    #[clippy::version = "1.51.0"]
     pub FIND_MAP,
     "this lint has been replaced by `manual_find_map`, a more specific lint"
 }
@@ -169,6 +182,7 @@ declare_deprecated_lint! {
     /// ### Deprecation reason
     /// This lint has been replaced by `manual_filter_map`, a
     /// more specific lint.
+    #[clippy::version = "1.53.0"]
     pub FILTER_MAP,
     "this lint has been replaced by `manual_filter_map`, a more specific lint"
 }
@@ -181,6 +195,7 @@ declare_deprecated_lint! {
     /// The `avoid_breaking_exported_api` config option was added, which
     /// enables the `enum_variant_names` lint for public items.
     /// ```
+    #[clippy::version = "1.54.0"]
     pub PUB_ENUM_VARIANT_NAMES,
     "set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items"
 }
@@ -192,6 +207,7 @@ declare_deprecated_lint! {
     /// ### Deprecation reason
     /// The `avoid_breaking_exported_api` config option was added, which
     /// enables the `wrong_self_conversion` lint for public items.
+    #[clippy::version = "1.54.0"]
     pub WRONG_PUB_SELF_CONVENTION,
     "set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items"
 }
diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs
index 975353add08..bbaae94d3dd 100644
--- a/clippy_lints/src/dereference.rs
+++ b/clippy_lints/src/dereference.rs
@@ -34,6 +34,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// let _ = d.unwrap().deref();
     /// ```
+    #[clippy::version = "1.44.0"]
     pub EXPLICIT_DEREF_METHODS,
     pedantic,
     "Explicit use of deref or deref_mut method while not in a method chain."
diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs
index 01ec306e5e1..d0fab2b48fb 100644
--- a/clippy_lints/src/derivable_impls.rs
+++ b/clippy_lints/src/derivable_impls.rs
@@ -46,6 +46,7 @@ declare_clippy_lint! {
     /// has exactly equal bounds, and therefore this lint is disabled for types with
     /// generic parameters.
     ///
+    #[clippy::version = "1.57.0"]
     pub DERIVABLE_IMPLS,
     complexity,
     "manual implementation of the `Default` trait which is equal to a derive"
diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs
index 24ac5917dcb..4b232a26e5d 100644
--- a/clippy_lints/src/derive.rs
+++ b/clippy_lints/src/derive.rs
@@ -38,6 +38,7 @@ declare_clippy_lint! {
     ///     ...
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DERIVE_HASH_XOR_EQ,
     correctness,
     "deriving `Hash` but implementing `PartialEq` explicitly"
@@ -88,6 +89,7 @@ declare_clippy_lint! {
     /// #[derive(Ord, PartialOrd, PartialEq, Eq)]
     /// struct Foo;
     /// ```
+    #[clippy::version = "1.47.0"]
     pub DERIVE_ORD_XOR_PARTIAL_ORD,
     correctness,
     "deriving `Ord` but implementing `PartialOrd` explicitly"
@@ -114,6 +116,7 @@ declare_clippy_lint! {
     ///     // ..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EXPL_IMPL_CLONE_ON_COPY,
     pedantic,
     "implementing `Clone` explicitly on `Copy` types"
@@ -147,6 +150,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.45.0"]
     pub UNSAFE_DERIVE_DESERIALIZE,
     pedantic,
     "deriving `serde::Deserialize` on a type that has methods using `unsafe`"
diff --git a/clippy_lints/src/disallowed_method.rs b/clippy_lints/src/disallowed_method.rs
index 22d726cdcb7..c2217353b32 100644
--- a/clippy_lints/src/disallowed_method.rs
+++ b/clippy_lints/src/disallowed_method.rs
@@ -47,6 +47,7 @@ declare_clippy_lint! {
     /// let mut xs = Vec::new(); // Vec::new is _not_ disallowed in the config.
     /// xs.push(123); // Vec::push is _not_ disallowed in the config.
     /// ```
+    #[clippy::version = "1.49.0"]
     pub DISALLOWED_METHOD,
     nursery,
     "use of a disallowed method call"
diff --git a/clippy_lints/src/disallowed_script_idents.rs b/clippy_lints/src/disallowed_script_idents.rs
index 6d38d30cd0b..3c3f3631849 100644
--- a/clippy_lints/src/disallowed_script_idents.rs
+++ b/clippy_lints/src/disallowed_script_idents.rs
@@ -38,6 +38,7 @@ declare_clippy_lint! {
     /// let zähler = 10; // OK, it's still latin.
     /// let カウンタ = 10; // Will spawn the lint.
     /// ```
+    #[clippy::version = "1.55.0"]
     pub DISALLOWED_SCRIPT_IDENTS,
     restriction,
     "usage of non-allowed Unicode scripts"
diff --git a/clippy_lints/src/disallowed_type.rs b/clippy_lints/src/disallowed_type.rs
index 48f781516f4..7e784ca2244 100644
--- a/clippy_lints/src/disallowed_type.rs
+++ b/clippy_lints/src/disallowed_type.rs
@@ -42,6 +42,7 @@ declare_clippy_lint! {
     /// // A similar type that is allowed by the config
     /// use std::collections::HashMap;
     /// ```
+    #[clippy::version = "1.55.0"]
     pub DISALLOWED_TYPE,
     nursery,
     "use of a disallowed type"
diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs
index 87ad5178ff0..249cf260309 100644
--- a/clippy_lints/src/doc.rs
+++ b/clippy_lints/src/doc.rs
@@ -67,6 +67,7 @@ declare_clippy_lint! {
     /// /// [SmallVec]: SmallVec
     /// fn main() {}
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DOC_MARKDOWN,
     pedantic,
     "presence of `_`, `::` or camel-case outside backticks in documentation"
@@ -101,6 +102,7 @@ declare_clippy_lint! {
     ///     unimplemented!();
     /// }
     /// ```
+    #[clippy::version = "1.39.0"]
     pub MISSING_SAFETY_DOC,
     style,
     "`pub unsafe fn` without `# Safety` docs"
@@ -129,6 +131,7 @@ declare_clippy_lint! {
     ///     unimplemented!();
     /// }
     /// ```
+    #[clippy::version = "1.41.0"]
     pub MISSING_ERRORS_DOC,
     pedantic,
     "`pub fn` returns `Result` without `# Errors` in doc comment"
@@ -159,6 +162,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.52.0"]
     pub MISSING_PANICS_DOC,
     pedantic,
     "`pub fn` may panic without `# Panics` in doc comment"
@@ -187,6 +191,7 @@ declare_clippy_lint! {
     ///     unimplemented!();
     /// }
     /// ``````
+    #[clippy::version = "1.40.0"]
     pub NEEDLESS_DOCTEST_MAIN,
     style,
     "presence of `fn main() {` in code examples"
diff --git a/clippy_lints/src/double_comparison.rs b/clippy_lints/src/double_comparison.rs
index 6520bb91faf..176092e5b28 100644
--- a/clippy_lints/src/double_comparison.rs
+++ b/clippy_lints/src/double_comparison.rs
@@ -31,6 +31,7 @@ declare_clippy_lint! {
     /// # let y = 2;
     /// if x <= y {}
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DOUBLE_COMPARISONS,
     complexity,
     "unnecessary double comparisons that can be simplified"
diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs
index d0d87b6df9a..e10f740d24a 100644
--- a/clippy_lints/src/double_parens.rs
+++ b/clippy_lints/src/double_parens.rs
@@ -32,6 +32,7 @@ declare_clippy_lint! {
     /// // Good
     /// foo(0);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DOUBLE_PARENS,
     complexity,
     "Warn on unnecessary double parentheses"
diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs
index 0f3dc866afb..a8f8e3d8a42 100644
--- a/clippy_lints/src/drop_forget_ref.rs
+++ b/clippy_lints/src/drop_forget_ref.rs
@@ -25,6 +25,7 @@ declare_clippy_lint! {
     /// // still locked
     /// operation_that_requires_mutex_to_be_unlocked();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DROP_REF,
     correctness,
     "calls to `std::mem::drop` with a reference instead of an owned value"
@@ -46,6 +47,7 @@ declare_clippy_lint! {
     /// let x = Box::new(1);
     /// std::mem::forget(&x) // Should have been forget(x), x will still be dropped
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FORGET_REF,
     correctness,
     "calls to `std::mem::forget` with a reference instead of an owned value"
@@ -67,6 +69,7 @@ declare_clippy_lint! {
     /// std::mem::drop(x) // A copy of x is passed to the function, leaving the
     ///                   // original unaffected
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DROP_COPY,
     correctness,
     "calls to `std::mem::drop` with a value that implements Copy"
@@ -94,6 +97,7 @@ declare_clippy_lint! {
     /// std::mem::forget(x) // A copy of x is passed to the function, leaving the
     ///                     // original unaffected
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FORGET_COPY,
     correctness,
     "calls to `std::mem::forget` with a value that implements Copy"
diff --git a/clippy_lints/src/duration_subsec.rs b/clippy_lints/src/duration_subsec.rs
index 3774de62521..3070d105014 100644
--- a/clippy_lints/src/duration_subsec.rs
+++ b/clippy_lints/src/duration_subsec.rs
@@ -33,6 +33,7 @@ declare_clippy_lint! {
     /// let _micros = dur.subsec_micros();
     /// let _millis = dur.subsec_millis();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DURATION_SUBSEC,
     complexity,
     "checks for calculation of subsecond microseconds or milliseconds"
diff --git a/clippy_lints/src/else_if_without_else.rs b/clippy_lints/src/else_if_without_else.rs
index b64246515f3..92c56c762aa 100644
--- a/clippy_lints/src/else_if_without_else.rs
+++ b/clippy_lints/src/else_if_without_else.rs
@@ -40,6 +40,7 @@ declare_clippy_lint! {
     ///     // We don't care about zero.
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ELSE_IF_WITHOUT_ELSE,
     restriction,
     "`if` expression with an `else if`, but without a final `else` branch"
diff --git a/clippy_lints/src/empty_enum.rs b/clippy_lints/src/empty_enum.rs
index 3453c2da278..af9e65e6361 100644
--- a/clippy_lints/src/empty_enum.rs
+++ b/clippy_lints/src/empty_enum.rs
@@ -34,6 +34,7 @@ declare_clippy_lint! {
     ///
     /// struct Test(!);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EMPTY_ENUM,
     pedantic,
     "enum with no variants"
diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs
index 57fd24bd4f0..3d92eb16870 100644
--- a/clippy_lints/src/entry.rs
+++ b/clippy_lints/src/entry.rs
@@ -54,6 +54,7 @@ declare_clippy_lint! {
     /// # let v = 1;
     /// map.entry(k).or_insert(v);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MAP_ENTRY,
     perf,
     "use of `contains_key` followed by `insert` on a `HashMap` or `BTreeMap`"
diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs
index a2c3c7a7b49..3b6661c817b 100644
--- a/clippy_lints/src/enum_clike.rs
+++ b/clippy_lints/src/enum_clike.rs
@@ -28,6 +28,7 @@ declare_clippy_lint! {
     ///     Y = 0,
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ENUM_CLIKE_UNPORTABLE_VARIANT,
     correctness,
     "C-like enums that are `repr(isize/usize)` and have values that don't fit into an `i32`"
diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs
index 404b67c8f29..fc3a35efaf8 100644
--- a/clippy_lints/src/enum_variants.rs
+++ b/clippy_lints/src/enum_variants.rs
@@ -34,6 +34,7 @@ declare_clippy_lint! {
     ///     Battenberg,
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ENUM_VARIANT_NAMES,
     style,
     "enums where all variants share a prefix/postfix"
@@ -59,6 +60,7 @@ declare_clippy_lint! {
     ///     struct BlackForest;
     /// }
     /// ```
+    #[clippy::version = "1.33.0"]
     pub MODULE_NAME_REPETITIONS,
     pedantic,
     "type names prefixed/postfixed with their containing module's name"
@@ -89,6 +91,7 @@ declare_clippy_lint! {
     ///     ...
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MODULE_INCEPTION,
     style,
     "modules that have the same name as their parent module"
diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs
index c034c849b55..10123460527 100644
--- a/clippy_lints/src/eq_op.rs
+++ b/clippy_lints/src/eq_op.rs
@@ -34,6 +34,7 @@ declare_clippy_lint! {
     /// # let b = 4;
     /// assert_eq!(a, a);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EQ_OP,
     correctness,
     "equal operands on both sides of a comparison or bitwise combination (e.g., `x == x`)"
@@ -59,6 +60,7 @@ declare_clippy_lint! {
     /// // Good
     /// x == *y
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub OP_REF,
     style,
     "taking a reference to satisfy the type constraints on `==`"
diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs
index e8b1d6f6eda..8905cc0de45 100644
--- a/clippy_lints/src/equatable_if_let.rs
+++ b/clippy_lints/src/equatable_if_let.rs
@@ -32,6 +32,7 @@ declare_clippy_lint! {
     ///     do_thing();
     /// }
     /// ```
+    #[clippy::version = "1.57.0"]
     pub EQUATABLE_IF_LET,
     nursery,
     "using pattern matching instead of equality"
diff --git a/clippy_lints/src/erasing_op.rs b/clippy_lints/src/erasing_op.rs
index d0944c37cf5..d49cec26be5 100644
--- a/clippy_lints/src/erasing_op.rs
+++ b/clippy_lints/src/erasing_op.rs
@@ -21,6 +21,7 @@ declare_clippy_lint! {
     /// 0 * x;
     /// x & 0;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ERASING_OP,
     correctness,
     "using erasing operations, e.g., `x * 0` or `y & 0`"
diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs
index 75b1c882c23..bc5d2f6278d 100644
--- a/clippy_lints/src/escape.rs
+++ b/clippy_lints/src/escape.rs
@@ -41,6 +41,7 @@ declare_clippy_lint! {
     /// foo(x);
     /// println!("{}", x);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub BOXED_LOCAL,
     perf,
     "using `Box<T>` where unnecessary"
diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs
index 8def6529b3b..5a4b4247104 100644
--- a/clippy_lints/src/eta_reduction.rs
+++ b/clippy_lints/src/eta_reduction.rs
@@ -39,6 +39,7 @@ declare_clippy_lint! {
     /// ```
     /// where `foo(_)` is a plain function that takes the exact argument type of
     /// `x`.
+    #[clippy::version = "pre 1.29.0"]
     pub REDUNDANT_CLOSURE,
     style,
     "redundant closures, i.e., `|a| foo(a)` (which can be written as just `foo`)"
@@ -60,6 +61,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// Some('a').map(char::to_uppercase);
     /// ```
+    #[clippy::version = "1.35.0"]
     pub REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
     pedantic,
     "redundant closures for method calls"
diff --git a/clippy_lints/src/eval_order_dependence.rs b/clippy_lints/src/eval_order_dependence.rs
index 1b56dd4b081..cdac9f3e6e1 100644
--- a/clippy_lints/src/eval_order_dependence.rs
+++ b/clippy_lints/src/eval_order_dependence.rs
@@ -40,6 +40,7 @@ declare_clippy_lint! {
     /// };
     /// let a = tmp + x;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EVAL_ORDER_DEPENDENCE,
     suspicious,
     "whether a variable read occurs before a write depends on sub-expression evaluation order"
@@ -67,6 +68,7 @@ declare_clippy_lint! {
     /// let x = (a, b, c, panic!());
     /// // can simply be replaced by `panic!()`
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DIVERGING_SUB_EXPRESSION,
     complexity,
     "whether an expression contains a diverging sub expression"
diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs
index 06171702f75..5435e15de7b 100644
--- a/clippy_lints/src/excessive_bools.rs
+++ b/clippy_lints/src/excessive_bools.rs
@@ -37,6 +37,7 @@ declare_clippy_lint! {
     ///     Finished,
     /// }
     /// ```
+    #[clippy::version = "1.43.0"]
     pub STRUCT_EXCESSIVE_BOOLS,
     pedantic,
     "using too many bools in a struct"
@@ -75,6 +76,7 @@ declare_clippy_lint! {
     ///
     /// fn f(shape: Shape, temperature: Temperature) { ... }
     /// ```
+    #[clippy::version = "1.43.0"]
     pub FN_PARAMS_EXCESSIVE_BOOLS,
     pedantic,
     "using too many bools in function parameters"
diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs
index bb4684ce38b..b0f50b5c144 100644
--- a/clippy_lints/src/exhaustive_items.rs
+++ b/clippy_lints/src/exhaustive_items.rs
@@ -31,6 +31,7 @@ declare_clippy_lint! {
     ///     Baz
     /// }
     /// ```
+    #[clippy::version = "1.51.0"]
     pub EXHAUSTIVE_ENUMS,
     restriction,
     "detects exported enums that have not been marked #[non_exhaustive]"
@@ -60,6 +61,7 @@ declare_clippy_lint! {
     ///     baz: String,
     /// }
     /// ```
+    #[clippy::version = "1.51.0"]
     pub EXHAUSTIVE_STRUCTS,
     restriction,
     "detects exported structs that have not been marked #[non_exhaustive]"
diff --git a/clippy_lints/src/exit.rs b/clippy_lints/src/exit.rs
index 9cd5b2d9f44..d64cc61916c 100644
--- a/clippy_lints/src/exit.rs
+++ b/clippy_lints/src/exit.rs
@@ -18,6 +18,7 @@ declare_clippy_lint! {
     /// ```ignore
     /// std::process::exit(0)
     /// ```
+    #[clippy::version = "1.41.0"]
     pub EXIT,
     restriction,
     "`std::process::exit` is called, terminating the program"
diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs
index 4f46ef906f4..6b327b9ce17 100644
--- a/clippy_lints/src/explicit_write.rs
+++ b/clippy_lints/src/explicit_write.rs
@@ -23,6 +23,7 @@ declare_clippy_lint! {
     /// // this would be clearer as `eprintln!("foo: {:?}", bar);`
     /// writeln!(&mut std::io::stderr(), "foo: {:?}", bar).unwrap();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EXPLICIT_WRITE,
     complexity,
     "using the `write!()` family of functions instead of the `print!()` family of functions, when using the latter would work"
diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs
index 70337f5bbeb..05d300058cf 100644
--- a/clippy_lints/src/fallible_impl_from.rs
+++ b/clippy_lints/src/fallible_impl_from.rs
@@ -44,6 +44,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FALLIBLE_IMPL_FROM,
     nursery,
     "Warn on impls of `From<..>` that contain `panic!()` or `unwrap()`"
diff --git a/clippy_lints/src/feature_name.rs b/clippy_lints/src/feature_name.rs
index f534327f7a0..dc6bef52ddd 100644
--- a/clippy_lints/src/feature_name.rs
+++ b/clippy_lints/src/feature_name.rs
@@ -31,6 +31,7 @@ declare_clippy_lint! {
     /// ghi = []
     /// ```
     ///
+    #[clippy::version = "1.57.0"]
     pub REDUNDANT_FEATURE_NAMES,
     cargo,
     "usage of a redundant feature name"
@@ -60,6 +61,7 @@ declare_clippy_lint! {
     /// def = []
     ///
     /// ```
+    #[clippy::version = "1.57.0"]
     pub NEGATIVE_FEATURE_NAMES,
     cargo,
     "usage of a negative feature name"
diff --git a/clippy_lints/src/float_equality_without_abs.rs b/clippy_lints/src/float_equality_without_abs.rs
index c33d80b8e8e..ca8886228de 100644
--- a/clippy_lints/src/float_equality_without_abs.rs
+++ b/clippy_lints/src/float_equality_without_abs.rs
@@ -37,6 +37,7 @@ declare_clippy_lint! {
     ///     (a - b).abs() < f32::EPSILON
     /// }
     /// ```
+    #[clippy::version = "1.48.0"]
     pub FLOAT_EQUALITY_WITHOUT_ABS,
     suspicious,
     "float equality check without `.abs()`"
diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs
index 1e8a5bd7d34..d30dede833c 100644
--- a/clippy_lints/src/float_literal.rs
+++ b/clippy_lints/src/float_literal.rs
@@ -27,6 +27,7 @@ declare_clippy_lint! {
     /// let v: f64 = 0.123_456_789_9;
     /// println!("{}", v); //  0.123_456_789_9
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EXCESSIVE_PRECISION,
     style,
     "excessive precision for float literal"
@@ -50,6 +51,7 @@ declare_clippy_lint! {
     /// let _: f32 = 16_777_216.0;
     /// let _: f64 = 16_777_217.0;
     /// ```
+    #[clippy::version = "1.43.0"]
     pub LOSSY_FLOAT_LITERAL,
     restriction,
     "lossy whole number float literals"
diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs
index eda611117ba..914723a4802 100644
--- a/clippy_lints/src/floating_point_arithmetic.rs
+++ b/clippy_lints/src/floating_point_arithmetic.rs
@@ -43,6 +43,7 @@ declare_clippy_lint! {
     /// let _ = a.ln_1p();
     /// let _ = a.exp_m1();
     /// ```
+    #[clippy::version = "1.43.0"]
     pub IMPRECISE_FLOPS,
     nursery,
     "usage of imprecise floating point operations"
@@ -99,6 +100,7 @@ declare_clippy_lint! {
     /// let _ = a.abs();
     /// let _ = -a.abs();
     /// ```
+    #[clippy::version = "1.43.0"]
     pub SUBOPTIMAL_FLOPS,
     nursery,
     "usage of sub-optimal floating point operations"
diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs
index 7169ac9ad6c..3f043e5f2f1 100644
--- a/clippy_lints/src/format.rs
+++ b/clippy_lints/src/format.rs
@@ -33,6 +33,7 @@ declare_clippy_lint! {
     /// // Good
     /// foo.to_owned();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub USELESS_FORMAT,
     complexity,
     "useless use of `format!`"
diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs
index bb30accf145..f0e1a67dcdd 100644
--- a/clippy_lints/src/format_args.rs
+++ b/clippy_lints/src/format_args.rs
@@ -31,6 +31,7 @@ declare_clippy_lint! {
     /// # use std::panic::Location;
     /// println!("error: something failed at {}", Location::caller());
     /// ```
+    #[clippy::version = "1.58.0"]
     pub FORMAT_IN_FORMAT_ARGS,
     perf,
     "`format!` used in a macro that does formatting"
@@ -56,6 +57,7 @@ declare_clippy_lint! {
     /// # use std::panic::Location;
     /// println!("error: something failed at {}", Location::caller());
     /// ```
+    #[clippy::version = "1.58.0"]
     pub TO_STRING_IN_FORMAT_ARGS,
     perf,
     "`to_string` applied to a type that implements `Display` in format args"
diff --git a/clippy_lints/src/formatting.rs b/clippy_lints/src/formatting.rs
index b4f186525c5..3e85c8a9c80 100644
--- a/clippy_lints/src/formatting.rs
+++ b/clippy_lints/src/formatting.rs
@@ -21,6 +21,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// a =- 42; // confusing, should it be `a -= 42` or `a = -42`?
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SUSPICIOUS_ASSIGNMENT_FORMATTING,
     suspicious,
     "suspicious formatting of `*=`, `-=` or `!=`"
@@ -43,6 +44,7 @@ declare_clippy_lint! {
     /// if foo &&! bar { // this should be `foo && !bar` but looks like a different operator
     /// }
     /// ```
+    #[clippy::version = "1.40.0"]
     pub SUSPICIOUS_UNARY_OP_FORMATTING,
     suspicious,
     "suspicious formatting of unary `-` or `!` on the RHS of a BinOp"
@@ -79,6 +81,7 @@ declare_clippy_lint! {
     /// if bar { // this is the `else` block of the previous `if`, but should it be?
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SUSPICIOUS_ELSE_FORMATTING,
     suspicious,
     "suspicious formatting of `else`"
@@ -99,6 +102,7 @@ declare_clippy_lint! {
     ///     -4, -5, -6
     /// ];
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub POSSIBLE_MISSING_COMMA,
     correctness,
     "possible missing comma in array"
diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs
index 347c6eb12cb..866ff216f84 100644
--- a/clippy_lints/src/from_over_into.rs
+++ b/clippy_lints/src/from_over_into.rs
@@ -34,6 +34,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.51.0"]
     pub FROM_OVER_INTO,
     style,
     "Warns on implementations of `Into<..>` to use `From<..>`"
diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs
index 98ce3db025c..73e800073b0 100644
--- a/clippy_lints/src/from_str_radix_10.rs
+++ b/clippy_lints/src/from_str_radix_10.rs
@@ -35,6 +35,7 @@ declare_clippy_lint! {
     /// let input: &str = get_input();
     /// let num: u16 = input.parse()?;
     /// ```
+    #[clippy::version = "1.52.0"]
     pub FROM_STR_RADIX_10,
     style,
     "from_str_radix with radix 10"
diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs
index d7c5ec9ba35..ad031cbc09d 100644
--- a/clippy_lints/src/functions/mod.rs
+++ b/clippy_lints/src/functions/mod.rs
@@ -26,6 +26,7 @@ declare_clippy_lint! {
     ///     // ..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TOO_MANY_ARGUMENTS,
     complexity,
     "functions with too many arguments"
@@ -49,6 +50,7 @@ declare_clippy_lint! {
     ///     println!("");
     /// }
     /// ```
+    #[clippy::version = "1.34.0"]
     pub TOO_MANY_LINES,
     pedantic,
     "functions with too many lines"
@@ -84,6 +86,7 @@ declare_clippy_lint! {
     ///     println!("{}", unsafe { *x });
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NOT_UNSAFE_PTR_ARG_DEREF,
     correctness,
     "public functions dereferencing raw pointer arguments but not marked `unsafe`"
@@ -103,6 +106,7 @@ declare_clippy_lint! {
     /// #[must_use]
     /// fn useless() { }
     /// ```
+    #[clippy::version = "1.40.0"]
     pub MUST_USE_UNIT,
     style,
     "`#[must_use]` attribute on a unit-returning function / method"
@@ -126,6 +130,7 @@ declare_clippy_lint! {
     ///     unimplemented!();
     /// }
     /// ```
+    #[clippy::version = "1.40.0"]
     pub DOUBLE_MUST_USE,
     style,
     "`#[must_use]` attribute on a `#[must_use]`-returning function / method"
@@ -155,6 +160,7 @@ declare_clippy_lint! {
     /// // this could be annotated with `#[must_use]`.
     /// fn id<T>(t: T) -> T { t }
     /// ```
+    #[clippy::version = "1.40.0"]
     pub MUST_USE_CANDIDATE,
     pedantic,
     "function or method that could take a `#[must_use]` attribute"
@@ -204,6 +210,7 @@ declare_clippy_lint! {
     ///
     /// Note that there are crates that simplify creating the error type, e.g.
     /// [`thiserror`](https://docs.rs/thiserror).
+    #[clippy::version = "1.49.0"]
     pub RESULT_UNIT_ERR,
     style,
     "public function returning `Result` with an `Err` type of `()`"
diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs
index e18442515b8..03af498eae3 100644
--- a/clippy_lints/src/future_not_send.rs
+++ b/clippy_lints/src/future_not_send.rs
@@ -41,6 +41,7 @@ declare_clippy_lint! {
     /// ```rust
     /// async fn is_send(bytes: std::sync::Arc<[u8]>) {}
     /// ```
+    #[clippy::version = "1.44.0"]
     pub FUTURE_NOT_SEND,
     nursery,
     "public Futures must be Send"
diff --git a/clippy_lints/src/get_last_with_len.rs b/clippy_lints/src/get_last_with_len.rs
index f3929b0f1e6..edca701869e 100644
--- a/clippy_lints/src/get_last_with_len.rs
+++ b/clippy_lints/src/get_last_with_len.rs
@@ -39,6 +39,7 @@ declare_clippy_lint! {
     /// let x = vec![2, 3, 5];
     /// let last_element = x.last();
     /// ```
+    #[clippy::version = "1.37.0"]
     pub GET_LAST_WITH_LEN,
     complexity,
     "Using `x.get(x.len() - 1)` when `x.last()` is correct and simpler"
diff --git a/clippy_lints/src/identity_op.rs b/clippy_lints/src/identity_op.rs
index 414f465c494..b4e7bbc7671 100644
--- a/clippy_lints/src/identity_op.rs
+++ b/clippy_lints/src/identity_op.rs
@@ -22,6 +22,7 @@ declare_clippy_lint! {
     /// # let x = 1;
     /// x / 1 + 0 * 1 - 0 | 0;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub IDENTITY_OP,
     complexity,
     "using identity operations, e.g., `x + 0` or `y / 1`"
diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs
index a4118bf54b6..e20741d2407 100644
--- a/clippy_lints/src/if_let_mutex.rs
+++ b/clippy_lints/src/if_let_mutex.rs
@@ -36,6 +36,7 @@ declare_clippy_lint! {
     ///     use_locked(locked);
     /// }
     /// ```
+    #[clippy::version = "1.45.0"]
     pub IF_LET_MUTEX,
     correctness,
     "locking a `Mutex` in an `if let` block can cause deadlocks"
diff --git a/clippy_lints/src/if_not_else.rs b/clippy_lints/src/if_not_else.rs
index ac938156237..3d59b783337 100644
--- a/clippy_lints/src/if_not_else.rs
+++ b/clippy_lints/src/if_not_else.rs
@@ -39,6 +39,7 @@ declare_clippy_lint! {
     ///     a()
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub IF_NOT_ELSE,
     pedantic,
     "`if` branches that could be swapped so no negation operation is necessary on the condition"
diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs
index a2dac57454f..d95f5d41071 100644
--- a/clippy_lints/src/if_then_some_else_none.rs
+++ b/clippy_lints/src/if_then_some_else_none.rs
@@ -36,6 +36,7 @@ declare_clippy_lint! {
     ///     42
     /// });
     /// ```
+    #[clippy::version = "1.53.0"]
     pub IF_THEN_SOME_ELSE_NONE,
     restriction,
     "Finds if-else that could be written using `bool::then`"
diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs
index 81eb51e6f7c..6358228dd47 100644
--- a/clippy_lints/src/implicit_hasher.rs
+++ b/clippy_lints/src/implicit_hasher.rs
@@ -54,6 +54,7 @@ declare_clippy_lint! {
     ///
     /// pub fn foo<S: BuildHasher>(map: &mut HashMap<i32, i32, S>) { }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub IMPLICIT_HASHER,
     pedantic,
     "missing generalization over different hashers"
diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs
index 6d7c71d4082..07caeada80d 100644
--- a/clippy_lints/src/implicit_return.rs
+++ b/clippy_lints/src/implicit_return.rs
@@ -35,6 +35,7 @@ declare_clippy_lint! {
     ///     return x;
     /// }
     /// ```
+    #[clippy::version = "1.33.0"]
     pub IMPLICIT_RETURN,
     restriction,
     "use a return statement like `return expr` instead of an expression"
diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs
index ecbbbc5bc64..4088c54623d 100644
--- a/clippy_lints/src/implicit_saturating_sub.rs
+++ b/clippy_lints/src/implicit_saturating_sub.rs
@@ -30,6 +30,7 @@ declare_clippy_lint! {
     /// // Good
     /// i = i.saturating_sub(1);
     /// ```
+    #[clippy::version = "1.44.0"]
     pub IMPLICIT_SATURATING_SUB,
     pedantic,
     "Perform saturating subtraction instead of implicitly checking lower bound of data type"
diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs
index 52c92b3b122..1debdef9d86 100644
--- a/clippy_lints/src/inconsistent_struct_constructor.rs
+++ b/clippy_lints/src/inconsistent_struct_constructor.rs
@@ -55,6 +55,7 @@ declare_clippy_lint! {
     /// # let y = 2;
     /// Foo { x, y };
     /// ```
+    #[clippy::version = "1.52.0"]
     pub INCONSISTENT_STRUCT_CONSTRUCTOR,
     pedantic,
     "the order of the field init shorthand is inconsistent with the order in the struct definition"
diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs
index f52f090d387..9ead4bb27a5 100644
--- a/clippy_lints/src/indexing_slicing.rs
+++ b/clippy_lints/src/indexing_slicing.rs
@@ -33,6 +33,7 @@ declare_clippy_lint! {
     /// x[0];
     /// x[3];
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub OUT_OF_BOUNDS_INDEXING,
     correctness,
     "out of bounds constant indexing"
@@ -85,6 +86,7 @@ declare_clippy_lint! {
     /// y.get(10..);
     /// y.get(..100);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INDEXING_SLICING,
     restriction,
     "indexing/slicing usage"
diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs
index 68c1fa35fcc..c7db47a552b 100644
--- a/clippy_lints/src/infinite_iter.rs
+++ b/clippy_lints/src/infinite_iter.rs
@@ -20,6 +20,7 @@ declare_clippy_lint! {
     ///
     /// iter::repeat(1_u8).collect::<Vec<_>>();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INFINITE_ITER,
     correctness,
     "infinite iteration"
@@ -42,6 +43,7 @@ declare_clippy_lint! {
     /// let infinite_iter = 0..;
     /// [0..].iter().zip(infinite_iter.take_while(|x| *x > 5));
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MAYBE_INFINITE_ITER,
     pedantic,
     "possible infinite iteration"
diff --git a/clippy_lints/src/inherent_impl.rs b/clippy_lints/src/inherent_impl.rs
index bd0b2964309..254d3f8a4d0 100644
--- a/clippy_lints/src/inherent_impl.rs
+++ b/clippy_lints/src/inherent_impl.rs
@@ -36,6 +36,7 @@ declare_clippy_lint! {
     ///     fn other() {}
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MULTIPLE_INHERENT_IMPL,
     restriction,
     "Multiple inherent impl that could be grouped"
diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs
index 61dd0eb4af7..60d234cd6f0 100644
--- a/clippy_lints/src/inherent_to_string.rs
+++ b/clippy_lints/src/inherent_to_string.rs
@@ -41,6 +41,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.38.0"]
     pub INHERENT_TO_STRING,
     style,
     "type implements inherent method `to_string()`, but should instead implement the `Display` trait"
@@ -88,6 +89,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.38.0"]
     pub INHERENT_TO_STRING_SHADOW_DISPLAY,
     correctness,
     "type implements inherent method `to_string()`, which gets shadowed by the implementation of the `Display` trait"
diff --git a/clippy_lints/src/inline_fn_without_body.rs b/clippy_lints/src/inline_fn_without_body.rs
index 3e3df903f17..df69d3dcc51 100644
--- a/clippy_lints/src/inline_fn_without_body.rs
+++ b/clippy_lints/src/inline_fn_without_body.rs
@@ -24,6 +24,7 @@ declare_clippy_lint! {
     ///     fn name(&self) -> &'static str;
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INLINE_FN_WITHOUT_BODY,
     correctness,
     "use of `#[inline]` on trait methods without bodies"
diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs
index 6850e0c3476..3716d36ad88 100644
--- a/clippy_lints/src/int_plus_one.rs
+++ b/clippy_lints/src/int_plus_one.rs
@@ -28,6 +28,7 @@ declare_clippy_lint! {
     /// # let y = 1;
     /// if x > y {}
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INT_PLUS_ONE,
     complexity,
     "instead of using `x >= y + 1`, use `x > y`"
diff --git a/clippy_lints/src/integer_division.rs b/clippy_lints/src/integer_division.rs
index c962e814fa5..fa786205678 100644
--- a/clippy_lints/src/integer_division.rs
+++ b/clippy_lints/src/integer_division.rs
@@ -23,6 +23,7 @@ declare_clippy_lint! {
     /// let x = 3f32 / 2f32;
     /// println!("{}", x);
     /// ```
+    #[clippy::version = "1.37.0"]
     pub INTEGER_DIVISION,
     restriction,
     "integer division may cause loss of precision"
diff --git a/clippy_lints/src/invalid_upcast_comparisons.rs b/clippy_lints/src/invalid_upcast_comparisons.rs
index 82438d85c7a..36e03e50a8e 100644
--- a/clippy_lints/src/invalid_upcast_comparisons.rs
+++ b/clippy_lints/src/invalid_upcast_comparisons.rs
@@ -30,6 +30,7 @@ declare_clippy_lint! {
     /// let x: u8 = 1;
     /// (x as u32) > 300;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INVALID_UPCAST_COMPARISONS,
     pedantic,
     "a comparison involving an upcast which is always true or false"
diff --git a/clippy_lints/src/items_after_statements.rs b/clippy_lints/src/items_after_statements.rs
index 3736d237642..b118d3c8b87 100644
--- a/clippy_lints/src/items_after_statements.rs
+++ b/clippy_lints/src/items_after_statements.rs
@@ -45,6 +45,7 @@ declare_clippy_lint! {
     ///     foo(); // prints "foo"
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ITEMS_AFTER_STATEMENTS,
     pedantic,
     "blocks where an item comes after a statement"
diff --git a/clippy_lints/src/iter_not_returning_iterator.rs b/clippy_lints/src/iter_not_returning_iterator.rs
index 6c779348ca2..968bbc524b2 100644
--- a/clippy_lints/src/iter_not_returning_iterator.rs
+++ b/clippy_lints/src/iter_not_returning_iterator.rs
@@ -32,6 +32,7 @@ declare_clippy_lint! {
     ///    }
     /// }
     /// ```
+    #[clippy::version = "1.57.0"]
     pub ITER_NOT_RETURNING_ITERATOR,
     pedantic,
     "methods named `iter` or `iter_mut` that do not return an `Iterator`"
diff --git a/clippy_lints/src/large_const_arrays.rs b/clippy_lints/src/large_const_arrays.rs
index fe6814e35d0..80260e4cd83 100644
--- a/clippy_lints/src/large_const_arrays.rs
+++ b/clippy_lints/src/large_const_arrays.rs
@@ -27,6 +27,7 @@ declare_clippy_lint! {
     /// // Good
     /// pub static a = [0u32; 1_000_000];
     /// ```
+    #[clippy::version = "1.44.0"]
     pub LARGE_CONST_ARRAYS,
     perf,
     "large non-scalar const array may cause performance overhead"
diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs
index 392166237be..0191713f60d 100644
--- a/clippy_lints/src/large_enum_variant.rs
+++ b/clippy_lints/src/large_enum_variant.rs
@@ -40,6 +40,7 @@ declare_clippy_lint! {
     ///     B(Box<[i32; 8000]>),
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub LARGE_ENUM_VARIANT,
     perf,
     "large size difference between variants on an enum"
diff --git a/clippy_lints/src/large_stack_arrays.rs b/clippy_lints/src/large_stack_arrays.rs
index bbb6c1f902c..1cc2c28c04a 100644
--- a/clippy_lints/src/large_stack_arrays.rs
+++ b/clippy_lints/src/large_stack_arrays.rs
@@ -19,6 +19,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// let a = [0u32; 1_000_000];
     /// ```
+    #[clippy::version = "1.41.0"]
     pub LARGE_STACK_ARRAYS,
     pedantic,
     "allocating large arrays on stack may cause stack overflow"
diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs
index f336fb9d42f..09cd0d22d8b 100644
--- a/clippy_lints/src/len_zero.rs
+++ b/clippy_lints/src/len_zero.rs
@@ -46,6 +46,7 @@ declare_clippy_lint! {
     ///     ..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub LEN_ZERO,
     style,
     "checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` could be used instead"
@@ -71,6 +72,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub LEN_WITHOUT_IS_EMPTY,
     style,
     "traits or impls with a public `len` method but no corresponding `is_empty` method"
@@ -108,6 +110,7 @@ declare_clippy_lint! {
     ///     ..
     /// }
     /// ```
+    #[clippy::version = "1.49.0"]
     pub COMPARISON_TO_EMPTY,
     style,
     "checking `x == \"\"` or `x == []` (or similar) when `.is_empty()` could be used instead"
diff --git a/clippy_lints/src/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs
index 7f2c7b707f0..db09d00d730 100644
--- a/clippy_lints/src/let_if_seq.rs
+++ b/clippy_lints/src/let_if_seq.rs
@@ -48,6 +48,7 @@ declare_clippy_lint! {
     ///     None
     /// };
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub USELESS_LET_IF_SEQ,
     nursery,
     "unidiomatic `let mut` declaration followed by initialization in `if`"
diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs
index 9efd7aba7e8..557051fb8d2 100644
--- a/clippy_lints/src/let_underscore.rs
+++ b/clippy_lints/src/let_underscore.rs
@@ -26,6 +26,7 @@ declare_clippy_lint! {
     /// // is_ok() is marked #[must_use]
     /// let _ = f().is_ok();
     /// ```
+    #[clippy::version = "1.42.0"]
     pub LET_UNDERSCORE_MUST_USE,
     restriction,
     "non-binding let on a `#[must_use]` expression"
@@ -53,6 +54,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// let _lock = mutex.lock();
     /// ```
+    #[clippy::version = "1.43.0"]
     pub LET_UNDERSCORE_LOCK,
     correctness,
     "non-binding let on a synchronization lock"
@@ -94,6 +96,7 @@ declare_clippy_lint! {
     ///     // dropped at end of scope
     /// }
     /// ```
+    #[clippy::version = "1.50.0"]
     pub LET_UNDERSCORE_DROP,
     pedantic,
     "non-binding let on a type that implements `Drop`"
diff --git a/clippy_lints/src/lib.register_internal.rs b/clippy_lints/src/lib.register_internal.rs
index c8c1e0262ab..7d4c7d2adb5 100644
--- a/clippy_lints/src/lib.register_internal.rs
+++ b/clippy_lints/src/lib.register_internal.rs
@@ -9,9 +9,11 @@ store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![
     LintId::of(utils::internal_lints::DEFAULT_LINT),
     LintId::of(utils::internal_lints::IF_CHAIN_STYLE),
     LintId::of(utils::internal_lints::INTERNING_DEFINED_SYMBOL),
+    LintId::of(utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE),
     LintId::of(utils::internal_lints::INVALID_PATHS),
     LintId::of(utils::internal_lints::LINT_WITHOUT_LINT_PASS),
     LintId::of(utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM),
+    LintId::of(utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE),
     LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA),
     LintId::of(utils::internal_lints::PRODUCE_ICE),
     LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR),
diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs
index 2cb86418e3c..b32c9b060ae 100644
--- a/clippy_lints/src/lib.register_lints.rs
+++ b/clippy_lints/src/lib.register_lints.rs
@@ -16,12 +16,16 @@ store.register_lints(&[
     #[cfg(feature = "internal-lints")]
     utils::internal_lints::INTERNING_DEFINED_SYMBOL,
     #[cfg(feature = "internal-lints")]
+    utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE,
+    #[cfg(feature = "internal-lints")]
     utils::internal_lints::INVALID_PATHS,
     #[cfg(feature = "internal-lints")]
     utils::internal_lints::LINT_WITHOUT_LINT_PASS,
     #[cfg(feature = "internal-lints")]
     utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
     #[cfg(feature = "internal-lints")]
+    utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE,
+    #[cfg(feature = "internal-lints")]
     utils::internal_lints::OUTER_EXPN_EXPN_DATA,
     #[cfg(feature = "internal-lints")]
     utils::internal_lints::PRODUCE_ICE,
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 02fb6b79dc4..6aa136371cf 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -154,6 +154,10 @@ macro_rules! declare_clippy_lint {
 
 #[cfg(feature = "metadata-collector-lint")]
 mod deprecated_lints;
+#[cfg_attr(
+    any(feature = "internal-lints", feature = "metadata-collector-lint"),
+    allow(clippy::missing_clippy_version_attribute)
+)]
 mod utils;
 
 // begin lints modules, do not remove this comment, it’s used in `update_lints`
diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs
index 6565d5a6d70..fad3343d128 100644
--- a/clippy_lints/src/lifetimes.rs
+++ b/clippy_lints/src/lifetimes.rs
@@ -45,6 +45,7 @@ declare_clippy_lint! {
     ///     x
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEEDLESS_LIFETIMES,
     complexity,
     "using explicit lifetimes for references in function arguments when elision rules \
@@ -73,6 +74,7 @@ declare_clippy_lint! {
     ///     // ...
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EXTRA_UNUSED_LIFETIMES,
     complexity,
     "unused lifetimes in function definitions"
diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs
index 0e36ab085a3..130543bbbee 100644
--- a/clippy_lints/src/literal_representation.rs
+++ b/clippy_lints/src/literal_representation.rs
@@ -28,6 +28,7 @@ declare_clippy_lint! {
     /// // Good
     /// let x: u64 = 61_864_918_973_511;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNREADABLE_LITERAL,
     pedantic,
     "long literal without underscores"
@@ -53,6 +54,7 @@ declare_clippy_lint! {
     /// // Good
     /// 2_i32;
     /// ```
+    #[clippy::version = "1.30.0"]
     pub MISTYPED_LITERAL_SUFFIXES,
     correctness,
     "mistyped literal suffix"
@@ -75,6 +77,7 @@ declare_clippy_lint! {
     /// // Good
     /// let x: u64 = 61_864_918_973_511;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INCONSISTENT_DIGIT_GROUPING,
     style,
     "integer literals with digits grouped inconsistently"
@@ -93,6 +96,7 @@ declare_clippy_lint! {
     /// let x: u32 = 0xFFF_FFF;
     /// let y: u8 = 0b01_011_101;
     /// ```
+    #[clippy::version = "1.49.0"]
     pub UNUSUAL_BYTE_GROUPINGS,
     style,
     "binary or hex literals that aren't grouped by four"
@@ -111,6 +115,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let x: u64 = 6186491_8973511;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub LARGE_DIGIT_GROUPS,
     pedantic,
     "grouping digits into groups that are too large"
@@ -128,6 +133,7 @@ declare_clippy_lint! {
     /// `255` => `0xFF`
     /// `65_535` => `0xFFFF`
     /// `4_042_322_160` => `0xF0F0_F0F0`
+    #[clippy::version = "pre 1.29.0"]
     pub DECIMAL_LITERAL_REPRESENTATION,
     restriction,
     "using decimal representation when hexadecimal would be better"
diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs
index 5df1b796401..ccec648fa78 100644
--- a/clippy_lints/src/loops/mod.rs
+++ b/clippy_lints/src/loops/mod.rs
@@ -47,6 +47,7 @@ declare_clippy_lint! {
     /// # let mut dst = vec![0; 65];
     /// dst[64..(src.len() + 64)].clone_from_slice(&src[..]);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MANUAL_MEMCPY,
     perf,
     "manually copying items between slices"
@@ -75,6 +76,7 @@ declare_clippy_lint! {
     ///     println!("{}", i);
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEEDLESS_RANGE_LOOP,
     style,
     "for-looping over a range of indices where an iterator over items would do"
@@ -107,6 +109,7 @@ declare_clippy_lint! {
     ///     // ..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EXPLICIT_ITER_LOOP,
     pedantic,
     "for-looping over `_.iter()` or `_.iter_mut()` when `&_` or `&mut _` would do"
@@ -135,6 +138,7 @@ declare_clippy_lint! {
     ///     // ..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EXPLICIT_INTO_ITER_LOOP,
     pedantic,
     "for-looping over `_.into_iter()` when `_` would do"
@@ -158,6 +162,7 @@ declare_clippy_lint! {
     ///     ..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ITER_NEXT_LOOP,
     correctness,
     "for-looping over `_.next()` which is probably not intended"
@@ -201,6 +206,7 @@ declare_clippy_lint! {
     ///     // ..
     /// }
     /// ```
+    #[clippy::version = "1.45.0"]
     pub FOR_LOOPS_OVER_FALLIBLES,
     suspicious,
     "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`"
@@ -233,6 +239,7 @@ declare_clippy_lint! {
     ///     // .. do something with x
     /// };
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub WHILE_LET_LOOP,
     complexity,
     "`loop { if let { ... } else break }`, which can be written as a `while let` loop"
@@ -254,6 +261,7 @@ declare_clippy_lint! {
     /// // should be
     /// let len = iterator.count();
     /// ```
+    #[clippy::version = "1.30.0"]
     pub NEEDLESS_COLLECT,
     perf,
     "collecting an iterator when collect is not needed"
@@ -284,6 +292,7 @@ declare_clippy_lint! {
     /// # fn bar(bar: usize, baz: usize) {}
     /// for (i, item) in v.iter().enumerate() { bar(i, *item); }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EXPLICIT_COUNTER_LOOP,
     complexity,
     "for-looping with an explicit counter when `_.enumerate()` would do"
@@ -317,6 +326,7 @@ declare_clippy_lint! {
     /// ```no_run
     /// loop {}
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EMPTY_LOOP,
     suspicious,
     "empty `loop {}`, which should block or sleep"
@@ -336,6 +346,7 @@ declare_clippy_lint! {
     ///     ..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub WHILE_LET_ON_ITERATOR,
     style,
     "using a `while let` loop instead of a for loop on an iterator"
@@ -364,6 +375,7 @@ declare_clippy_lint! {
     ///     ..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FOR_KV_MAP,
     style,
     "looping on a map using `iter` when `keys` or `values` would do"
@@ -385,6 +397,7 @@ declare_clippy_lint! {
     ///     break;
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEVER_LOOP,
     correctness,
     "any loop that will always `break` or `return`"
@@ -420,6 +433,7 @@ declare_clippy_lint! {
     ///     println!("{}", i); // prints numbers from 0 to 42, not 0 to 21
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MUT_RANGE_BOUND,
     suspicious,
     "for loop over a range where one of the bounds is a mutable variable"
@@ -446,6 +460,7 @@ declare_clippy_lint! {
     ///     println!("let me loop forever!");
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub WHILE_IMMUTABLE_CONDITION,
     correctness,
     "variables used within while expression are not mutated in the body"
@@ -480,6 +495,7 @@ declare_clippy_lint! {
     /// let mut vec: Vec<u8> = vec![item1; 20];
     /// vec.resize(20 + 30, item2);
     /// ```
+    #[clippy::version = "1.47.0"]
     pub SAME_ITEM_PUSH,
     style,
     "the same item is pushed inside of a for loop"
@@ -506,6 +522,7 @@ declare_clippy_lint! {
     /// let item = &item1;
     /// println!("{}", item);
     /// ```
+    #[clippy::version = "1.49.0"]
     pub SINGLE_ELEMENT_LOOP,
     complexity,
     "there is no reason to have a single element loop"
@@ -537,6 +554,7 @@ declare_clippy_lint! {
     ///     println!("{}", n);
     /// }
     /// ```
+    #[clippy::version = "1.52.0"]
     pub MANUAL_FLATTEN,
     complexity,
     "for loops over `Option`s or `Result`s with a single expression can be simplified"
diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs
index bccdc3be5e9..262a26951c6 100644
--- a/clippy_lints/src/macro_use.rs
+++ b/clippy_lints/src/macro_use.rs
@@ -24,6 +24,7 @@ declare_clippy_lint! {
     /// #[macro_use]
     /// use some_macro;
     /// ```
+    #[clippy::version = "1.44.0"]
     pub MACRO_USE_IMPORTS,
     pedantic,
     "#[macro_use] is no longer needed"
diff --git a/clippy_lints/src/main_recursion.rs b/clippy_lints/src/main_recursion.rs
index 23b3ba2296e..fad8fa467d4 100644
--- a/clippy_lints/src/main_recursion.rs
+++ b/clippy_lints/src/main_recursion.rs
@@ -20,6 +20,7 @@ declare_clippy_lint! {
     ///     main();
     /// }
     /// ```
+    #[clippy::version = "1.38.0"]
     pub MAIN_RECURSION,
     style,
     "recursion using the entrypoint"
diff --git a/clippy_lints/src/manual_assert.rs b/clippy_lints/src/manual_assert.rs
index e55aa3f1850..ed3166086f7 100644
--- a/clippy_lints/src/manual_assert.rs
+++ b/clippy_lints/src/manual_assert.rs
@@ -26,6 +26,7 @@ declare_clippy_lint! {
     /// let sad_people: Vec<&str> = vec![];
     /// assert!(sad_people.is_empty(), "there are sad people: {:?}", sad_people);
     /// ```
+    #[clippy::version = "1.57.0"]
     pub MANUAL_ASSERT,
     pedantic,
     "`panic!` and only a `panic!` in `if`-then statement"
diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs
index b632af455f8..86819752f90 100644
--- a/clippy_lints/src/manual_async_fn.rs
+++ b/clippy_lints/src/manual_async_fn.rs
@@ -30,6 +30,7 @@ declare_clippy_lint! {
     /// ```rust
     /// async fn foo() -> i32 { 42 }
     /// ```
+    #[clippy::version = "1.45.0"]
     pub MANUAL_ASYNC_FN,
     style,
     "manual implementations of `async` functions can be simplified using the dedicated syntax"
diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs
index 96df3d0a490..cf5dabd9462 100644
--- a/clippy_lints/src/manual_map.rs
+++ b/clippy_lints/src/manual_map.rs
@@ -36,6 +36,7 @@ declare_clippy_lint! {
     /// ```rust
     /// Some(0).map(|x| x + 1);
     /// ```
+    #[clippy::version = "1.52.0"]
     pub MANUAL_MAP,
     style,
     "reimplementation of `map`"
diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs
index 335ea001ee4..63a72d4fdde 100644
--- a/clippy_lints/src/manual_non_exhaustive.rs
+++ b/clippy_lints/src/manual_non_exhaustive.rs
@@ -52,6 +52,7 @@ declare_clippy_lint! {
     /// #[non_exhaustive]
     /// struct T(pub i32, pub i32);
     /// ```
+    #[clippy::version = "1.45.0"]
     pub MANUAL_NON_EXHAUSTIVE,
     style,
     "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]"
diff --git a/clippy_lints/src/manual_ok_or.rs b/clippy_lints/src/manual_ok_or.rs
index cf641d0ce86..b60e2dc366b 100644
--- a/clippy_lints/src/manual_ok_or.rs
+++ b/clippy_lints/src/manual_ok_or.rs
@@ -32,6 +32,7 @@ declare_clippy_lint! {
     /// let foo: Option<i32> = None;
     /// foo.ok_or("error");
     /// ```
+    #[clippy::version = "1.49.0"]
     pub MANUAL_OK_OR,
     pedantic,
     "finds patterns that can be encoded more concisely with `Option::ok_or`"
diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs
index 4e040508b6b..f8e28f1671f 100644
--- a/clippy_lints/src/manual_strip.rs
+++ b/clippy_lints/src/manual_strip.rs
@@ -42,6 +42,7 @@ declare_clippy_lint! {
     ///     assert_eq!(end.to_uppercase(), "WORLD!");
     /// }
     /// ```
+    #[clippy::version = "1.48.0"]
     pub MANUAL_STRIP,
     complexity,
     "suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing"
diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs
index 42478e3416e..aac3c6e0de2 100644
--- a/clippy_lints/src/manual_unwrap_or.rs
+++ b/clippy_lints/src/manual_unwrap_or.rs
@@ -35,6 +35,7 @@ declare_clippy_lint! {
     /// let foo: Option<i32> = None;
     /// foo.unwrap_or(1);
     /// ```
+    #[clippy::version = "1.49.0"]
     pub MANUAL_UNWRAP_OR,
     complexity,
     "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`"
diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs
index 7db5c7e52ea..c2b78e21861 100644
--- a/clippy_lints/src/map_clone.rs
+++ b/clippy_lints/src/map_clone.rs
@@ -37,6 +37,7 @@ declare_clippy_lint! {
     /// let y = x.iter();
     /// let z = y.cloned();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MAP_CLONE,
     style,
     "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types"
diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs
index 82d3732326e..61f21d532c5 100644
--- a/clippy_lints/src/map_err_ignore.rs
+++ b/clippy_lints/src/map_err_ignore.rs
@@ -97,6 +97,7 @@ declare_clippy_lint! {
     ///         })
     /// }
     /// ```
+    #[clippy::version = "1.48.0"]
     pub MAP_ERR_IGNORE,
     restriction,
     "`map_err` should not ignore the original error"
diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs
index 40de9ffcd4e..a84de3e079b 100644
--- a/clippy_lints/src/map_unit_fn.rs
+++ b/clippy_lints/src/map_unit_fn.rs
@@ -47,6 +47,7 @@ declare_clippy_lint! {
     ///     log_err_msg(format_msg(msg));
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub OPTION_MAP_UNIT_FN,
     complexity,
     "using `option.map(f)`, where `f` is a function or closure that returns `()`"
@@ -87,6 +88,7 @@ declare_clippy_lint! {
     ///     log_err_msg(format_msg(msg));
     /// };
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub RESULT_MAP_UNIT_FN,
     complexity,
     "using `result.map(f)`, where `f` is a function or closure that returns `()`"
diff --git a/clippy_lints/src/match_on_vec_items.rs b/clippy_lints/src/match_on_vec_items.rs
index 552c9a58897..583b577ffe2 100644
--- a/clippy_lints/src/match_on_vec_items.rs
+++ b/clippy_lints/src/match_on_vec_items.rs
@@ -40,6 +40,7 @@ declare_clippy_lint! {
     ///     _ => {},
     /// }
     /// ```
+    #[clippy::version = "1.45.0"]
     pub MATCH_ON_VEC_ITEMS,
     pedantic,
     "matching on vector elements can panic"
diff --git a/clippy_lints/src/match_result_ok.rs b/clippy_lints/src/match_result_ok.rs
index ecf6ad316a4..b1839f00aae 100644
--- a/clippy_lints/src/match_result_ok.rs
+++ b/clippy_lints/src/match_result_ok.rs
@@ -38,6 +38,7 @@ declare_clippy_lint! {
     ///        vec.push(value)
     /// }
     /// ```
+    #[clippy::version = "1.57.0"]
     pub MATCH_RESULT_OK,
     style,
     "usage of `ok()` in `let Some(pat)` statements is unnecessary, match on `Ok(pat)` instead"
diff --git a/clippy_lints/src/match_str_case_mismatch.rs b/clippy_lints/src/match_str_case_mismatch.rs
index f501593c518..3316ebf4051 100644
--- a/clippy_lints/src/match_str_case_mismatch.rs
+++ b/clippy_lints/src/match_str_case_mismatch.rs
@@ -39,6 +39,7 @@ declare_clippy_lint! {
     ///     _ => {},
     /// }
     /// ```
+    #[clippy::version = "1.58.0"]
     pub MATCH_STR_CASE_MISMATCH,
     correctness,
     "creation of a case altering match expression with non-compliant arms"
diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs
index e4e533fa71a..74765a1a1de 100644
--- a/clippy_lints/src/matches.rs
+++ b/clippy_lints/src/matches.rs
@@ -57,6 +57,7 @@ declare_clippy_lint! {
     ///     bar(foo);
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SINGLE_MATCH,
     style,
     "a `match` statement with a single nontrivial arm (i.e., where the other arm is `_ => {}`) instead of `if let`"
@@ -98,6 +99,7 @@ declare_clippy_lint! {
     ///     bar(&other_ref);
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SINGLE_MATCH_ELSE,
     pedantic,
     "a `match` statement with two arms where the second arm's pattern is a placeholder instead of a specific match pattern"
@@ -129,6 +131,7 @@ declare_clippy_lint! {
     ///     _ => frob(x),
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MATCH_REF_PATS,
     style,
     "a `match` or `if let` with all arms prefixed with `&` instead of deref-ing the match expression"
@@ -163,6 +166,7 @@ declare_clippy_lint! {
     ///     bar();
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MATCH_BOOL,
     pedantic,
     "a `match` on a boolean expression instead of an `if..else` block"
@@ -185,6 +189,7 @@ declare_clippy_lint! {
     ///     _ => (),
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MATCH_OVERLAPPING_ARM,
     style,
     "a `match` with overlapping arms"
@@ -207,6 +212,7 @@ declare_clippy_lint! {
     ///     Err(_) => panic!("err"),
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MATCH_WILD_ERR_ARM,
     pedantic,
     "a `match` with `Err(_)` arm and take drastic actions"
@@ -233,6 +239,7 @@ declare_clippy_lint! {
     /// // Good
     /// let r: Option<&()> = x.as_ref();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MATCH_AS_REF,
     complexity,
     "a `match` on an Option value instead of using `as_ref()` or `as_mut`"
@@ -265,6 +272,7 @@ declare_clippy_lint! {
     ///     Foo::B(_) => {},
     /// }
     /// ```
+    #[clippy::version = "1.34.0"]
     pub WILDCARD_ENUM_MATCH_ARM,
     restriction,
     "a wildcard enum match arm using `_`"
@@ -299,6 +307,7 @@ declare_clippy_lint! {
     ///     Foo::C => {},
     /// }
     /// ```
+    #[clippy::version = "1.45.0"]
     pub MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
     pedantic,
     "a wildcard enum match for a single variant"
@@ -326,6 +335,7 @@ declare_clippy_lint! {
     ///     _ => {},
     /// }
     /// ```
+    #[clippy::version = "1.42.0"]
     pub WILDCARD_IN_OR_PATTERNS,
     complexity,
     "a wildcard pattern used with others patterns in same match arm"
@@ -361,6 +371,7 @@ declare_clippy_lint! {
     /// let wrapper = Wrapper::Data(42);
     /// let Wrapper::Data(data) = wrapper;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INFALLIBLE_DESTRUCTURING_MATCH,
     style,
     "a `match` statement with a single infallible arm instead of a `let`"
@@ -392,6 +403,7 @@ declare_clippy_lint! {
     /// // Good
     /// let (c, d) = (a, b);
     /// ```
+    #[clippy::version = "1.43.0"]
     pub MATCH_SINGLE_BINDING,
     complexity,
     "a match with a single binding instead of using `let` statement"
@@ -422,6 +434,7 @@ declare_clippy_lint! {
     ///     _ => {},
     /// }
     /// ```
+    #[clippy::version = "1.43.0"]
     pub REST_PAT_IN_FULLY_BOUND_STRUCTS,
     restriction,
     "a match on a struct that binds all fields but still uses the wildcard pattern"
@@ -477,6 +490,7 @@ declare_clippy_lint! {
     /// if IpAddr::V6(Ipv6Addr::LOCALHOST).is_ipv6() {}
     /// Ok::<i32, i32>(42).is_ok();
     /// ```
+    #[clippy::version = "1.31.0"]
     pub REDUNDANT_PATTERN_MATCHING,
     style,
     "use the proper utility function avoiding an `if let`"
@@ -513,6 +527,7 @@ declare_clippy_lint! {
     /// // Good
     /// let a = matches!(x, Some(0));
     /// ```
+    #[clippy::version = "1.47.0"]
     pub MATCH_LIKE_MATCHES_MACRO,
     style,
     "a match that could be written with the matches! macro"
@@ -557,6 +572,7 @@ declare_clippy_lint! {
     ///     Quz => quz(),
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MATCH_SAME_ARMS,
     pedantic,
     "`match` with identical arm bodies"
diff --git a/clippy_lints/src/mem_forget.rs b/clippy_lints/src/mem_forget.rs
index eb437dc47af..5ffcfd4d264 100644
--- a/clippy_lints/src/mem_forget.rs
+++ b/clippy_lints/src/mem_forget.rs
@@ -19,6 +19,7 @@ declare_clippy_lint! {
     /// # use std::rc::Rc;
     /// mem::forget(Rc::new(55))
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MEM_FORGET,
     restriction,
     "`mem::forget` usage on `Drop` types, likely to cause memory leaks"
diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs
index cf721fc65db..7fc39f17232 100644
--- a/clippy_lints/src/mem_replace.rs
+++ b/clippy_lints/src/mem_replace.rs
@@ -35,6 +35,7 @@ declare_clippy_lint! {
     /// let mut an_option = Some(0);
     /// let taken = an_option.take();
     /// ```
+    #[clippy::version = "1.31.0"]
     pub MEM_REPLACE_OPTION_WITH_NONE,
     style,
     "replacing an `Option` with `None` instead of `take()`"
@@ -66,6 +67,7 @@ declare_clippy_lint! {
     /// The [take_mut](https://docs.rs/take_mut) crate offers a sound solution,
     /// at the cost of either lazily creating a replacement value or aborting
     /// on panic, to ensure that the uninitialized value cannot be observed.
+    #[clippy::version = "1.39.0"]
     pub MEM_REPLACE_WITH_UNINIT,
     correctness,
     "`mem::replace(&mut _, mem::uninitialized())` or `mem::replace(&mut _, mem::zeroed())`"
@@ -90,6 +92,7 @@ declare_clippy_lint! {
     /// let mut text = String::from("foo");
     /// let taken = std::mem::take(&mut text);
     /// ```
+    #[clippy::version = "1.42.0"]
     pub MEM_REPLACE_WITH_DEFAULT,
     style,
     "replacing a value of type `T` with `T::default()` instead of using `std::mem::take`"
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index 6c4272f9e65..5cb849a56bc 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -99,6 +99,7 @@ declare_clippy_lint! {
     /// ```rust
     /// [1, 2, 3].iter().copied();
     /// ```
+    #[clippy::version = "1.53.0"]
     pub CLONED_INSTEAD_OF_COPIED,
     pedantic,
     "used `cloned` where `copied` could be used instead"
@@ -121,6 +122,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let nums: Vec<i32> = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect();
     /// ```
+    #[clippy::version = "1.53.0"]
     pub FLAT_MAP_OPTION,
     pedantic,
     "used `flat_map` where `filter_map` could be used instead"
@@ -166,6 +168,7 @@ declare_clippy_lint! {
     /// // Good
     /// res.expect("more helpful message");
     /// ```
+    #[clippy::version = "1.45.0"]
     pub UNWRAP_USED,
     restriction,
     "using `.unwrap()` on `Result` or `Option`, which should at least get a better message using `expect()`"
@@ -208,6 +211,7 @@ declare_clippy_lint! {
     /// res?;
     /// # Ok::<(), ()>(())
     /// ```
+    #[clippy::version = "1.45.0"]
     pub EXPECT_USED,
     restriction,
     "using `.expect()` on `Result` or `Option`, which might be better handled"
@@ -237,6 +241,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SHOULD_IMPLEMENT_TRAIT,
     style,
     "defining a method that should be implementing a std trait"
@@ -284,6 +289,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub WRONG_SELF_CONVENTION,
     style,
     "defining a method named with an established prefix (like \"into_\") that takes `self` with the wrong convention"
@@ -310,6 +316,7 @@ declare_clippy_lint! {
     /// // Good
     /// x.expect("why did I do this again?");
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub OK_EXPECT,
     style,
     "using `ok().expect()`, which gives worse error messages than calling `expect` directly on the Result"
@@ -335,6 +342,7 @@ declare_clippy_lint! {
     /// // Good
     /// x.unwrap_or_default();
     /// ```
+    #[clippy::version = "1.56.0"]
     pub UNWRAP_OR_ELSE_DEFAULT,
     style,
     "using `.unwrap_or_else(Default::default)`, which is more succinctly expressed as `.unwrap_or_default()`"
@@ -375,6 +383,7 @@ declare_clippy_lint! {
     /// // Good
     /// x.map_or_else(some_function, |a| a + 1);
     /// ```
+    #[clippy::version = "1.45.0"]
     pub MAP_UNWRAP_OR,
     pedantic,
     "using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`"
@@ -401,6 +410,7 @@ declare_clippy_lint! {
     /// // Good
     /// opt.and_then(|a| Some(a + 1));
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub OPTION_MAP_OR_NONE,
     style,
     "using `Option.map_or(None, f)`, which is more succinctly expressed as `and_then(f)`"
@@ -426,6 +436,7 @@ declare_clippy_lint! {
     /// # let r: Result<u32, &str> = Ok(1);
     /// assert_eq!(Some(1), r.ok());
     /// ```
+    #[clippy::version = "1.44.0"]
     pub RESULT_MAP_OR_INTO_OPTION,
     style,
     "using `Result.map_or(None, Some)`, which is more succinctly expressed as `ok()`"
@@ -458,6 +469,7 @@ declare_clippy_lint! {
     /// let _ = res().map(|s| if s.len() == 42 { 10 } else { 20 });
     /// let _ = res().map_err(|s| if s.len() == 42 { 10 } else { 20 });
     /// ```
+    #[clippy::version = "1.45.0"]
     pub BIND_INSTEAD_OF_MAP,
     complexity,
     "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`"
@@ -481,6 +493,7 @@ declare_clippy_lint! {
     /// # let vec = vec![1];
     /// vec.iter().find(|x| **x == 0);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FILTER_NEXT,
     complexity,
     "using `filter(p).next()`, which is more succinctly expressed as `.find(p)`"
@@ -504,6 +517,7 @@ declare_clippy_lint! {
     /// # let vec = vec![1];
     /// vec.iter().find(|x| **x != 0);
     /// ```
+    #[clippy::version = "1.42.0"]
     pub SKIP_WHILE_NEXT,
     complexity,
     "using `skip_while(p).next()`, which is more succinctly expressed as `.find(!p)`"
@@ -527,6 +541,7 @@ declare_clippy_lint! {
     /// // Good
     /// vec.iter().flat_map(|x| x.iter());
     /// ```
+    #[clippy::version = "1.31.0"]
     pub MAP_FLATTEN,
     pedantic,
     "using combinations of `flatten` and `map` which can usually be written as a single method call"
@@ -553,6 +568,7 @@ declare_clippy_lint! {
     /// ```rust
     /// (0_i32..10).filter_map(|n| n.checked_add(1));
     /// ```
+    #[clippy::version = "1.51.0"]
     pub MANUAL_FILTER_MAP,
     complexity,
     "using `_.filter(_).map(_)` in a way that can be written more simply as `filter_map(_)`"
@@ -579,6 +595,7 @@ declare_clippy_lint! {
     /// ```rust
     /// (0_i32..10).find_map(|n| n.checked_add(1));
     /// ```
+    #[clippy::version = "1.51.0"]
     pub MANUAL_FIND_MAP,
     complexity,
     "using `_.find(_).map(_)` in a way that can be written more simply as `find_map(_)`"
@@ -601,6 +618,7 @@ declare_clippy_lint! {
     /// ```rust
     ///  (0..3).find_map(|x| if x == 2 { Some(x) } else { None });
     /// ```
+    #[clippy::version = "1.36.0"]
     pub FILTER_MAP_NEXT,
     pedantic,
     "using combination of `filter_map` and `next` which can usually be written as a single method call"
@@ -623,6 +641,7 @@ declare_clippy_lint! {
     /// # let iter = vec![vec![0]].into_iter();
     /// iter.flatten();
     /// ```
+    #[clippy::version = "1.39.0"]
     pub FLAT_MAP_IDENTITY,
     complexity,
     "call to `flat_map` where `flatten` is sufficient"
@@ -652,6 +671,7 @@ declare_clippy_lint! {
     ///
     /// let _ = !"hello world".contains("world");
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SEARCH_IS_SOME,
     complexity,
     "using an iterator or string search followed by `is_some()` or `is_none()`, which is more succinctly expressed as a call to `any()` or `contains()` (with negation in case of `is_none()`)"
@@ -676,6 +696,7 @@ declare_clippy_lint! {
     /// let name = "foo";
     /// if name.starts_with('_') {};
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CHARS_NEXT_CMP,
     style,
     "using `.chars().next()` to check if a string starts with a char"
@@ -710,6 +731,7 @@ declare_clippy_lint! {
     /// # let foo = Some(String::new());
     /// foo.unwrap_or_default();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub OR_FUN_CALL,
     perf,
     "using any `*or` method with a function call, which suggests `*or_else`"
@@ -748,6 +770,7 @@ declare_clippy_lint! {
     /// # let err_msg = "I'm a teapot";
     /// foo.unwrap_or_else(|| panic!("Err {}: {}", err_code, err_msg));
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EXPECT_FUN_CALL,
     perf,
     "using any `expect` method with a function call"
@@ -765,6 +788,7 @@ declare_clippy_lint! {
     /// ```rust
     /// 42u64.clone();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CLONE_ON_COPY,
     complexity,
     "using `clone` on a `Copy` type"
@@ -792,6 +816,7 @@ declare_clippy_lint! {
     /// // Good
     /// Rc::clone(&x);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CLONE_ON_REF_PTR,
     restriction,
     "using 'clone' on a ref-counted pointer"
@@ -814,6 +839,7 @@ declare_clippy_lint! {
     ///     println!("{:p} {:p}", *y, z); // prints out the same pointer
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CLONE_DOUBLE_REF,
     correctness,
     "using `clone` on `&&T`"
@@ -837,6 +863,7 @@ declare_clippy_lint! {
     /// // OK, the specialized impl is used
     /// ["foo", "bar"].iter().map(|&s| s.to_string());
     /// ```
+    #[clippy::version = "1.40.0"]
     pub INEFFICIENT_TO_STRING,
     pedantic,
     "using `to_string` on `&&T` where `T: ToString`"
@@ -898,6 +925,7 @@ declare_clippy_lint! {
     ///     fn new() -> Self;
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEW_RET_NO_SELF,
     style,
     "not returning type containing `Self` in a `new` method"
@@ -922,6 +950,7 @@ declare_clippy_lint! {
     ///
     /// // Good
     /// _.split('x');
+    #[clippy::version = "pre 1.29.0"]
     pub SINGLE_CHAR_PATTERN,
     perf,
     "using a single-character str where a char could be used, e.g., `_.split(\"x\")`"
@@ -941,6 +970,7 @@ declare_clippy_lint! {
     ///     //..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ITERATOR_STEP_BY_ZERO,
     correctness,
     "using `Iterator::step_by(0)`, which will panic at runtime"
@@ -962,6 +992,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let _ = std::iter::empty::<Option<i32>>().flatten();
     /// ```
+    #[clippy::version = "1.53.0"]
     pub OPTION_FILTER_MAP,
     complexity,
     "filtering `Option` for `Some` then force-unwrapping, which can be one type-safe operation"
@@ -989,6 +1020,7 @@ declare_clippy_lint! {
     /// # s.insert(1);
     /// let x = s.iter().next();
     /// ```
+    #[clippy::version = "1.42.0"]
     pub ITER_NTH_ZERO,
     style,
     "replace `iter.nth(0)` with `iter.next()`"
@@ -1015,6 +1047,7 @@ declare_clippy_lint! {
     /// let bad_vec = some_vec.get(3);
     /// let bad_slice = &some_vec[..].get(3);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ITER_NTH,
     perf,
     "using `.iter().nth()` on a standard library type with O(1) element access"
@@ -1039,6 +1072,7 @@ declare_clippy_lint! {
     /// let bad_vec = some_vec.iter().nth(3);
     /// let bad_slice = &some_vec[..].iter().nth(3);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ITER_SKIP_NEXT,
     style,
     "using `.skip(x).next()` on an iterator"
@@ -1075,6 +1109,7 @@ declare_clippy_lint! {
     /// let last = some_vec[3];
     /// some_vec[0] = 1;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub GET_UNWRAP,
     restriction,
     "using `.get().unwrap()` or `.get_mut().unwrap()` when using `[]` would work instead"
@@ -1098,6 +1133,7 @@ declare_clippy_lint! {
     /// // Good
     /// a.append(&mut b);
     /// ```
+    #[clippy::version = "1.55.0"]
     pub EXTEND_WITH_DRAIN,
     perf,
     "using vec.append(&mut vec) to move the full range of a vecor to another"
@@ -1127,6 +1163,7 @@ declare_clippy_lint! {
     /// s.push_str(abc);
     /// s.push_str(&def);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub STRING_EXTEND_CHARS,
     style,
     "using `x.extend(s.chars())` where s is a `&str` or `String`"
@@ -1150,6 +1187,7 @@ declare_clippy_lint! {
     /// let s = [1, 2, 3, 4, 5];
     /// let s2: Vec<isize> = s.to_vec();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ITER_CLONED_COLLECT,
     style,
     "using `.cloned().collect()` on slice to create a `Vec`"
@@ -1174,6 +1212,7 @@ declare_clippy_lint! {
     /// // Good
     /// name.ends_with('_') || name.ends_with('-');
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CHARS_LAST_CMP,
     style,
     "using `.chars().last()` or `.chars().next_back()` to check if a string ends with a char"
@@ -1199,6 +1238,7 @@ declare_clippy_lint! {
     /// let x: &[i32] = &[1, 2, 3, 4, 5];
     /// do_stuff(x);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub USELESS_ASREF,
     complexity,
     "using `as_ref` where the types before and after the call are the same"
@@ -1221,6 +1261,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let _ = (0..3).any(|x| x > 2);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNNECESSARY_FOLD,
     style,
     "using `fold` when a more succinct alternative exists"
@@ -1250,6 +1291,7 @@ declare_clippy_lint! {
     /// // As there is no conditional check on the argument this could be written as:
     /// let _ = (0..4).map(|x| x + 1);
     /// ```
+    #[clippy::version = "1.31.0"]
     pub UNNECESSARY_FILTER_MAP,
     complexity,
     "using `filter_map` when a more succinct alternative exists"
@@ -1273,6 +1315,7 @@ declare_clippy_lint! {
     /// // Good
     /// let _ = (&vec![3, 4, 5]).iter();
     /// ```
+    #[clippy::version = "1.32.0"]
     pub INTO_ITER_ON_REF,
     style,
     "using `.into_iter()` on a reference"
@@ -1292,6 +1335,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let _ = (0..3).map(|x| x + 2).count();
     /// ```
+    #[clippy::version = "1.39.0"]
     pub SUSPICIOUS_MAP,
     suspicious,
     "suspicious usage of map"
@@ -1326,6 +1370,7 @@ declare_clippy_lint! {
     ///     MaybeUninit::uninit().assume_init()
     /// };
     /// ```
+    #[clippy::version = "1.39.0"]
     pub UNINIT_ASSUMED_INIT,
     correctness,
     "`MaybeUninit::uninit().assume_init()`"
@@ -1354,6 +1399,7 @@ declare_clippy_lint! {
     /// let add = x.saturating_add(y);
     /// let sub = x.saturating_sub(y);
     /// ```
+    #[clippy::version = "1.39.0"]
     pub MANUAL_SATURATING_ARITHMETIC,
     style,
     "`.chcked_add/sub(x).unwrap_or(MAX/MIN)`"
@@ -1371,6 +1417,7 @@ declare_clippy_lint! {
     /// ```rust
     /// unsafe { (&() as *const ()).offset(1) };
     /// ```
+    #[clippy::version = "1.41.0"]
     pub ZST_OFFSET,
     correctness,
     "Check for offset calculations on raw pointers to zero-sized types"
@@ -1412,6 +1459,7 @@ declare_clippy_lint! {
     /// # Ok::<_, std::io::Error>(())
     /// # };
     /// ```
+    #[clippy::version = "1.42.0"]
     pub FILETYPE_IS_FILE,
     restriction,
     "`FileType::is_file` is not recommended to test for readable file type"
@@ -1437,6 +1485,7 @@ declare_clippy_lint! {
     /// opt.as_deref()
     /// # ;
     /// ```
+    #[clippy::version = "1.42.0"]
     pub OPTION_AS_REF_DEREF,
     complexity,
     "using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`"
@@ -1463,6 +1512,7 @@ declare_clippy_lint! {
     /// a.get(2);
     /// b.get(0);
     /// ```
+    #[clippy::version = "1.46.0"]
     pub ITER_NEXT_SLICE,
     style,
     "using `.iter().next()` on a sliced array, which can be shortened to just `.get()`"
@@ -1488,6 +1538,7 @@ declare_clippy_lint! {
     /// string.insert(0, 'R');
     /// string.push('R');
     /// ```
+    #[clippy::version = "1.49.0"]
     pub SINGLE_CHAR_ADD_STR,
     style,
     "`push_str()` or `insert_str()` used with a single-character string literal as parameter"
@@ -1526,6 +1577,7 @@ declare_clippy_lint! {
     ///
     /// opt.unwrap_or(42);
     /// ```
+    #[clippy::version = "1.48.0"]
     pub UNNECESSARY_LAZY_EVALUATIONS,
     style,
     "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation"
@@ -1546,6 +1598,7 @@ declare_clippy_lint! {
     /// ```rust
     /// (0..3).try_for_each(|t| Err(t));
     /// ```
+    #[clippy::version = "1.49.0"]
     pub MAP_COLLECT_RESULT_UNIT,
     style,
     "using `.map(_).collect::<Result<(),_>()`, which can be replaced with `try_for_each`"
@@ -1578,6 +1631,7 @@ declare_clippy_lint! {
     ///
     /// assert_eq!(v, vec![5, 5, 5, 5, 5]);
     /// ```
+    #[clippy::version = "1.49.0"]
     pub FROM_ITER_INSTEAD_OF_COLLECT,
     pedantic,
     "use `.collect()` instead of `::from_iter()`"
@@ -1607,6 +1661,7 @@ declare_clippy_lint! {
     ///     assert!(x >= 0);
     /// });
     /// ```
+    #[clippy::version = "1.51.0"]
     pub INSPECT_FOR_EACH,
     complexity,
     "using `.inspect().for_each()`, which can be replaced with `.for_each()`"
@@ -1629,6 +1684,7 @@ declare_clippy_lint! {
     /// # let iter = vec![Some(1)].into_iter();
     /// iter.flatten();
     /// ```
+    #[clippy::version = "1.52.0"]
     pub FILTER_MAP_IDENTITY,
     complexity,
     "call to `filter_map` where `flatten` is sufficient"
@@ -1651,6 +1707,7 @@ declare_clippy_lint! {
     /// let x = [1, 2, 3];
     /// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
     /// ```
+    #[clippy::version = "1.52.0"]
     pub MAP_IDENTITY,
     complexity,
     "using iterator.map(|x| x)"
@@ -1672,6 +1729,7 @@ declare_clippy_lint! {
     /// // Good
     /// let _ = "Hello".as_bytes().get(3);
     /// ```
+    #[clippy::version = "1.52.0"]
     pub BYTES_NTH,
     style,
     "replace `.bytes().nth()` with `.as_bytes().get()`"
@@ -1697,6 +1755,7 @@ declare_clippy_lint! {
     /// let b = a.clone();
     /// let c = a.clone();
     /// ```
+    #[clippy::version = "1.52.0"]
     pub IMPLICIT_CLONE,
     pedantic,
     "implicitly cloning a value by invoking a function on its dereferenced type"
@@ -1722,6 +1781,7 @@ declare_clippy_lint! {
     /// let _ = some_vec.len();
     /// let _ = &some_vec[..].len();
     /// ```
+    #[clippy::version = "1.52.0"]
     pub ITER_COUNT,
     complexity,
     "replace `.iter().count()` with `.len()`"
@@ -1751,6 +1811,7 @@ declare_clippy_lint! {
     ///     // use x
     /// }
     /// ```
+    #[clippy::version = "1.54.0"]
     pub SUSPICIOUS_SPLITN,
     correctness,
     "checks for `.splitn(0, ..)` and `.splitn(1, ..)`"
@@ -1771,6 +1832,7 @@ declare_clippy_lint! {
     /// // Good
     /// let x: String = "x".repeat(10);
     /// ```
+    #[clippy::version = "1.54.0"]
     pub MANUAL_STR_REPEAT,
     perf,
     "manual implementation of `str::repeat`"
@@ -1793,6 +1855,7 @@ declare_clippy_lint! {
     /// let (key, value) = _.split_once('=')?;
     /// let value = _.split_once('=')?.1;
     /// ```
+    #[clippy::version = "1.57.0"]
     pub MANUAL_SPLIT_ONCE,
     complexity,
     "replace `.splitn(2, pat)` with `.split_once(pat)`"
diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs
index dc2dd45e4ed..a6450aec4f7 100644
--- a/clippy_lints/src/minmax.rs
+++ b/clippy_lints/src/minmax.rs
@@ -26,6 +26,7 @@ declare_clippy_lint! {
     /// ```
     /// It will always be equal to `0`. Probably the author meant to clamp the value
     /// between 0 and 100, but has erroneously swapped `min` and `max`.
+    #[clippy::version = "pre 1.29.0"]
     pub MIN_MAX,
     correctness,
     "`min(_, max(_, _))` (or vice versa) with bounds clamping the result to a constant"
diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs
index 0f32cd9164e..a93537104bf 100644
--- a/clippy_lints/src/misc.rs
+++ b/clippy_lints/src/misc.rs
@@ -56,6 +56,7 @@ declare_clippy_lint! {
     ///     true
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TOPLEVEL_REF_ARG,
     style,
     "an entire binding declared as `ref`, in a function argument or a `let` statement"
@@ -79,6 +80,7 @@ declare_clippy_lint! {
     /// // Good
     /// if x.is_nan() { }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CMP_NAN,
     correctness,
     "comparisons to `NAN`, which will always return false, probably not intended"
@@ -112,6 +114,7 @@ declare_clippy_lint! {
     /// if (y - 1.23f64).abs() < error_margin { }
     /// if (y - x).abs() > error_margin { }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FLOAT_CMP,
     pedantic,
     "using `==` or `!=` on float values instead of comparing difference with an epsilon"
@@ -139,6 +142,7 @@ declare_clippy_lint! {
     /// # let y = String::from("foo");
     /// if x == y {}
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CMP_OWNED,
     perf,
     "creating owned instances for comparing with others, e.g., `x == \"foo\".to_string()`"
@@ -162,6 +166,7 @@ declare_clippy_lint! {
     /// let a = x % 1;
     /// let a = x % -1;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MODULO_ONE,
     correctness,
     "taking a number modulo +/-1, which can either panic/overflow or always returns 0"
@@ -187,6 +192,7 @@ declare_clippy_lint! {
     /// let y = _x + 1; // Here we are using `_x`, even though it has a leading
     ///                 // underscore. We should rename `_x` to `x`
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub USED_UNDERSCORE_BINDING,
     pedantic,
     "using a binding which is prefixed with an underscore"
@@ -207,6 +213,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// f() && g(); // We should write `if f() { g(); }`.
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SHORT_CIRCUIT_STATEMENT,
     complexity,
     "using a short circuit boolean condition as a statement"
@@ -228,6 +235,7 @@ declare_clippy_lint! {
     /// // Good
     /// let a = std::ptr::null::<u32>();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ZERO_PTR,
     style,
     "using `0 as *{const, mut} T`"
@@ -259,6 +267,7 @@ declare_clippy_lint! {
     /// // let error_margin = std::f64::EPSILON;
     /// if (x - ONE).abs() < error_margin { }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FLOAT_CMP_CONST,
     restriction,
     "using `==` or `!=` on float constants instead of comparing difference with an epsilon"
diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs
index 7c3f5f22ade..6e09e25109f 100644
--- a/clippy_lints/src/misc_early/mod.rs
+++ b/clippy_lints/src/misc_early/mod.rs
@@ -46,6 +46,7 @@ declare_clippy_lint! {
     ///     Foo { .. } => {},
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNNEEDED_FIELD_PATTERN,
     restriction,
     "struct fields bound to a wildcard instead of using `..`"
@@ -67,6 +68,7 @@ declare_clippy_lint! {
     /// // Good
     /// fn bar(a: i32, _b: i32) {}
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DUPLICATE_UNDERSCORE_ARGUMENT,
     style,
     "function arguments having names which only differ by an underscore"
@@ -85,6 +87,7 @@ declare_clippy_lint! {
     /// let mut x = 3;
     /// --x;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DOUBLE_NEG,
     style,
     "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++"
@@ -106,6 +109,7 @@ declare_clippy_lint! {
     /// // Good
     /// let y = 0x1A9BACD;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MIXED_CASE_HEX_LITERALS,
     style,
     "hex literals whose letter digits are not consistently upper- or lowercased"
@@ -129,6 +133,7 @@ declare_clippy_lint! {
     /// // Good
     /// let y = 123832_i32;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNSEPARATED_LITERAL_SUFFIX,
     restriction,
     "literals whose suffix is not separated by an underscore"
@@ -151,6 +156,7 @@ declare_clippy_lint! {
     /// // Good
     /// let y = 123832i32;
     /// ```
+    #[clippy::version = "1.58.0"]
     pub SEPARATED_LITERAL_SUFFIX,
     restriction,
     "literals whose suffix is separated by an underscore"
@@ -189,6 +195,7 @@ declare_clippy_lint! {
     /// ```
     ///
     /// prints `83` (as `83 == 0o123` while `123 == 0o173`).
+    #[clippy::version = "pre 1.29.0"]
     pub ZERO_PREFIXED_LITERAL,
     complexity,
     "integer literals starting with `0`"
@@ -210,6 +217,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub BUILTIN_TYPE_SHADOW,
     style,
     "shadowing a builtin type"
@@ -239,6 +247,7 @@ declare_clippy_lint! {
     ///     y => (),
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub REDUNDANT_PATTERN,
     style,
     "using `name @ _` in a pattern"
@@ -273,6 +282,7 @@ declare_clippy_lint! {
     ///     _ => (),
     /// }
     /// ```
+    #[clippy::version = "1.40.0"]
     pub UNNEEDED_WILDCARD_PATTERN,
     complexity,
     "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)"
diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs
index 5b2584d43a1..a8d41050856 100644
--- a/clippy_lints/src/missing_const_for_fn.rs
+++ b/clippy_lints/src/missing_const_for_fn.rs
@@ -63,6 +63,7 @@ declare_clippy_lint! {
     /// }
     /// # }
     /// ```
+    #[clippy::version = "1.34.0"]
     pub MISSING_CONST_FOR_FN,
     nursery,
     "Lint functions definitions that could be made `const fn`"
diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs
index 6920b4f72ea..db6aab0671b 100644
--- a/clippy_lints/src/missing_doc.rs
+++ b/clippy_lints/src/missing_doc.rs
@@ -27,6 +27,7 @@ declare_clippy_lint! {
     /// allowed-by-default lint for
     /// public members, but has no way to enforce documentation of private items.
     /// This lint fixes that.
+    #[clippy::version = "pre 1.29.0"]
     pub MISSING_DOCS_IN_PRIVATE_ITEMS,
     restriction,
     "detects missing documentation for public and private members"
diff --git a/clippy_lints/src/missing_enforced_import_rename.rs b/clippy_lints/src/missing_enforced_import_rename.rs
index 448bfc2fdd6..68d49e0f150 100644
--- a/clippy_lints/src/missing_enforced_import_rename.rs
+++ b/clippy_lints/src/missing_enforced_import_rename.rs
@@ -33,6 +33,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// use serde_json::Value as JsonValue;
     /// ```
+    #[clippy::version = "1.55.0"]
     pub MISSING_ENFORCED_IMPORT_RENAMES,
     restriction,
     "enforce import renames"
diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs
index b593c747498..ac2f16b49e3 100644
--- a/clippy_lints/src/missing_inline.rs
+++ b/clippy_lints/src/missing_inline.rs
@@ -52,6 +52,7 @@ declare_clippy_lint! {
     ///    fn def_bar() {} // missing #[inline]
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MISSING_INLINE_IN_PUBLIC_ITEMS,
     restriction,
     "detects missing `#[inline]` attribute for public callables (functions, trait methods, methods...)"
diff --git a/clippy_lints/src/module_style.rs b/clippy_lints/src/module_style.rs
index d41b5474564..3b65f80cba2 100644
--- a/clippy_lints/src/module_style.rs
+++ b/clippy_lints/src/module_style.rs
@@ -32,6 +32,7 @@ declare_clippy_lint! {
     ///   stuff.rs
     ///   lib.rs
     /// ```
+    #[clippy::version = "1.57.0"]
     pub MOD_MODULE_FILES,
     restriction,
     "checks that module layout is consistent"
@@ -61,6 +62,7 @@ declare_clippy_lint! {
     ///   lib.rs
     /// ```
 
+    #[clippy::version = "1.57.0"]
     pub SELF_NAMED_MODULE_FILES,
     restriction,
     "checks that module layout is consistent"
diff --git a/clippy_lints/src/modulo_arithmetic.rs b/clippy_lints/src/modulo_arithmetic.rs
index f45e68233a1..d182a7d5249 100644
--- a/clippy_lints/src/modulo_arithmetic.rs
+++ b/clippy_lints/src/modulo_arithmetic.rs
@@ -24,6 +24,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let x = -17 % 3;
     /// ```
+    #[clippy::version = "1.42.0"]
     pub MODULO_ARITHMETIC,
     restriction,
     "any modulo arithmetic statement"
diff --git a/clippy_lints/src/multiple_crate_versions.rs b/clippy_lints/src/multiple_crate_versions.rs
index 816b2f275fb..e45cc86d417 100644
--- a/clippy_lints/src/multiple_crate_versions.rs
+++ b/clippy_lints/src/multiple_crate_versions.rs
@@ -33,6 +33,7 @@ declare_clippy_lint! {
     /// ctrlc = "=3.1.0"
     /// ansi_term = "=0.11.0"
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MULTIPLE_CRATE_VERSIONS,
     cargo,
     "multiple versions of the same crate being used"
diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs
index 8476257f086..5fe887a4573 100644
--- a/clippy_lints/src/mut_key.rs
+++ b/clippy_lints/src/mut_key.rs
@@ -72,6 +72,7 @@ declare_clippy_lint! {
     ///     let _: HashSet<Bad> = HashSet::new();
     /// }
     /// ```
+    #[clippy::version = "1.42.0"]
     pub MUTABLE_KEY_TYPE,
     suspicious,
     "Check for mutable `Map`/`Set` key type"
diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs
index 7c4cac29ba8..bcbea8f1e66 100644
--- a/clippy_lints/src/mut_mut.rs
+++ b/clippy_lints/src/mut_mut.rs
@@ -22,6 +22,7 @@ declare_clippy_lint! {
     /// # let mut y = 1;
     /// let x = &mut &mut y;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MUT_MUT,
     pedantic,
     "usage of double-mut refs, e.g., `&mut &mut ...`"
diff --git a/clippy_lints/src/mut_mutex_lock.rs b/clippy_lints/src/mut_mutex_lock.rs
index b96fa4774cb..b1e6308d2e1 100644
--- a/clippy_lints/src/mut_mutex_lock.rs
+++ b/clippy_lints/src/mut_mutex_lock.rs
@@ -38,6 +38,7 @@ declare_clippy_lint! {
     /// let value = value_mutex.get_mut().unwrap();
     /// *value += 1;
     /// ```
+    #[clippy::version = "1.49.0"]
     pub MUT_MUTEX_LOCK,
     style,
     "`&mut Mutex::lock` does unnecessary locking"
diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs
index 8d5d7951fc5..63a1cf7b7d5 100644
--- a/clippy_lints/src/mut_reference.rs
+++ b/clippy_lints/src/mut_reference.rs
@@ -23,6 +23,7 @@ declare_clippy_lint! {
     /// // Good
     /// my_vec.push(&value)
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNNECESSARY_MUT_PASSED,
     style,
     "an argument passed as a mutable reference although the callee only demands an immutable reference"
diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs
index ee50891cc31..fa7274990db 100644
--- a/clippy_lints/src/mutable_debug_assertion.rs
+++ b/clippy_lints/src/mutable_debug_assertion.rs
@@ -26,6 +26,7 @@ declare_clippy_lint! {
     /// fn take_a_mut_parameter(_: &mut u32) -> bool { unimplemented!() }
     /// debug_assert!(take_a_mut_parameter(&mut 5));
     /// ```
+    #[clippy::version = "1.40.0"]
     pub DEBUG_ASSERT_WITH_MUT_CALL,
     nursery,
     "mutable arguments in `debug_assert{,_ne,_eq}!`"
diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs
index 5feddcbfc61..816377fe65e 100644
--- a/clippy_lints/src/mutex_atomic.rs
+++ b/clippy_lints/src/mutex_atomic.rs
@@ -36,6 +36,7 @@ declare_clippy_lint! {
     /// # use std::sync::atomic::AtomicBool;
     /// let x = AtomicBool::new(y);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MUTEX_ATOMIC,
     perf,
     "using a mutex where an atomic value could be used instead"
@@ -64,6 +65,7 @@ declare_clippy_lint! {
     /// # use std::sync::atomic::AtomicUsize;
     /// let x = AtomicUsize::new(0usize);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MUTEX_INTEGER,
     nursery,
     "using a mutex for an integer type"
diff --git a/clippy_lints/src/needless_arbitrary_self_type.rs b/clippy_lints/src/needless_arbitrary_self_type.rs
index c8a8750c2ff..9838d3cad9f 100644
--- a/clippy_lints/src/needless_arbitrary_self_type.rs
+++ b/clippy_lints/src/needless_arbitrary_self_type.rs
@@ -52,6 +52,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.47.0"]
     pub NEEDLESS_ARBITRARY_SELF_TYPE,
     complexity,
     "type of `self` parameter is already by default `Self`"
diff --git a/clippy_lints/src/needless_bitwise_bool.rs b/clippy_lints/src/needless_bitwise_bool.rs
index ad981599473..a8a8d174a82 100644
--- a/clippy_lints/src/needless_bitwise_bool.rs
+++ b/clippy_lints/src/needless_bitwise_bool.rs
@@ -30,6 +30,7 @@ declare_clippy_lint! {
     /// let (x,y) = (true, false);
     /// if x && !y {}
     /// ```
+    #[clippy::version = "1.54.0"]
     pub NEEDLESS_BITWISE_BOOL,
     pedantic,
     "Boolean expressions that use bitwise rather than lazy operators"
diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs
index 91944653500..3709f3948c2 100644
--- a/clippy_lints/src/needless_bool.rs
+++ b/clippy_lints/src/needless_bool.rs
@@ -41,6 +41,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// !x
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEEDLESS_BOOL,
     complexity,
     "if-statements with plain booleans in the then- and else-clause, e.g., `if p { true } else { false }`"
@@ -65,6 +66,7 @@ declare_clippy_lint! {
     /// if x {}
     /// if !y {}
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub BOOL_COMPARISON,
     complexity,
     "comparing a variable to a boolean, e.g., `if x == true` or `if x != true`"
diff --git a/clippy_lints/src/needless_borrow.rs b/clippy_lints/src/needless_borrow.rs
index 085be6650cc..6709fed91bd 100644
--- a/clippy_lints/src/needless_borrow.rs
+++ b/clippy_lints/src/needless_borrow.rs
@@ -37,6 +37,7 @@ declare_clippy_lint! {
     /// let x: &i32 = &5;
     /// fun(x);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEEDLESS_BORROW,
     style,
     "taking a reference that is going to be automatically dereferenced"
@@ -63,6 +64,7 @@ declare_clippy_lint! {
     ///     // use `&x` here
     /// }
     /// ```
+    #[clippy::version = "1.54.0"]
     pub REF_BINDING_TO_REFERENCE,
     pedantic,
     "`ref` binding to a reference"
diff --git a/clippy_lints/src/needless_borrowed_ref.rs b/clippy_lints/src/needless_borrowed_ref.rs
index 36879eda7c0..0fcc419e722 100644
--- a/clippy_lints/src/needless_borrowed_ref.rs
+++ b/clippy_lints/src/needless_borrowed_ref.rs
@@ -38,6 +38,7 @@ declare_clippy_lint! {
     /// let mut v = Vec::<String>::new();
     /// let _ = v.iter_mut().filter(|a| a.is_empty());
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEEDLESS_BORROWED_REFERENCE,
     complexity,
     "destructuring a reference and borrowing the inner value"
diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs
index 7aa93ed7839..98a3bce1ff3 100644
--- a/clippy_lints/src/needless_continue.rs
+++ b/clippy_lints/src/needless_continue.rs
@@ -110,6 +110,7 @@ declare_clippy_lint! {
     ///     # break;
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEEDLESS_CONTINUE,
     pedantic,
     "`continue` statements that can be replaced by a rearrangement of code"
diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs
index 9a6ddc72ce5..0c1da035173 100644
--- a/clippy_lints/src/needless_for_each.rs
+++ b/clippy_lints/src/needless_for_each.rs
@@ -40,6 +40,7 @@ declare_clippy_lint! {
     ///     println!("{}", elem);
     /// }
     /// ```
+    #[clippy::version = "1.53.0"]
     pub NEEDLESS_FOR_EACH,
     pedantic,
     "using `for_each` where a `for` loop would be simpler"
diff --git a/clippy_lints/src/needless_option_as_deref.rs b/clippy_lints/src/needless_option_as_deref.rs
index e88e98e6081..a28b08c33ec 100644
--- a/clippy_lints/src/needless_option_as_deref.rs
+++ b/clippy_lints/src/needless_option_as_deref.rs
@@ -26,6 +26,7 @@ declare_clippy_lint! {
     /// let a = Some(&1);
     /// let b = a;
     /// ```
+    #[clippy::version = "1.57.0"]
     pub NEEDLESS_OPTION_AS_DEREF,
     complexity,
     "no-op use of `deref` or `deref_mut` method to `Option`."
diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs
index 352dc6f8bec..35877d51c0c 100644
--- a/clippy_lints/src/needless_pass_by_value.rs
+++ b/clippy_lints/src/needless_pass_by_value.rs
@@ -51,6 +51,7 @@ declare_clippy_lint! {
     ///     assert_eq!(v.len(), 42);
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEEDLESS_PASS_BY_VALUE,
     pedantic,
     "functions taking arguments by value, but not consuming them in its body"
diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs
index 42e48336e15..3ca933ea8fb 100644
--- a/clippy_lints/src/needless_question_mark.rs
+++ b/clippy_lints/src/needless_question_mark.rs
@@ -53,6 +53,7 @@ declare_clippy_lint! {
     ///     tr.and_then(|t| t.magic)
     /// }
     /// ```
+    #[clippy::version = "1.51.0"]
     pub NEEDLESS_QUESTION_MARK,
     complexity,
     "Suggest `value.inner_option` instead of `Some(value.inner_option?)`. The same goes for `Result<T, E>`."
diff --git a/clippy_lints/src/needless_update.rs b/clippy_lints/src/needless_update.rs
index 2a33b7392ca..ed315efaa2f 100644
--- a/clippy_lints/src/needless_update.rs
+++ b/clippy_lints/src/needless_update.rs
@@ -40,6 +40,7 @@ declare_clippy_lint! {
     ///     ..zero_point
     /// };
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEEDLESS_UPDATE,
     complexity,
     "using `Foo { ..base }` when there are no missing fields"
diff --git a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs
index 6ad49b70605..efe31a15441 100644
--- a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs
+++ b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs
@@ -36,6 +36,7 @@ declare_clippy_lint! {
     ///     _ => false,
     /// };
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEG_CMP_OP_ON_PARTIAL_ORD,
     complexity,
     "The use of negated comparison operators on partially ordered types may produce confusing code."
diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs
index 1b15d29439f..cb67fab1740 100644
--- a/clippy_lints/src/neg_multiply.rs
+++ b/clippy_lints/src/neg_multiply.rs
@@ -20,6 +20,7 @@ declare_clippy_lint! {
     /// ```ignore
     /// x * -1
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEG_MULTIPLY,
     style,
     "multiplying integers with `-1`"
diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs
index 0ac27f1cba2..f0c0c89ca8f 100644
--- a/clippy_lints/src/new_without_default.rs
+++ b/clippy_lints/src/new_without_default.rs
@@ -45,6 +45,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEW_WITHOUT_DEFAULT,
     style,
     "`fn new() -> Self` method without `Default` implementation"
diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs
index 6dae8f32043..31134743d02 100644
--- a/clippy_lints/src/no_effect.rs
+++ b/clippy_lints/src/no_effect.rs
@@ -22,6 +22,7 @@ declare_clippy_lint! {
     /// ```rust
     /// 0;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NO_EFFECT,
     complexity,
     "statements with no effect"
@@ -44,6 +45,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// let _i_serve_no_purpose = 1;
     /// ```
+    #[clippy::version = "1.58.0"]
     pub NO_EFFECT_UNDERSCORE_BINDING,
     pedantic,
     "binding to `_` prefixed variable with no side-effect"
@@ -62,6 +64,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// compute_array()[0];
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNNECESSARY_OPERATION,
     complexity,
     "outer expressions with no effect"
diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs
index 2a85a67fa09..df82775d507 100644
--- a/clippy_lints/src/non_copy_const.rs
+++ b/clippy_lints/src/non_copy_const.rs
@@ -69,6 +69,7 @@ declare_clippy_lint! {
     /// STATIC_ATOM.store(9, SeqCst);
     /// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DECLARE_INTERIOR_MUTABLE_CONST,
     style,
     "declaring `const` with interior mutability"
@@ -113,6 +114,7 @@ declare_clippy_lint! {
     /// STATIC_ATOM.store(9, SeqCst);
     /// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub BORROW_INTERIOR_MUTABLE_CONST,
     style,
     "referencing `const` with interior mutability"
diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs
index 5b254bc8133..1a124e2d3cb 100644
--- a/clippy_lints/src/non_expressive_names.rs
+++ b/clippy_lints/src/non_expressive_names.rs
@@ -24,6 +24,7 @@ declare_clippy_lint! {
     /// let checked_exp = something;
     /// let checked_expr = something_else;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SIMILAR_NAMES,
     pedantic,
     "similarly named items and bindings"
@@ -42,6 +43,7 @@ declare_clippy_lint! {
     /// ```ignore
     /// let (a, b, c, d, e, f, g) = (...);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MANY_SINGLE_CHAR_NAMES,
     pedantic,
     "too many single character bindings"
@@ -62,6 +64,7 @@ declare_clippy_lint! {
     /// let ___1 = 1;
     /// let __1___2 = 11;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub JUST_UNDERSCORES_AND_DIGITS,
     style,
     "unclear name"
diff --git a/clippy_lints/src/non_octal_unix_permissions.rs b/clippy_lints/src/non_octal_unix_permissions.rs
index 3b74f69d375..4b57dbc4c41 100644
--- a/clippy_lints/src/non_octal_unix_permissions.rs
+++ b/clippy_lints/src/non_octal_unix_permissions.rs
@@ -32,6 +32,7 @@ declare_clippy_lint! {
     /// let mut options = OpenOptions::new();
     /// options.mode(0o644);
     /// ```
+    #[clippy::version = "1.53.0"]
     pub NON_OCTAL_UNIX_PERMISSIONS,
     correctness,
     "use of non-octal value to set unix file permissions, which will be translated into octal"
diff --git a/clippy_lints/src/non_send_fields_in_send_ty.rs b/clippy_lints/src/non_send_fields_in_send_ty.rs
index 7ebf84d400f..9c07488bfe6 100644
--- a/clippy_lints/src/non_send_fields_in_send_ty.rs
+++ b/clippy_lints/src/non_send_fields_in_send_ty.rs
@@ -43,6 +43,7 @@ declare_clippy_lint! {
     /// ```
     /// Use thread-safe types like [`std::sync::Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html)
     /// or specify correct bounds on generic type parameters (`T: Send`).
+    #[clippy::version = "1.57.0"]
     pub NON_SEND_FIELDS_IN_SEND_TY,
     suspicious,
     "there is field that does not implement `Send` in a `Send` struct"
diff --git a/clippy_lints/src/nonstandard_macro_braces.rs b/clippy_lints/src/nonstandard_macro_braces.rs
index bab15217d52..3dcc9e26c9e 100644
--- a/clippy_lints/src/nonstandard_macro_braces.rs
+++ b/clippy_lints/src/nonstandard_macro_braces.rs
@@ -31,6 +31,7 @@ declare_clippy_lint! {
     /// ```rust
     /// vec![1, 2, 3];
     /// ```
+    #[clippy::version = "1.55.0"]
     pub NONSTANDARD_MACRO_BRACES,
     nursery,
     "check consistent use of braces in macro"
diff --git a/clippy_lints/src/open_options.rs b/clippy_lints/src/open_options.rs
index 5752342cf62..2c77100bdcf 100644
--- a/clippy_lints/src/open_options.rs
+++ b/clippy_lints/src/open_options.rs
@@ -22,6 +22,7 @@ declare_clippy_lint! {
     ///
     /// OpenOptions::new().read(true).truncate(true);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NONSENSICAL_OPEN_OPTIONS,
     correctness,
     "nonsensical combination of options for opening a file"
diff --git a/clippy_lints/src/option_env_unwrap.rs b/clippy_lints/src/option_env_unwrap.rs
index d7306628030..3f5286ba097 100644
--- a/clippy_lints/src/option_env_unwrap.rs
+++ b/clippy_lints/src/option_env_unwrap.rs
@@ -26,6 +26,7 @@ declare_clippy_lint! {
     /// ```rust,no_run
     /// let _ = env!("HOME");
     /// ```
+    #[clippy::version = "1.43.0"]
     pub OPTION_ENV_UNWRAP,
     correctness,
     "using `option_env!(...).unwrap()` to get environment variable"
diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs
index ed5583799fe..df72f4b0eb2 100644
--- a/clippy_lints/src/option_if_let_else.rs
+++ b/clippy_lints/src/option_if_let_else.rs
@@ -59,6 +59,7 @@ declare_clippy_lint! {
     ///     y*y
     /// }, |foo| foo);
     /// ```
+    #[clippy::version = "1.47.0"]
     pub OPTION_IF_LET_ELSE,
     nursery,
     "reimplementation of Option::map_or"
diff --git a/clippy_lints/src/overflow_check_conditional.rs b/clippy_lints/src/overflow_check_conditional.rs
index 0f9e5ada3a8..6dabbd48031 100644
--- a/clippy_lints/src/overflow_check_conditional.rs
+++ b/clippy_lints/src/overflow_check_conditional.rs
@@ -19,6 +19,7 @@ declare_clippy_lint! {
     /// # let b = 2;
     /// a + b < a;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub OVERFLOW_CHECK_CONDITIONAL,
     complexity,
     "overflow checks inspired by C which are likely to panic"
diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs
index 583c42b6563..8769c045214 100644
--- a/clippy_lints/src/panic_in_result_fn.rs
+++ b/clippy_lints/src/panic_in_result_fn.rs
@@ -30,6 +30,7 @@ declare_clippy_lint! {
     ///     Err(String::from("error"))
     /// }
     /// ```
+    #[clippy::version = "1.48.0"]
     pub PANIC_IN_RESULT_FN,
     restriction,
     "functions of type `Result<..>` that contain `panic!()`, `todo!()`, `unreachable()`, `unimplemented()` or assertion"
diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs
index d8d9081d6f1..edfac824ded 100644
--- a/clippy_lints/src/panic_unimplemented.rs
+++ b/clippy_lints/src/panic_unimplemented.rs
@@ -17,6 +17,7 @@ declare_clippy_lint! {
     /// ```no_run
     /// panic!("even with a good reason");
     /// ```
+    #[clippy::version = "1.40.0"]
     pub PANIC,
     restriction,
     "usage of the `panic!` macro"
@@ -33,6 +34,7 @@ declare_clippy_lint! {
     /// ```no_run
     /// unimplemented!();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNIMPLEMENTED,
     restriction,
     "`unimplemented!` should not be present in production code"
@@ -49,6 +51,7 @@ declare_clippy_lint! {
     /// ```no_run
     /// todo!();
     /// ```
+    #[clippy::version = "1.40.0"]
     pub TODO,
     restriction,
     "`todo!` should not be present in production code"
@@ -65,6 +68,7 @@ declare_clippy_lint! {
     /// ```no_run
     /// unreachable!();
     /// ```
+    #[clippy::version = "1.40.0"]
     pub UNREACHABLE,
     restriction,
     "usage of the `unreachable!` macro"
diff --git a/clippy_lints/src/partialeq_ne_impl.rs b/clippy_lints/src/partialeq_ne_impl.rs
index 4ec493e5f45..e827cdaae87 100644
--- a/clippy_lints/src/partialeq_ne_impl.rs
+++ b/clippy_lints/src/partialeq_ne_impl.rs
@@ -25,6 +25,7 @@ declare_clippy_lint! {
     ///    fn ne(&self, other: &Foo) -> bool { !(self == other) }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub PARTIALEQ_NE_IMPL,
     complexity,
     "re-implementing `PartialEq::ne`"
diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs
index 6229b9608b3..3092ab8392a 100644
--- a/clippy_lints/src/pass_by_ref_or_value.rs
+++ b/clippy_lints/src/pass_by_ref_or_value.rs
@@ -65,6 +65,7 @@ declare_clippy_lint! {
     /// // Better
     /// fn foo(v: u32) {}
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TRIVIALLY_COPY_PASS_BY_REF,
     pedantic,
     "functions taking small copyable arguments by reference"
@@ -98,6 +99,7 @@ declare_clippy_lint! {
     /// // Good
     /// fn foo(v: &TooLarge) {}
     /// ```
+    #[clippy::version = "1.49.0"]
     pub LARGE_TYPES_PASSED_BY_VALUE,
     pedantic,
     "functions taking large arguments by value"
diff --git a/clippy_lints/src/path_buf_push_overwrite.rs b/clippy_lints/src/path_buf_push_overwrite.rs
index 3df7a72d295..8ebee9bd04d 100644
--- a/clippy_lints/src/path_buf_push_overwrite.rs
+++ b/clippy_lints/src/path_buf_push_overwrite.rs
@@ -35,6 +35,7 @@ declare_clippy_lint! {
     /// x.push("bar");
     /// assert_eq!(x, PathBuf::from("/foo/bar"));
     /// ```
+    #[clippy::version = "1.36.0"]
     pub PATH_BUF_PUSH_OVERWRITE,
     nursery,
     "calling `push` with file system root on `PathBuf` can overwrite it"
diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs
index e7bc2446590..3e7eef4edac 100644
--- a/clippy_lints/src/pattern_type_mismatch.rs
+++ b/clippy_lints/src/pattern_type_mismatch.rs
@@ -77,6 +77,7 @@ declare_clippy_lint! {
     ///     *a += b;
     /// }
     /// ```
+    #[clippy::version = "1.47.0"]
     pub PATTERN_TYPE_MISMATCH,
     restriction,
     "type of pattern does not match the expression type"
diff --git a/clippy_lints/src/precedence.rs b/clippy_lints/src/precedence.rs
index 1a8da00d9d6..cc0533c9f5d 100644
--- a/clippy_lints/src/precedence.rs
+++ b/clippy_lints/src/precedence.rs
@@ -42,6 +42,7 @@ declare_clippy_lint! {
     /// ### Example
     /// * `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7
     /// * `-1i32.abs()` equals -1, while `(-1i32).abs()` equals 1
+    #[clippy::version = "pre 1.29.0"]
     pub PRECEDENCE,
     complexity,
     "operations where precedence may be unclear"
diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs
index 8a36e20fc97..c08a19d520b 100644
--- a/clippy_lints/src/ptr.rs
+++ b/clippy_lints/src/ptr.rs
@@ -70,6 +70,7 @@ declare_clippy_lint! {
     /// // Good
     /// fn foo(&[u32]) { .. }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub PTR_ARG,
     style,
     "fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively"
@@ -96,6 +97,7 @@ declare_clippy_lint! {
     ///     ..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CMP_NULL,
     style,
     "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead"
@@ -121,6 +123,7 @@ declare_clippy_lint! {
     /// ```ignore
     /// fn foo(&Foo) -> &mut Bar { .. }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MUT_FROM_REF,
     correctness,
     "fns that create mutable refs from immutable ref args"
@@ -143,6 +146,7 @@ declare_clippy_lint! {
     /// // Good
     /// unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); }
     /// ```
+    #[clippy::version = "1.53.0"]
     pub INVALID_NULL_PTR_USAGE,
     correctness,
     "invalid usage of a null pointer, suggesting `NonNull::dangling()` instead"
diff --git a/clippy_lints/src/ptr_eq.rs b/clippy_lints/src/ptr_eq.rs
index 2df34d6d9b9..3c126fc1ca6 100644
--- a/clippy_lints/src/ptr_eq.rs
+++ b/clippy_lints/src/ptr_eq.rs
@@ -29,6 +29,7 @@ declare_clippy_lint! {
     ///
     /// assert!(std::ptr::eq(a, b));
     /// ```
+    #[clippy::version = "1.49.0"]
     pub PTR_EQ,
     style,
     "use `std::ptr::eq` when comparing raw pointers"
diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs
index cfb5287c667..964564b5794 100644
--- a/clippy_lints/src/ptr_offset_with_cast.rs
+++ b/clippy_lints/src/ptr_offset_with_cast.rs
@@ -38,6 +38,7 @@ declare_clippy_lint! {
     ///     ptr.add(offset);
     /// }
     /// ```
+    #[clippy::version = "1.30.0"]
     pub PTR_OFFSET_WITH_CAST,
     complexity,
     "unneeded pointer offset cast"
diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs
index f63ef163bcb..a5531993ee6 100644
--- a/clippy_lints/src/question_mark.rs
+++ b/clippy_lints/src/question_mark.rs
@@ -32,6 +32,7 @@ declare_clippy_lint! {
     /// ```ignore
     /// option?;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub QUESTION_MARK,
     style,
     "checks for expressions that could be replaced by the question mark operator"
diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs
index 87364a88ed0..09c64485bbb 100644
--- a/clippy_lints/src/ranges.rs
+++ b/clippy_lints/src/ranges.rs
@@ -35,6 +35,7 @@ declare_clippy_lint! {
     /// # let x = vec![1];
     /// x.iter().enumerate();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub RANGE_ZIP_WITH_LEN,
     complexity,
     "zipping iterator with a range when `enumerate()` would do"
@@ -72,6 +73,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// for x..=y { .. }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub RANGE_PLUS_ONE,
     pedantic,
     "`x..(y+1)` reads better as `x..=y`"
@@ -100,6 +102,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// for x..y { .. }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub RANGE_MINUS_ONE,
     pedantic,
     "`x..=(y-1)` reads better as `x..y`"
@@ -132,6 +135,7 @@ declare_clippy_lint! {
     ///     let sub = &arr[1..3];
     /// }
     /// ```
+    #[clippy::version = "1.45.0"]
     pub REVERSED_EMPTY_RANGES,
     correctness,
     "reversing the limits of range expressions, resulting in empty ranges"
@@ -158,6 +162,7 @@ declare_clippy_lint! {
     ///# let x = 6;
     /// assert!((3..8).contains(&x));
     /// ```
+    #[clippy::version = "1.49.0"]
     pub MANUAL_RANGE_CONTAINS,
     style,
     "manually reimplementing {`Range`, `RangeInclusive`}`::contains`"
diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs
index f7711b6fe94..1a2c86a7686 100644
--- a/clippy_lints/src/redundant_clone.rs
+++ b/clippy_lints/src/redundant_clone.rs
@@ -62,6 +62,7 @@ declare_clippy_lint! {
     ///
     /// Path::new("/a/b").join("c").to_path_buf();
     /// ```
+    #[clippy::version = "1.32.0"]
     pub REDUNDANT_CLONE,
     perf,
     "`clone()` of an owned value that is going to be dropped immediately"
diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs
index 90e3c3f4b3e..0de282542fc 100644
--- a/clippy_lints/src/redundant_closure_call.rs
+++ b/clippy_lints/src/redundant_closure_call.rs
@@ -30,6 +30,7 @@ declare_clippy_lint! {
     /// // Good
     /// let a = 42
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub REDUNDANT_CLOSURE_CALL,
     complexity,
     "throwaway closures called in the expression they are defined"
diff --git a/clippy_lints/src/redundant_else.rs b/clippy_lints/src/redundant_else.rs
index 68b256d2944..93dbe936d58 100644
--- a/clippy_lints/src/redundant_else.rs
+++ b/clippy_lints/src/redundant_else.rs
@@ -36,6 +36,7 @@ declare_clippy_lint! {
     ///     print!("Moving on...");
     /// }
     /// ```
+    #[clippy::version = "1.50.0"]
     pub REDUNDANT_ELSE,
     pedantic,
     "`else` branch that can be removed without changing semantics"
diff --git a/clippy_lints/src/redundant_field_names.rs b/clippy_lints/src/redundant_field_names.rs
index 47df4917510..0dea4a784b2 100644
--- a/clippy_lints/src/redundant_field_names.rs
+++ b/clippy_lints/src/redundant_field_names.rs
@@ -30,6 +30,7 @@ declare_clippy_lint! {
     /// ```ignore
     /// let foo = Foo { bar };
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub REDUNDANT_FIELD_NAMES,
     style,
     "checks for fields in struct literals where shorthands could be used"
diff --git a/clippy_lints/src/redundant_pub_crate.rs b/clippy_lints/src/redundant_pub_crate.rs
index 919d4e11e5a..2cee3c14d7f 100644
--- a/clippy_lints/src/redundant_pub_crate.rs
+++ b/clippy_lints/src/redundant_pub_crate.rs
@@ -26,6 +26,7 @@ declare_clippy_lint! {
     ///     pub fn internal_fn() { }
     /// }
     /// ```
+    #[clippy::version = "1.44.0"]
     pub REDUNDANT_PUB_CRATE,
     nursery,
     "Using `pub(crate)` visibility on items that are not crate visible due to the visibility of the module that contains them."
diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs
index 0c460150087..b2bd0103d11 100644
--- a/clippy_lints/src/redundant_slicing.rs
+++ b/clippy_lints/src/redundant_slicing.rs
@@ -34,6 +34,7 @@ declare_clippy_lint! {
     ///     x
     /// }
     /// ```
+    #[clippy::version = "1.51.0"]
     pub REDUNDANT_SLICING,
     complexity,
     "redundant slicing of the whole range of a type"
diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs
index d5a1a61da6b..ea5064217ab 100644
--- a/clippy_lints/src/redundant_static_lifetimes.rs
+++ b/clippy_lints/src/redundant_static_lifetimes.rs
@@ -27,6 +27,7 @@ declare_clippy_lint! {
     ///  const FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]
     ///  static FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]
     /// ```
+    #[clippy::version = "1.37.0"]
     pub REDUNDANT_STATIC_LIFETIMES,
     style,
     "Using explicit `'static` lifetime for constants or statics when elision rules would allow omitting them."
diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs
index d543832e314..909d6971a54 100644
--- a/clippy_lints/src/ref_option_ref.rs
+++ b/clippy_lints/src/ref_option_ref.rs
@@ -28,6 +28,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// let x: Option<&u32> = Some(&0u32);
     /// ```
+    #[clippy::version = "1.49.0"]
     pub REF_OPTION_REF,
     pedantic,
     "use `Option<&T>` instead of `&Option<&T>`"
diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs
index 70dff5ad313..22ae7a291d0 100644
--- a/clippy_lints/src/reference.rs
+++ b/clippy_lints/src/reference.rs
@@ -30,6 +30,7 @@ declare_clippy_lint! {
     /// let a = f(b);
     /// let c = d;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DEREF_ADDROF,
     complexity,
     "use of `*&` or `*&mut` in an expression"
@@ -124,6 +125,7 @@ declare_clippy_lint! {
     /// # let point = Point(30, 20);
     /// let x = point.0;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub REF_IN_DEREF,
     complexity,
     "Use of reference in auto dereference expression."
diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs
index 5d08aee1e5f..8e5983b4773 100644
--- a/clippy_lints/src/regex.rs
+++ b/clippy_lints/src/regex.rs
@@ -22,6 +22,7 @@ declare_clippy_lint! {
     /// ```ignore
     /// Regex::new("|")
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INVALID_REGEX,
     correctness,
     "invalid regular expressions"
@@ -46,6 +47,7 @@ declare_clippy_lint! {
     /// ```ignore
     /// Regex::new("^foobar")
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TRIVIAL_REGEX,
     nursery,
     "trivial regular expressions"
diff --git a/clippy_lints/src/repeat_once.rs b/clippy_lints/src/repeat_once.rs
index e5e55ee7505..b5dd2de6337 100644
--- a/clippy_lints/src/repeat_once.rs
+++ b/clippy_lints/src/repeat_once.rs
@@ -35,6 +35,7 @@ declare_clippy_lint! {
     ///     let x = String::from("hello world").clone();
     /// }
     /// ```
+    #[clippy::version = "1.47.0"]
     pub REPEAT_ONCE,
     complexity,
     "using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` "
diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs
index e2b1a33746e..494bc7dda18 100644
--- a/clippy_lints/src/returns.rs
+++ b/clippy_lints/src/returns.rs
@@ -37,6 +37,7 @@ declare_clippy_lint! {
     ///     String::new()
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub LET_AND_RETURN,
     style,
     "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block"
@@ -62,6 +63,7 @@ declare_clippy_lint! {
     ///     x
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEEDLESS_RETURN,
     style,
     "using a return statement like `return expr;` where an expression would suffice"
diff --git a/clippy_lints/src/same_name_method.rs b/clippy_lints/src/same_name_method.rs
index 737ff634e44..3f38d12fb55 100644
--- a/clippy_lints/src/same_name_method.rs
+++ b/clippy_lints/src/same_name_method.rs
@@ -33,6 +33,7 @@ declare_clippy_lint! {
     ///     fn foo(&self) {}
     /// }
     /// ```
+    #[clippy::version = "1.57.0"]
     pub SAME_NAME_METHOD,
     restriction,
     "two method with same name"
diff --git a/clippy_lints/src/self_assignment.rs b/clippy_lints/src/self_assignment.rs
index fbd65fef7d1..b14f0518bdb 100644
--- a/clippy_lints/src/self_assignment.rs
+++ b/clippy_lints/src/self_assignment.rs
@@ -30,6 +30,7 @@ declare_clippy_lint! {
     ///     a.y = a.y;
     /// }
     /// ```
+    #[clippy::version = "1.48.0"]
     pub SELF_ASSIGNMENT,
     correctness,
     "explicit self-assignment"
diff --git a/clippy_lints/src/self_named_constructors.rs b/clippy_lints/src/self_named_constructors.rs
index 4ba5e1a0f53..dd73dbfe09e 100644
--- a/clippy_lints/src/self_named_constructors.rs
+++ b/clippy_lints/src/self_named_constructors.rs
@@ -32,6 +32,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.55.0"]
     pub SELF_NAMED_CONSTRUCTORS,
     style,
     "method should not have the same name as the type it is implemented for"
diff --git a/clippy_lints/src/semicolon_if_nothing_returned.rs b/clippy_lints/src/semicolon_if_nothing_returned.rs
index d3b4929a189..0b3bbbc8155 100644
--- a/clippy_lints/src/semicolon_if_nothing_returned.rs
+++ b/clippy_lints/src/semicolon_if_nothing_returned.rs
@@ -29,6 +29,7 @@ declare_clippy_lint! {
     ///     println!("Hello world");
     /// }
     /// ```
+    #[clippy::version = "1.52.0"]
     pub SEMICOLON_IF_NOTHING_RETURNED,
     pedantic,
     "add a semicolon if nothing is returned"
diff --git a/clippy_lints/src/serde_api.rs b/clippy_lints/src/serde_api.rs
index 2cd0f85999c..a38b3c4ab69 100644
--- a/clippy_lints/src/serde_api.rs
+++ b/clippy_lints/src/serde_api.rs
@@ -15,6 +15,7 @@ declare_clippy_lint! {
     /// ### Example
     /// Implementing `Visitor::visit_string` but not
     /// `Visitor::visit_str`.
+    #[clippy::version = "pre 1.29.0"]
     pub SERDE_API_MISUSE,
     correctness,
     "various things that will negatively affect your serde experience"
diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs
index 64841f33cc3..5f82aed872c 100644
--- a/clippy_lints/src/shadow.rs
+++ b/clippy_lints/src/shadow.rs
@@ -29,6 +29,7 @@ declare_clippy_lint! {
     /// // Good
     /// let y = &x; // use different variable name
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SHADOW_SAME,
     restriction,
     "rebinding a name to itself, e.g., `let mut x = &mut x`"
@@ -55,6 +56,7 @@ declare_clippy_lint! {
     /// let x = 2;
     /// let y = x + 1;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SHADOW_REUSE,
     restriction,
     "rebinding a name to an expression that re-uses the original value, e.g., `let x = x + 1`"
@@ -84,6 +86,7 @@ declare_clippy_lint! {
     /// // Good
     /// let w = z; // use different variable name
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SHADOW_UNRELATED,
     restriction,
     "rebinding a name without even using the original value"
diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs
index 87aa02b6585..28d32203da9 100644
--- a/clippy_lints/src/single_component_path_imports.rs
+++ b/clippy_lints/src/single_component_path_imports.rs
@@ -27,6 +27,7 @@ declare_clippy_lint! {
     ///     regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
     /// }
     /// ```
+    #[clippy::version = "1.43.0"]
     pub SINGLE_COMPONENT_PATH_IMPORTS,
     style,
     "imports with single component path are redundant"
diff --git a/clippy_lints/src/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs
index 3e4e4a8d0c0..df1e85afdd7 100644
--- a/clippy_lints/src/size_of_in_element_count.rs
+++ b/clippy_lints/src/size_of_in_element_count.rs
@@ -29,6 +29,7 @@ declare_clippy_lint! {
     /// let mut y = [2u8; SIZE];
     /// unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>() * SIZE) };
     /// ```
+    #[clippy::version = "1.50.0"]
     pub SIZE_OF_IN_ELEMENT_COUNT,
     correctness,
     "using `size_of::<T>` or `size_of_val::<T>` where a count of elements of `T` is expected"
diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs
index 3608fe1472d..1ae772ef70b 100644
--- a/clippy_lints/src/slow_vector_initialization.rs
+++ b/clippy_lints/src/slow_vector_initialization.rs
@@ -36,6 +36,7 @@ declare_clippy_lint! {
     /// let mut vec1 = vec![0; len];
     /// let mut vec2 = vec![0; len];
     /// ```
+    #[clippy::version = "1.32.0"]
     pub SLOW_VECTOR_INITIALIZATION,
     perf,
     "slow vector initialization"
diff --git a/clippy_lints/src/stable_sort_primitive.rs b/clippy_lints/src/stable_sort_primitive.rs
index 4ea1293d504..953d21e07a3 100644
--- a/clippy_lints/src/stable_sort_primitive.rs
+++ b/clippy_lints/src/stable_sort_primitive.rs
@@ -28,6 +28,7 @@ declare_clippy_lint! {
     /// let mut vec = vec![2, 1, 3];
     /// vec.sort_unstable();
     /// ```
+    #[clippy::version = "1.47.0"]
     pub STABLE_SORT_PRIMITIVE,
     perf,
     "use of sort() when sort_unstable() is equivalent"
diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs
index 6435107b8b4..368274440d5 100644
--- a/clippy_lints/src/strings.rs
+++ b/clippy_lints/src/strings.rs
@@ -31,6 +31,7 @@ declare_clippy_lint! {
     /// x += ", World";
     /// x.push_str(", World");
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub STRING_ADD_ASSIGN,
     pedantic,
     "using `x = x + ..` where x is a `String` instead of `push_str()`"
@@ -58,6 +59,7 @@ declare_clippy_lint! {
     /// let x = "Hello".to_owned();
     /// x + ", World";
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub STRING_ADD,
     restriction,
     "using `x + ..` where x is a `String` instead of `push_str()`"
@@ -102,6 +104,7 @@ declare_clippy_lint! {
     /// // Good
     /// let bs = b"a byte string";
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub STRING_LIT_AS_BYTES,
     nursery,
     "calling `as_bytes` on a string literal instead of using a byte string literal"
@@ -125,6 +128,7 @@ declare_clippy_lint! {
     /// ```rust,should_panic
     /// &"Ölkanne"[1..];
     /// ```
+    #[clippy::version = "1.58.0"]
     pub STRING_SLICE,
     restriction,
     "slicing a string"
@@ -227,6 +231,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let _ = &"Hello World!"[6..11];
     /// ```
+    #[clippy::version = "1.50.0"]
     pub STRING_FROM_UTF8_AS_BYTES,
     complexity,
     "casting string slices to byte slices and back"
@@ -371,6 +376,7 @@ declare_clippy_lint! {
     /// // example code which does not raise clippy warning
     /// let _ = "str".to_owned();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub STR_TO_STRING,
     restriction,
     "using `to_string()` on a `&str`, which should be `to_owned()`"
@@ -420,6 +426,7 @@ declare_clippy_lint! {
     /// let msg = String::from("Hello World");
     /// let _ = msg.clone();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub STRING_TO_STRING,
     restriction,
     "using `to_string()` on a `String`, which should be `clone()`"
diff --git a/clippy_lints/src/strlen_on_c_strings.rs b/clippy_lints/src/strlen_on_c_strings.rs
index 8bf40ec5312..be7431f11aa 100644
--- a/clippy_lints/src/strlen_on_c_strings.rs
+++ b/clippy_lints/src/strlen_on_c_strings.rs
@@ -30,6 +30,7 @@ declare_clippy_lint! {
     /// let cstring = CString::new("foo").expect("CString::new failed");
     /// let len = cstring.as_bytes().len();
     /// ```
+    #[clippy::version = "1.55.0"]
     pub STRLEN_ON_C_STRINGS,
     complexity,
     "using `libc::strlen` on a `CString` or `CStr` value, while `as_bytes().len()` or `to_bytes().len()` respectively can be used instead"
diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs
index 201aa067824..faf43fd9fc1 100644
--- a/clippy_lints/src/suspicious_operation_groupings.rs
+++ b/clippy_lints/src/suspicious_operation_groupings.rs
@@ -59,6 +59,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.50.0"]
     pub SUSPICIOUS_OPERATION_GROUPINGS,
     nursery,
     "groupings of binary operations that look suspiciously like typos"
diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs
index 682fad00a13..a3195de81d1 100644
--- a/clippy_lints/src/suspicious_trait_impl.rs
+++ b/clippy_lints/src/suspicious_trait_impl.rs
@@ -25,6 +25,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SUSPICIOUS_ARITHMETIC_IMPL,
     suspicious,
     "suspicious use of operators in impl of arithmetic trait"
@@ -46,6 +47,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SUSPICIOUS_OP_ASSIGN_IMPL,
     suspicious,
     "suspicious use of operators in impl of OpAssign trait"
diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs
index bd779124dee..4c10b12437d 100644
--- a/clippy_lints/src/swap.rs
+++ b/clippy_lints/src/swap.rs
@@ -35,6 +35,7 @@ declare_clippy_lint! {
     /// let mut b = 2;
     /// std::mem::swap(&mut a, &mut b);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MANUAL_SWAP,
     complexity,
     "manual swap of two variables"
@@ -60,6 +61,7 @@ declare_clippy_lint! {
     /// # let mut b = 2;
     /// std::mem::swap(&mut a, &mut b);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ALMOST_SWAPPED,
     correctness,
     "`foo = bar; bar = foo` sequence"
diff --git a/clippy_lints/src/tabs_in_doc_comments.rs b/clippy_lints/src/tabs_in_doc_comments.rs
index 4a67cabf323..c9b4b245f4c 100644
--- a/clippy_lints/src/tabs_in_doc_comments.rs
+++ b/clippy_lints/src/tabs_in_doc_comments.rs
@@ -51,6 +51,7 @@ declare_clippy_lint! {
     ///    second_string: String,
     ///}
     /// ```
+    #[clippy::version = "1.41.0"]
     pub TABS_IN_DOC_COMMENTS,
     style,
     "using tabs in doc comments is not recommended"
diff --git a/clippy_lints/src/temporary_assignment.rs b/clippy_lints/src/temporary_assignment.rs
index a9da690339c..3766b8f8ed1 100644
--- a/clippy_lints/src/temporary_assignment.rs
+++ b/clippy_lints/src/temporary_assignment.rs
@@ -17,6 +17,7 @@ declare_clippy_lint! {
     /// ```rust
     /// (0, 0).0 = 1
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TEMPORARY_ASSIGNMENT,
     complexity,
     "assignments to temporaries"
diff --git a/clippy_lints/src/to_digit_is_some.rs b/clippy_lints/src/to_digit_is_some.rs
index 1c14a919995..5eb58b47838 100644
--- a/clippy_lints/src/to_digit_is_some.rs
+++ b/clippy_lints/src/to_digit_is_some.rs
@@ -28,6 +28,7 @@ declare_clippy_lint! {
     /// # let radix = 10;
     /// let is_digit = c.is_digit(radix);
     /// ```
+    #[clippy::version = "1.41.0"]
     pub TO_DIGIT_IS_SOME,
     style,
     "`char.is_digit()` is clearer"
diff --git a/clippy_lints/src/to_string_in_display.rs b/clippy_lints/src/to_string_in_display.rs
index b7414cec87c..f8b6bdcd3e1 100644
--- a/clippy_lints/src/to_string_in_display.rs
+++ b/clippy_lints/src/to_string_in_display.rs
@@ -39,6 +39,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.48.0"]
     pub TO_STRING_IN_DISPLAY,
     correctness,
     "`to_string` method used while implementing `Display` trait"
diff --git a/clippy_lints/src/trailing_empty_array.rs b/clippy_lints/src/trailing_empty_array.rs
index c216a1f81ea..47c0a84cd46 100644
--- a/clippy_lints/src/trailing_empty_array.rs
+++ b/clippy_lints/src/trailing_empty_array.rs
@@ -28,6 +28,7 @@ declare_clippy_lint! {
     ///     last: [u32; 0],
     /// }
     /// ```
+    #[clippy::version = "1.58.0"]
     pub TRAILING_EMPTY_ARRAY,
     nursery,
     "struct with a trailing zero-sized array but without `#[repr(C)]` or another `repr` attribute"
diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs
index 73bdcae9e39..fb4abceac25 100644
--- a/clippy_lints/src/trait_bounds.rs
+++ b/clippy_lints/src/trait_bounds.rs
@@ -28,6 +28,7 @@ declare_clippy_lint! {
     /// ```rust
     /// pub fn foo<T>(t: T) where T: Copy + Clone {}
     /// ```
+    #[clippy::version = "1.38.0"]
     pub TYPE_REPETITION_IN_BOUNDS,
     pedantic,
     "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
@@ -57,6 +58,7 @@ declare_clippy_lint! {
     /// ```rust
     /// fn func<T>(arg: T) where T: Clone + Default {}
     /// ```
+    #[clippy::version = "1.47.0"]
     pub TRAIT_DUPLICATION_IN_BOUNDS,
     pedantic,
     "Check if the same trait bounds are specified twice during a function declaration"
diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs
index e6acf1a94c9..3ad4ec74bf5 100644
--- a/clippy_lints/src/transmute/mod.rs
+++ b/clippy_lints/src/transmute/mod.rs
@@ -36,6 +36,7 @@ declare_clippy_lint! {
     /// ```ignore
     /// let ptr: *const T = core::intrinsics::transmute('x')
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub WRONG_TRANSMUTE,
     correctness,
     "transmutes that are confusing at best, undefined behaviour at worst and always useless"
@@ -55,6 +56,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// core::intrinsics::transmute(t); // where the result type is the same as `t`'s
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub USELESS_TRANSMUTE,
     nursery,
     "transmutes that have the same to and from types or could be a cast/coercion"
@@ -80,6 +82,7 @@ declare_clippy_lint! {
     /// # let p: *const [i32] = &[];
     /// p as *const [u16];
     /// ```
+    #[clippy::version = "1.47.0"]
     pub TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
     complexity,
     "transmutes that could be a pointer cast"
@@ -98,6 +101,7 @@ declare_clippy_lint! {
     /// core::intrinsics::transmute(t) // where the result type is the same as
     ///                                // `*t` or `&t`'s
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CROSSPOINTER_TRANSMUTE,
     complexity,
     "transmutes that have to or from types that are a pointer to the other"
@@ -125,6 +129,7 @@ declare_clippy_lint! {
     /// // can be written:
     /// let _: &T = &*p;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TRANSMUTE_PTR_TO_REF,
     complexity,
     "transmutes from a pointer to a reference type"
@@ -158,6 +163,7 @@ declare_clippy_lint! {
     /// // should be:
     /// let _ = std::char::from_u32(x).unwrap();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TRANSMUTE_INT_TO_CHAR,
     complexity,
     "transmutes from an integer to a `char`"
@@ -191,6 +197,7 @@ declare_clippy_lint! {
     /// // should be:
     /// let _ = std::str::from_utf8(b).unwrap();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TRANSMUTE_BYTES_TO_STR,
     complexity,
     "transmutes from a `&[u8]` to a `&str`"
@@ -213,6 +220,7 @@ declare_clippy_lint! {
     /// // should be:
     /// let _: bool = x != 0;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TRANSMUTE_INT_TO_BOOL,
     complexity,
     "transmutes from an integer to a `bool`"
@@ -235,6 +243,7 @@ declare_clippy_lint! {
     /// // should be:
     /// let _: f32 = f32::from_bits(1_u32);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TRANSMUTE_INT_TO_FLOAT,
     complexity,
     "transmutes from an integer to a float"
@@ -257,6 +266,7 @@ declare_clippy_lint! {
     /// // should be:
     /// let _: u32 = 1f32.to_bits();
     /// ```
+    #[clippy::version = "1.41.0"]
     pub TRANSMUTE_FLOAT_TO_INT,
     complexity,
     "transmutes from a float to an integer"
@@ -279,6 +289,7 @@ declare_clippy_lint! {
     /// // should be
     /// let x: [u8; 8] = 0i64.to_ne_bytes();
     /// ```
+    #[clippy::version = "1.58.0"]
     pub TRANSMUTE_NUM_TO_BYTES,
     complexity,
     "transmutes from a number to an array of `u8`"
@@ -306,6 +317,7 @@ declare_clippy_lint! {
     /// let _ = ptr as *const f32;
     /// let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TRANSMUTE_PTR_TO_PTR,
     pedantic,
     "transmutes from a pointer to a pointer / a reference to a reference"
@@ -337,6 +349,7 @@ declare_clippy_lint! {
     /// ```rust
     /// vec![2_u16].into_iter().map(u32::from).collect::<Vec<_>>();
     /// ```
+    #[clippy::version = "1.40.0"]
     pub UNSOUND_COLLECTION_TRANSMUTE,
     correctness,
     "transmute between collections of layout-incompatible types"
diff --git a/clippy_lints/src/transmuting_null.rs b/clippy_lints/src/transmuting_null.rs
index ef80663d1da..7939dfedc3a 100644
--- a/clippy_lints/src/transmuting_null.rs
+++ b/clippy_lints/src/transmuting_null.rs
@@ -25,6 +25,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };
     /// ```
+    #[clippy::version = "1.35.0"]
     pub TRANSMUTING_NULL,
     correctness,
     "transmutes from a null pointer to a reference, which is undefined behavior"
diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs
index cc450b1e599..e0e7ec9a452 100644
--- a/clippy_lints/src/try_err.rs
+++ b/clippy_lints/src/try_err.rs
@@ -41,6 +41,7 @@ declare_clippy_lint! {
     ///     Ok(0)
     /// }
     /// ```
+    #[clippy::version = "1.38.0"]
     pub TRY_ERR,
     style,
     "return errors explicitly rather than hiding them behind a `?`"
diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs
index bbe07db5358..5a7ef760a30 100644
--- a/clippy_lints/src/types/mod.rs
+++ b/clippy_lints/src/types/mod.rs
@@ -43,6 +43,7 @@ declare_clippy_lint! {
     ///     values: Vec<Foo>,
     /// }
     /// ```
+    #[clippy::version = "1.57.0"]
     pub BOX_COLLECTION,
     perf,
     "usage of `Box<Vec<T>>`, vector elements are already on the heap"
@@ -75,6 +76,7 @@ declare_clippy_lint! {
     ///     values: Vec<i32>,
     /// }
     /// ```
+    #[clippy::version = "1.33.0"]
     pub VEC_BOX,
     complexity,
     "usage of `Vec<Box<T>>` where T: Sized, vector elements are already on the heap"
@@ -113,6 +115,7 @@ declare_clippy_lint! {
     ///     Contents::None
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub OPTION_OPTION,
     pedantic,
     "usage of `Option<Option<T>>`"
@@ -152,6 +155,7 @@ declare_clippy_lint! {
     /// # use std::collections::LinkedList;
     /// let x: LinkedList<usize> = LinkedList::new();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub LINKEDLIST,
     pedantic,
     "usage of LinkedList, usually a vector is faster, or a more specialized data structure like a `VecDeque`"
@@ -176,6 +180,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// fn foo(bar: &T) { ... }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub BORROWED_BOX,
     complexity,
     "a borrow of a boxed type"
@@ -200,6 +205,7 @@ declare_clippy_lint! {
     /// ```rust
     /// fn foo(bar: &usize) {}
     /// ```
+    #[clippy::version = "1.44.0"]
     pub REDUNDANT_ALLOCATION,
     perf,
     "redundant allocation"
@@ -234,6 +240,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// fn foo(interned: Rc<str>) { ... }
     /// ```
+    #[clippy::version = "1.48.0"]
     pub RC_BUFFER,
     restriction,
     "shared ownership of a buffer type"
@@ -255,6 +262,7 @@ declare_clippy_lint! {
     ///     inner: Rc<Vec<Vec<Box<(u32, u32, u32, u32)>>>>,
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TYPE_COMPLEXITY,
     complexity,
     "usage of very complex types that might be better factored into `type` definitions"
@@ -287,6 +295,7 @@ declare_clippy_lint! {
     /// use std::cell::RefCell
     /// fn foo(interned: Rc<RefCell<i32>>) { ... }
     /// ```
+    #[clippy::version = "1.55.0"]
     pub RC_MUTEX,
     restriction,
     "usage of `Rc<Mutex<T>>`"
diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs
index fbf66712261..c886faf5d28 100644
--- a/clippy_lints/src/undocumented_unsafe_blocks.rs
+++ b/clippy_lints/src/undocumented_unsafe_blocks.rs
@@ -39,6 +39,7 @@ declare_clippy_lint! {
     /// // Safety: references are guaranteed to be non-null.
     /// let ptr = unsafe { NonNull::new_unchecked(a) };
     /// ```
+    #[clippy::version = "1.58.0"]
     pub UNDOCUMENTED_UNSAFE_BLOCKS,
     restriction,
     "creating an unsafe block without explaining why it is safe"
diff --git a/clippy_lints/src/undropped_manually_drops.rs b/clippy_lints/src/undropped_manually_drops.rs
index 09570616593..c58fa67a023 100644
--- a/clippy_lints/src/undropped_manually_drops.rs
+++ b/clippy_lints/src/undropped_manually_drops.rs
@@ -28,6 +28,7 @@ declare_clippy_lint! {
     ///     std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S));
     /// }
     /// ```
+    #[clippy::version = "1.49.0"]
     pub UNDROPPED_MANUALLY_DROPS,
     correctness,
     "use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value"
diff --git a/clippy_lints/src/unicode.rs b/clippy_lints/src/unicode.rs
index f49ce696a04..a514e8c44e2 100644
--- a/clippy_lints/src/unicode.rs
+++ b/clippy_lints/src/unicode.rs
@@ -20,6 +20,7 @@ declare_clippy_lint! {
     /// ### Example
     /// You don't see it, but there may be a zero-width space or soft hyphen
     /// some­where in this text.
+    #[clippy::version = "1.49.0"]
     pub INVISIBLE_CHARACTERS,
     correctness,
     "using an invisible character in a string literal, which is confusing"
@@ -44,6 +45,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let x = String::from("\u{20ac}");
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NON_ASCII_LITERAL,
     restriction,
     "using any literal non-ASCII chars in a string literal instead of using the `\\u` escape"
@@ -62,6 +64,7 @@ declare_clippy_lint! {
     /// ### Example
     /// You may not see it, but "à"" and "à"" aren't the same string. The
     /// former when escaped is actually `"a\u{300}"` while the latter is `"\u{e0}"`.
+    #[clippy::version = "pre 1.29.0"]
     pub UNICODE_NOT_NFC,
     pedantic,
     "using a Unicode literal not in NFC normal form (see [Unicode tr15](http://www.unicode.org/reports/tr15/) for further information)"
diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs
index f3e8b688105..46cc76b150e 100644
--- a/clippy_lints/src/uninit_vec.rs
+++ b/clippy_lints/src/uninit_vec.rs
@@ -52,6 +52,7 @@ declare_clippy_lint! {
     ///    // perform initialization with `remaining`
     ///    vec.set_len(...);  // Safe to call `set_len()` on initialized part
     ///    ```
+    #[clippy::version = "1.58.0"]
     pub UNINIT_VEC,
     correctness,
     "Vec with uninitialized data"
diff --git a/clippy_lints/src/unit_hash.rs b/clippy_lints/src/unit_hash.rs
index a3a3f2d41c7..26b4e0f58a8 100644
--- a/clippy_lints/src/unit_hash.rs
+++ b/clippy_lints/src/unit_hash.rs
@@ -39,6 +39,7 @@ declare_clippy_lint! {
     /// 	WithValue(x) => x.hash(&mut state),
     /// }
     /// ```
+    #[clippy::version = "1.58.0"]
     pub UNIT_HASH,
     correctness,
     "hashing a unit value, which does nothing"
diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs
index db0f412f2a1..9fb8f236899 100644
--- a/clippy_lints/src/unit_return_expecting_ord.rs
+++ b/clippy_lints/src/unit_return_expecting_ord.rs
@@ -30,6 +30,7 @@ declare_clippy_lint! {
     /// let mut twins = vec!((1, 1), (2, 2));
     /// twins.sort_by_key(|x| { x.1; });
     /// ```
+    #[clippy::version = "1.47.0"]
     pub UNIT_RETURN_EXPECTING_ORD,
     correctness,
     "fn arguments of type Fn(...) -> Ord returning the unit type ()."
diff --git a/clippy_lints/src/unit_types/mod.rs b/clippy_lints/src/unit_types/mod.rs
index 66b1abbe50b..d9f5b53b413 100644
--- a/clippy_lints/src/unit_types/mod.rs
+++ b/clippy_lints/src/unit_types/mod.rs
@@ -21,6 +21,7 @@ declare_clippy_lint! {
     ///     1;
     /// };
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub LET_UNIT_VALUE,
     pedantic,
     "creating a `let` binding to a value of unit type, which usually can't be used afterwards"
@@ -68,6 +69,7 @@ declare_clippy_lint! {
     /// assert_eq!({ foo(); }, { bar(); });
     /// ```
     /// will always succeed
+    #[clippy::version = "pre 1.29.0"]
     pub UNIT_CMP,
     correctness,
     "comparing unit values"
@@ -88,6 +90,7 @@ declare_clippy_lint! {
     ///     baz(a);
     /// })
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNIT_ARG,
     complexity,
     "passing unit to a function"
diff --git a/clippy_lints/src/unnamed_address.rs b/clippy_lints/src/unnamed_address.rs
index 1eafdee0352..0bcafde658a 100644
--- a/clippy_lints/src/unnamed_address.rs
+++ b/clippy_lints/src/unnamed_address.rs
@@ -24,6 +24,7 @@ declare_clippy_lint! {
     ///     // ...
     /// }
     /// ```
+    #[clippy::version = "1.44.0"]
     pub FN_ADDRESS_COMPARISONS,
     correctness,
     "comparison with an address of a function item"
@@ -47,6 +48,7 @@ declare_clippy_lint! {
     ///     ...
     /// }
     /// ```
+    #[clippy::version = "1.44.0"]
     pub VTABLE_ADDRESS_COMPARISONS,
     correctness,
     "comparison with an address of a trait vtable"
diff --git a/clippy_lints/src/unnecessary_self_imports.rs b/clippy_lints/src/unnecessary_self_imports.rs
index 4cfd2df551f..839a4bdab09 100644
--- a/clippy_lints/src/unnecessary_self_imports.rs
+++ b/clippy_lints/src/unnecessary_self_imports.rs
@@ -26,6 +26,7 @@ declare_clippy_lint! {
     /// ```rust
     /// use std::io;
     /// ```
+    #[clippy::version = "1.53.0"]
     pub UNNECESSARY_SELF_IMPORTS,
     restriction,
     "imports ending in `::{self}`, which can be omitted"
diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs
index 26b56e0f2f3..d024577f485 100644
--- a/clippy_lints/src/unnecessary_sort_by.rs
+++ b/clippy_lints/src/unnecessary_sort_by.rs
@@ -39,6 +39,7 @@ declare_clippy_lint! {
     /// # let mut vec: Vec<A> = Vec::new();
     /// vec.sort_by_key(|a| a.foo());
     /// ```
+    #[clippy::version = "1.46.0"]
     pub UNNECESSARY_SORT_BY,
     complexity,
     "Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer"
diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs
index fcfa8403177..1728533f18b 100644
--- a/clippy_lints/src/unnecessary_wraps.rs
+++ b/clippy_lints/src/unnecessary_wraps.rs
@@ -49,6 +49,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.50.0"]
     pub UNNECESSARY_WRAPS,
     pedantic,
     "functions that only return `Ok` or `Some`"
diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs
index d6cf7190abb..0bd151fed91 100644
--- a/clippy_lints/src/unnested_or_patterns.rs
+++ b/clippy_lints/src/unnested_or_patterns.rs
@@ -39,6 +39,7 @@ declare_clippy_lint! {
     ///     if let Some(0 | 2) = Some(0) {}
     /// }
     /// ```
+    #[clippy::version = "1.46.0"]
     pub UNNESTED_OR_PATTERNS,
     pedantic,
     "unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`"
diff --git a/clippy_lints/src/unsafe_removed_from_name.rs b/clippy_lints/src/unsafe_removed_from_name.rs
index 3c694af2b9d..44b1989dbc6 100644
--- a/clippy_lints/src/unsafe_removed_from_name.rs
+++ b/clippy_lints/src/unsafe_removed_from_name.rs
@@ -21,6 +21,7 @@ declare_clippy_lint! {
     /// extern crate crossbeam;
     /// use crossbeam::{spawn_unsafe as spawn};
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNSAFE_REMOVED_FROM_NAME,
     style,
     "`unsafe` removed from API names on import"
diff --git a/clippy_lints/src/unused_async.rs b/clippy_lints/src/unused_async.rs
index f4808682b69..1ccb78425c2 100644
--- a/clippy_lints/src/unused_async.rs
+++ b/clippy_lints/src/unused_async.rs
@@ -29,6 +29,7 @@ declare_clippy_lint! {
     /// }
     /// let number_future = async { get_random_number_improved() };
     /// ```
+    #[clippy::version = "1.54.0"]
     pub UNUSED_ASYNC,
     pedantic,
     "finds async functions with no await statements"
diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs
index 031b182bd2f..d4b5c9770a2 100644
--- a/clippy_lints/src/unused_io_amount.rs
+++ b/clippy_lints/src/unused_io_amount.rs
@@ -29,6 +29,7 @@ declare_clippy_lint! {
     ///     Ok(())
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNUSED_IO_AMOUNT,
     correctness,
     "unused written/read amount"
diff --git a/clippy_lints/src/unused_self.rs b/clippy_lints/src/unused_self.rs
index e7e249c79a2..fd9d5b52e50 100644
--- a/clippy_lints/src/unused_self.rs
+++ b/clippy_lints/src/unused_self.rs
@@ -29,6 +29,7 @@ declare_clippy_lint! {
     ///     fn method() {}
     /// }
     /// ```
+    #[clippy::version = "1.40.0"]
     pub UNUSED_SELF,
     pedantic,
     "methods that contain a `self` argument but don't use it"
diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs
index 1164ac4938f..48c17fa2a40 100644
--- a/clippy_lints/src/unused_unit.rs
+++ b/clippy_lints/src/unused_unit.rs
@@ -28,6 +28,7 @@ declare_clippy_lint! {
     /// ```rust
     /// fn return_unit() {}
     /// ```
+    #[clippy::version = "1.31.0"]
     pub UNUSED_UNIT,
     style,
     "needless unit expression"
diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs
index ebaa9dcbbf8..9e83cae79bc 100644
--- a/clippy_lints/src/unwrap.rs
+++ b/clippy_lints/src/unwrap.rs
@@ -39,6 +39,7 @@ declare_clippy_lint! {
     ///     do_something_with(value)
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNNECESSARY_UNWRAP,
     complexity,
     "checks for calls of `unwrap[_err]()` that cannot fail"
@@ -65,6 +66,7 @@ declare_clippy_lint! {
     /// ```
     ///
     /// This code will always panic. The if condition should probably be inverted.
+    #[clippy::version = "pre 1.29.0"]
     pub PANICKING_UNWRAP,
     correctness,
     "checks for calls of `unwrap[_err]()` that will always fail"
diff --git a/clippy_lints/src/unwrap_in_result.rs b/clippy_lints/src/unwrap_in_result.rs
index 6447e3fa2ca..994df85cb8a 100644
--- a/clippy_lints/src/unwrap_in_result.rs
+++ b/clippy_lints/src/unwrap_in_result.rs
@@ -51,6 +51,7 @@ declare_clippy_lint! {
     ///     Ok(())
     /// }
     /// ```
+    #[clippy::version = "1.48.0"]
     pub UNWRAP_IN_RESULT,
     restriction,
     "functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`"
diff --git a/clippy_lints/src/upper_case_acronyms.rs b/clippy_lints/src/upper_case_acronyms.rs
index dbf335a70c8..4773e350760 100644
--- a/clippy_lints/src/upper_case_acronyms.rs
+++ b/clippy_lints/src/upper_case_acronyms.rs
@@ -33,6 +33,7 @@ declare_clippy_lint! {
     /// ```rust
     /// struct HttpResponse;
     /// ```
+    #[clippy::version = "1.51.0"]
     pub UPPER_CASE_ACRONYMS,
     style,
     "capitalized acronyms are against the naming convention"
diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs
index 09aad296f03..059f7f647f8 100644
--- a/clippy_lints/src/use_self.rs
+++ b/clippy_lints/src/use_self.rs
@@ -51,6 +51,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub USE_SELF,
     nursery,
     "unnecessary structure name repetition whereas `Self` is applicable"
diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs
index 88f11542072..0e4b32541c9 100644
--- a/clippy_lints/src/useless_conversion.rs
+++ b/clippy_lints/src/useless_conversion.rs
@@ -28,6 +28,7 @@ declare_clippy_lint! {
     /// // Good
     /// let s: String = format!("hello");
     /// ```
+    #[clippy::version = "1.45.0"]
     pub USELESS_CONVERSION,
     complexity,
     "calls to `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` which perform useless conversions to the same type"
diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs
index 824ec53ab9c..1f97c8ba7e6 100644
--- a/clippy_lints/src/utils/internal_lints.rs
+++ b/clippy_lints/src/utils/internal_lints.rs
@@ -8,6 +8,7 @@ use clippy_utils::{
     paths, SpanlessEq,
 };
 use if_chain::if_chain;
+use rustc_ast as ast;
 use rustc_ast::ast::{Crate, ItemKind, LitKind, ModKind, NodeId};
 use rustc_ast::visit::FnKind;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
@@ -25,10 +26,11 @@ use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintCon
 use rustc_middle::hir::map::Map;
 use rustc_middle::mir::interpret::ConstValue;
 use rustc_middle::ty;
+use rustc_semver::RustcVersion;
 use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{Symbol, SymbolStr};
-use rustc_span::{BytePos, Span};
+use rustc_span::{sym, BytePos, Span};
 use rustc_typeck::hir_ty_to_ty;
 
 use std::borrow::{Borrow, Cow};
@@ -314,6 +316,27 @@ declare_clippy_lint! {
     "non-idiomatic `if_chain!` usage"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for invalid `clippy::version` attributes.
+    ///
+    /// Valid values are:
+    /// * "pre 1.29.0"
+    /// * any valid semantic version
+    pub INVALID_CLIPPY_VERSION_ATTRIBUTE,
+    internal,
+    "found an invalid `clippy::version` attribute"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for declared clippy lints without the `clippy::version` attribute.
+    ///
+    pub MISSING_CLIPPY_VERSION_ATTRIBUTE,
+    internal,
+    "found clippy lint without `clippy::version` attribute"
+}
+
 declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
 
 impl EarlyLintPass for ClippyLintsInternal {
@@ -351,7 +374,7 @@ pub struct LintWithoutLintPass {
     registered_lints: FxHashSet<Symbol>,
 }
 
-impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS]);
+impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE]);
 
 impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
@@ -361,6 +384,8 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
 
         if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind {
             if is_lint_ref_type(cx, ty) {
+                check_invalid_clippy_version_attribute(cx, item);
+
                 let expr = &cx.tcx.hir().body(body_id).value;
                 if_chain! {
                     if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind;
@@ -458,6 +483,57 @@ fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &Ty<'_>) -> bool {
     false
 }
 
+fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) {
+    if let Some(value) = extract_clippy_version_value(cx, item) {
+        // The `sym!` macro doesn't work as it only expects a single token.
+        // It's better to keep it this way and have a direct `Symbol::intern` call here.
+        if value == Symbol::intern("pre 1.29.0") {
+            return;
+        }
+
+        if RustcVersion::parse(&*value.as_str()).is_err() {
+            span_lint_and_help(
+                cx,
+                INVALID_CLIPPY_VERSION_ATTRIBUTE,
+                item.span,
+                "this item has an invalid `clippy::version` attribute",
+                None,
+                "please use a valid sematic version, see `doc/adding_lints.md`",
+            );
+        }
+    } else {
+        span_lint_and_help(
+            cx,
+            MISSING_CLIPPY_VERSION_ATTRIBUTE,
+            item.span,
+            "this lint is missing the `clippy::version` attribute or version value",
+            None,
+            "please use a `clippy::version` attribute, see `doc/adding_lints.md`",
+        );
+    }
+}
+
+/// This function extracts the version value of a `clippy::version` attribute if the given value has
+/// one
+fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option<Symbol> {
+    let attrs = cx.tcx.hir().attrs(item.hir_id());
+    attrs.iter().find_map(|attr| {
+        if_chain! {
+            // Identify attribute
+            if let ast::AttrKind::Normal(ref attr_kind, _) = &attr.kind;
+            if let [tool_name, attr_name] = &attr_kind.path.segments[..];
+            if tool_name.ident.name == sym::clippy;
+            if attr_name.ident.name == sym::version;
+            if let Some(version) = attr.value_str();
+            then {
+                Some(version)
+            } else {
+                None
+            }
+        }
+    })
+}
+
 struct LintCollector<'a, 'tcx> {
     output: &'a mut FxHashSet<Symbol>,
     cx: &'a LateContext<'tcx>,
diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs
index 99cf4c1ed40..8051c58bad7 100644
--- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs
+++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs
@@ -25,7 +25,7 @@ use std::fs::{self, OpenOptions};
 use std::io::prelude::*;
 use std::path::Path;
 
-use crate::utils::internal_lints::is_lint_ref_type;
+use crate::utils::internal_lints::{extract_clippy_version_value, is_lint_ref_type};
 use clippy_utils::{
     diagnostics::span_lint, last_path_segment, match_def_path, match_function_call, match_path, paths, ty::match_type,
     ty::walk_ptrs_ty_depth,
@@ -116,6 +116,8 @@ const DEPRECATED_LINT_TYPE: [&str; 3] = ["clippy_lints", "deprecated_lints", "Cl
 const APPLICABILITY_NAME_INDEX: usize = 2;
 /// This applicability will be set for unresolved applicability values.
 const APPLICABILITY_UNRESOLVED_STR: &str = "Unresolved";
+/// The version that will be displayed if none has been defined
+const VERION_DEFAULT_STR: &str = "Unknown";
 
 declare_clippy_lint! {
     /// ### What it does
@@ -144,6 +146,7 @@ declare_clippy_lint! {
     ///     "docs": " ### What it does\nCollects metadata about clippy lints for the website. [...] "
     /// }
     /// ```
+    #[clippy::version = "1.56.0"]
     pub INTERNAL_METADATA_COLLECTOR,
     internal_warn,
     "A busy bee collection metadata about lints"
@@ -215,18 +218,27 @@ struct LintMetadata {
     group: String,
     level: String,
     docs: String,
+    version: String,
     /// This field is only used in the output and will only be
     /// mapped shortly before the actual output.
     applicability: Option<ApplicabilityInfo>,
 }
 
 impl LintMetadata {
-    fn new(id: String, id_span: SerializableSpan, group: String, level: &'static str, docs: String) -> Self {
+    fn new(
+        id: String,
+        id_span: SerializableSpan,
+        group: String,
+        level: &'static str,
+        version: String,
+        docs: String,
+    ) -> Self {
         Self {
             id,
             id_span,
             group,
             level: level.to_string(),
+            version,
             docs,
             applicability: None,
         }
@@ -410,12 +422,14 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector {
                     if let Some(configuration_section) = self.get_lint_configs(&lint_name) {
                         docs.push_str(&configuration_section);
                     }
+                    let version = get_lint_version(cx, item);
 
                     self.lints.push(LintMetadata::new(
                         lint_name,
                         SerializableSpan::from_item(cx, item),
                         group,
                         level,
+                        version,
                         docs,
                     ));
                 }
@@ -429,11 +443,14 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector {
                 // Metadata the little we can get from a deprecated lint
                 if let Some(docs) = extract_attr_docs_or_lint(cx, item);
                 then {
+                    let version = get_lint_version(cx, item);
+
                     self.lints.push(LintMetadata::new(
                         lint_name,
                         SerializableSpan::from_item(cx, item),
                         DEPRECATED_LINT_GROUP_STR.to_string(),
                         DEPRECATED_LINT_LEVEL,
+                        version,
                         docs,
                     ));
                 }
@@ -552,6 +569,13 @@ fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
     Some(docs)
 }
 
+fn get_lint_version(cx: &LateContext<'_>, item: &Item<'_>) -> String {
+    extract_clippy_version_value(cx, item).map_or_else(
+        || VERION_DEFAULT_STR.to_string(),
+        |version| version.as_str().to_string(),
+    )
+}
+
 fn get_lint_group_and_level_or_lint(
     cx: &LateContext<'_>,
     lint_name: &str,
@@ -663,7 +687,6 @@ fn extract_emission_info<'hir>(
             applicability = resolve_applicability(cx, arg);
         } else if arg_ty.is_closure() {
             multi_part |= check_is_multi_part(cx, arg);
-            // TODO xFrednet 2021-03-01: don't use or_else but rather a comparison
             applicability = applicability.or_else(|| resolve_applicability(cx, arg));
         }
     }
diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs
index d3234b5758a..79e7410c3a8 100644
--- a/clippy_lints/src/vec.rs
+++ b/clippy_lints/src/vec.rs
@@ -36,6 +36,7 @@ declare_clippy_lint! {
     /// // Good
     /// foo(&[1, 2]);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub USELESS_VEC,
     perf,
     "useless `vec!`"
diff --git a/clippy_lints/src/vec_init_then_push.rs b/clippy_lints/src/vec_init_then_push.rs
index b92b6ca4f43..1bc0eb6303c 100644
--- a/clippy_lints/src/vec_init_then_push.rs
+++ b/clippy_lints/src/vec_init_then_push.rs
@@ -27,6 +27,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let v = vec![0];
     /// ```
+    #[clippy::version = "1.51.0"]
     pub VEC_INIT_THEN_PUSH,
     perf,
     "`push` immediately after `Vec` creation"
diff --git a/clippy_lints/src/vec_resize_to_zero.rs b/clippy_lints/src/vec_resize_to_zero.rs
index 5c0429db6b8..3441d9ccdfa 100644
--- a/clippy_lints/src/vec_resize_to_zero.rs
+++ b/clippy_lints/src/vec_resize_to_zero.rs
@@ -20,6 +20,7 @@ declare_clippy_lint! {
     /// ```rust
     /// vec!(1, 2, 3, 4, 5).resize(0, 5)
     /// ```
+    #[clippy::version = "1.46.0"]
     pub VEC_RESIZE_TO_ZERO,
     correctness,
     "emptying a vector with `resize(0, an_int)` instead of `clear()` is probably an argument inversion mistake"
diff --git a/clippy_lints/src/verbose_file_reads.rs b/clippy_lints/src/verbose_file_reads.rs
index e07c12f4f16..ebdaff1e676 100644
--- a/clippy_lints/src/verbose_file_reads.rs
+++ b/clippy_lints/src/verbose_file_reads.rs
@@ -27,6 +27,7 @@ declare_clippy_lint! {
     /// # use std::fs;
     /// let mut bytes = fs::read("foo.txt").unwrap();
     /// ```
+    #[clippy::version = "1.44.0"]
     pub VERBOSE_FILE_READS,
     restriction,
     "use of `File::read_to_end` or `File::read_to_string`"
diff --git a/clippy_lints/src/wildcard_dependencies.rs b/clippy_lints/src/wildcard_dependencies.rs
index d0c98b6bd79..80d7b8a1b6d 100644
--- a/clippy_lints/src/wildcard_dependencies.rs
+++ b/clippy_lints/src/wildcard_dependencies.rs
@@ -20,6 +20,7 @@ declare_clippy_lint! {
     /// [dependencies]
     /// regex = "*"
     /// ```
+    #[clippy::version = "1.32.0"]
     pub WILDCARD_DEPENDENCIES,
     cargo,
     "wildcard dependencies being used"
diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs
index 2f3e525fdcf..832da66a536 100644
--- a/clippy_lints/src/wildcard_imports.rs
+++ b/clippy_lints/src/wildcard_imports.rs
@@ -34,6 +34,7 @@ declare_clippy_lint! {
     /// use std::cmp::Ordering;
     /// foo(Ordering::Less)
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ENUM_GLOB_USE,
     pedantic,
     "use items that import all variants of an enum"
@@ -86,6 +87,7 @@ declare_clippy_lint! {
     ///
     /// foo();
     /// ```
+    #[clippy::version = "1.43.0"]
     pub WILDCARD_IMPORTS,
     pedantic,
     "lint `use _::*` statements"
diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs
index 85d1f65c51f..1b3c15fdf23 100644
--- a/clippy_lints/src/write.rs
+++ b/clippy_lints/src/write.rs
@@ -31,6 +31,7 @@ declare_clippy_lint! {
     /// // Good
     /// println!();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub PRINTLN_EMPTY_STRING,
     style,
     "using `println!(\"\")` with an empty string"
@@ -55,6 +56,7 @@ declare_clippy_lint! {
     /// # let name = "World";
     /// println!("Hello {}!", name);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub PRINT_WITH_NEWLINE,
     style,
     "using `print!()` with a format string that ends in a single newline"
@@ -76,6 +78,7 @@ declare_clippy_lint! {
     /// ```rust
     /// println!("Hello world!");
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub PRINT_STDOUT,
     restriction,
     "printing on stdout"
@@ -97,6 +100,7 @@ declare_clippy_lint! {
     /// ```rust
     /// eprintln!("Hello world!");
     /// ```
+    #[clippy::version = "1.50.0"]
     pub PRINT_STDERR,
     restriction,
     "printing on stderr"
@@ -116,6 +120,7 @@ declare_clippy_lint! {
     /// # let foo = "bar";
     /// println!("{:?}", foo);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub USE_DEBUG,
     restriction,
     "use of `Debug`-based formatting"
@@ -142,6 +147,7 @@ declare_clippy_lint! {
     /// ```rust
     /// println!("foo");
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub PRINT_LITERAL,
     style,
     "printing a literal with a format string"
@@ -165,6 +171,7 @@ declare_clippy_lint! {
     /// // Good
     /// writeln!(buf);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub WRITELN_EMPTY_STRING,
     style,
     "using `writeln!(buf, \"\")` with an empty string"
@@ -191,6 +198,7 @@ declare_clippy_lint! {
     /// // Good
     /// writeln!(buf, "Hello {}!", name);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub WRITE_WITH_NEWLINE,
     style,
     "using `write!()` with a format string that ends in a single newline"
@@ -219,6 +227,7 @@ declare_clippy_lint! {
     /// // Good
     /// writeln!(buf, "foo");
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub WRITE_LITERAL,
     style,
     "writing a literal with a format string"
diff --git a/clippy_lints/src/zero_div_zero.rs b/clippy_lints/src/zero_div_zero.rs
index e0746ce4d81..641681185a2 100644
--- a/clippy_lints/src/zero_div_zero.rs
+++ b/clippy_lints/src/zero_div_zero.rs
@@ -20,6 +20,7 @@ declare_clippy_lint! {
     /// // Good
     /// let nan = f32::NAN;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ZERO_DIVIDED_BY_ZERO,
     complexity,
     "usage of `0.0 / 0.0` to obtain NaN instead of `f32::NAN` or `f64::NAN`"
diff --git a/clippy_lints/src/zero_sized_map_values.rs b/clippy_lints/src/zero_sized_map_values.rs
index aa6b2614bbc..eb8436a501d 100644
--- a/clippy_lints/src/zero_sized_map_values.rs
+++ b/clippy_lints/src/zero_sized_map_values.rs
@@ -36,6 +36,7 @@ declare_clippy_lint! {
     ///     todo!();
     /// }
     /// ```
+    #[clippy::version = "1.50.0"]
     pub ZERO_SIZED_MAP_VALUES,
     pedantic,
     "usage of map with zero-sized value type"
diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs
index c19b558cd8c..7ae9615d560 100644
--- a/clippy_utils/src/attrs.rs
+++ b/clippy_utils/src/attrs.rs
@@ -14,15 +14,14 @@ pub enum DeprecationStatus {
     None,
 }
 
+#[rustfmt::skip]
 pub const BUILTIN_ATTRIBUTES: &[(&str, DeprecationStatus)] = &[
-    ("author", DeprecationStatus::None),
-    ("cognitive_complexity", DeprecationStatus::None),
-    (
-        "cyclomatic_complexity",
-        DeprecationStatus::Replaced("cognitive_complexity"),
-    ),
-    ("dump", DeprecationStatus::None),
-    ("msrv", DeprecationStatus::None),
+    ("author",                DeprecationStatus::None),
+    ("version",               DeprecationStatus::None),
+    ("cognitive_complexity",  DeprecationStatus::None),
+    ("cyclomatic_complexity", DeprecationStatus::Replaced("cognitive_complexity")),
+    ("dump",                  DeprecationStatus::None),
+    ("msrv",                  DeprecationStatus::None),
 ];
 
 pub struct LimitStack {
diff --git a/doc/adding_lints.md b/doc/adding_lints.md
index 26d06d334cd..cf16a1d5d3d 100644
--- a/doc/adding_lints.md
+++ b/doc/adding_lints.md
@@ -189,6 +189,7 @@ declare_clippy_lint! {
     /// ```rust
     /// // example code
     /// ```
+    #[clippy::version = "1.29.0"]
     pub FOO_FUNCTIONS,
     pedantic,
     "function named `foo`, which is not a descriptive name"
@@ -199,6 +200,10 @@ declare_clippy_lint! {
   section. This is the default documentation style and will be displayed
   [like this][example_lint_page]. To render and open this documentation locally
   in a browser, run `cargo dev serve`.
+* The `#[clippy::version]` attribute will be rendered as part of the lint documentation.
+  The value should be set to the current Rust version that the lint is developed in,
+  it can be retrieved by running `rustc -vV` in the rust-clippy directory. The version
+  is listed under *release*. (Use the version without the `-nightly`) suffix.
 * `FOO_FUNCTIONS` is the name of our lint. Be sure to follow the
   [lint naming guidelines][lint_naming] here when naming your lint.
   In short, the name should state the thing that is being checked for and
@@ -503,6 +508,7 @@ declare_clippy_lint! {
     /// // Good
     /// Insert a short example of improved code that doesn't trigger the lint
     /// ```
+    #[clippy::version = "1.29.0"]
     pub FOO_FUNCTIONS,
     pedantic,
     "function named `foo`, which is not a descriptive name"
diff --git a/tests/ui-internal/check_clippy_version_attribute.rs b/tests/ui-internal/check_clippy_version_attribute.rs
new file mode 100644
index 00000000000..31acac89cc6
--- /dev/null
+++ b/tests/ui-internal/check_clippy_version_attribute.rs
@@ -0,0 +1,87 @@
+#![deny(clippy::internal)]
+#![feature(rustc_private)]
+
+#[macro_use]
+extern crate rustc_middle;
+#[macro_use]
+extern crate rustc_session;
+extern crate rustc_lint;
+
+///////////////////////
+// Valid descriptions
+///////////////////////
+declare_tool_lint! {
+    #[clippy::version = "pre 1.29.0"]
+    pub clippy::VALID_ONE,
+    Warn,
+    "One",
+    report_in_external_macro: true
+}
+
+declare_tool_lint! {
+    #[clippy::version = "1.29.0"]
+    pub clippy::VALID_TWO,
+    Warn,
+    "Two",
+    report_in_external_macro: true
+}
+
+declare_tool_lint! {
+    #[clippy::version = "1.59.0"]
+    pub clippy::VALID_THREE,
+    Warn,
+    "Three",
+    report_in_external_macro: true
+}
+
+///////////////////////
+// Invalid attributes
+///////////////////////
+declare_tool_lint! {
+    #[clippy::version = "1.2.3.4.5.6"]
+    pub clippy::INVALID_ONE,
+    Warn,
+    "One",
+    report_in_external_macro: true
+}
+
+declare_tool_lint! {
+    #[clippy::version = "I'm a string"]
+    pub clippy::INVALID_TWO,
+    Warn,
+    "Two",
+    report_in_external_macro: true
+}
+
+///////////////////////
+// Missing attribute test
+///////////////////////
+declare_tool_lint! {
+    #[clippy::version]
+    pub clippy::MISSING_ATTRIBUTE_ONE,
+    Warn,
+    "Two",
+    report_in_external_macro: true
+}
+
+declare_tool_lint! {
+    pub clippy::MISSING_ATTRIBUTE_TWO,
+    Warn,
+    "Two",
+    report_in_external_macro: true
+}
+
+#[allow(clippy::missing_clippy_version_attribute)]
+mod internal_clippy_lints {
+    declare_tool_lint! {
+        pub clippy::ALLOW_MISSING_ATTRIBUTE_ONE,
+        Warn,
+        "Two",
+        report_in_external_macro: true
+    }
+}
+
+use crate::internal_clippy_lints::ALLOW_MISSING_ATTRIBUTE_ONE;
+declare_lint_pass!(Pass2 => [VALID_ONE, VALID_TWO, VALID_THREE, INVALID_ONE, INVALID_TWO, MISSING_ATTRIBUTE_ONE, MISSING_ATTRIBUTE_TWO, ALLOW_MISSING_ATTRIBUTE_ONE]);
+
+fn main() {}
diff --git a/tests/ui-internal/check_clippy_version_attribute.stderr b/tests/ui-internal/check_clippy_version_attribute.stderr
new file mode 100644
index 00000000000..9302e02ccb9
--- /dev/null
+++ b/tests/ui-internal/check_clippy_version_attribute.stderr
@@ -0,0 +1,73 @@
+error: this item has an invalid `clippy::version` attribute
+  --> $DIR/check_clippy_version_attribute.rs:40:1
+   |
+LL | / declare_tool_lint! {
+LL | |     #[clippy::version = "1.2.3.4.5.6"]
+LL | |     pub clippy::INVALID_ONE,
+LL | |     Warn,
+LL | |     "One",
+LL | |     report_in_external_macro: true
+LL | | }
+   | |_^
+   |
+note: the lint level is defined here
+  --> $DIR/check_clippy_version_attribute.rs:1:9
+   |
+LL | #![deny(clippy::internal)]
+   |         ^^^^^^^^^^^^^^^^
+   = note: `#[deny(clippy::invalid_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]`
+   = help: please use a valid sematic version, see `doc/adding_lints.md`
+   = note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: this item has an invalid `clippy::version` attribute
+  --> $DIR/check_clippy_version_attribute.rs:48:1
+   |
+LL | / declare_tool_lint! {
+LL | |     #[clippy::version = "I'm a string"]
+LL | |     pub clippy::INVALID_TWO,
+LL | |     Warn,
+LL | |     "Two",
+LL | |     report_in_external_macro: true
+LL | | }
+   | |_^
+   |
+   = help: please use a valid sematic version, see `doc/adding_lints.md`
+   = note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: this lint is missing the `clippy::version` attribute or version value
+  --> $DIR/check_clippy_version_attribute.rs:59:1
+   |
+LL | / declare_tool_lint! {
+LL | |     #[clippy::version]
+LL | |     pub clippy::MISSING_ATTRIBUTE_ONE,
+LL | |     Warn,
+LL | |     "Two",
+LL | |     report_in_external_macro: true
+LL | | }
+   | |_^
+   |
+note: the lint level is defined here
+  --> $DIR/check_clippy_version_attribute.rs:1:9
+   |
+LL | #![deny(clippy::internal)]
+   |         ^^^^^^^^^^^^^^^^
+   = note: `#[deny(clippy::missing_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]`
+   = help: please use a `clippy::version` attribute, see `doc/adding_lints.md`
+   = note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: this lint is missing the `clippy::version` attribute or version value
+  --> $DIR/check_clippy_version_attribute.rs:67:1
+   |
+LL | / declare_tool_lint! {
+LL | |     pub clippy::MISSING_ATTRIBUTE_TWO,
+LL | |     Warn,
+LL | |     "Two",
+LL | |     report_in_external_macro: true
+LL | | }
+   | |_^
+   |
+   = help: please use a `clippy::version` attribute, see `doc/adding_lints.md`
+   = note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui-internal/collapsible_span_lint_calls.fixed b/tests/ui-internal/collapsible_span_lint_calls.fixed
index 7764cc8da78..a5a6f20ddd5 100644
--- a/tests/ui-internal/collapsible_span_lint_calls.fixed
+++ b/tests/ui-internal/collapsible_span_lint_calls.fixed
@@ -1,5 +1,6 @@
 // run-rustfix
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 #![feature(rustc_private)]
 
 extern crate clippy_utils;
diff --git a/tests/ui-internal/collapsible_span_lint_calls.rs b/tests/ui-internal/collapsible_span_lint_calls.rs
index bdd296db832..6d783aa0786 100644
--- a/tests/ui-internal/collapsible_span_lint_calls.rs
+++ b/tests/ui-internal/collapsible_span_lint_calls.rs
@@ -1,5 +1,6 @@
 // run-rustfix
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 #![feature(rustc_private)]
 
 extern crate clippy_utils;
diff --git a/tests/ui-internal/collapsible_span_lint_calls.stderr b/tests/ui-internal/collapsible_span_lint_calls.stderr
index 0632b038577..558d1299160 100644
--- a/tests/ui-internal/collapsible_span_lint_calls.stderr
+++ b/tests/ui-internal/collapsible_span_lint_calls.stderr
@@ -1,5 +1,5 @@
 error: this call is collapsible
-  --> $DIR/collapsible_span_lint_calls.rs:35:9
+  --> $DIR/collapsible_span_lint_calls.rs:36:9
    |
 LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 LL | |             db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable);
@@ -14,7 +14,7 @@ LL | #![deny(clippy::internal)]
    = note: `#[deny(clippy::collapsible_span_lint_calls)]` implied by `#[deny(clippy::internal)]`
 
 error: this call is collapsible
-  --> $DIR/collapsible_span_lint_calls.rs:38:9
+  --> $DIR/collapsible_span_lint_calls.rs:39:9
    |
 LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 LL | |             db.span_help(expr.span, help_msg);
@@ -22,7 +22,7 @@ LL | |         });
    | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg)`
 
 error: this call is collapsible
-  --> $DIR/collapsible_span_lint_calls.rs:41:9
+  --> $DIR/collapsible_span_lint_calls.rs:42:9
    |
 LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 LL | |             db.help(help_msg);
@@ -30,7 +30,7 @@ LL | |         });
    | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)`
 
 error: this call is collspible
-  --> $DIR/collapsible_span_lint_calls.rs:44:9
+  --> $DIR/collapsible_span_lint_calls.rs:45:9
    |
 LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 LL | |             db.span_note(expr.span, note_msg);
@@ -38,7 +38,7 @@ LL | |         });
    | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)`
 
 error: this call is collspible
-  --> $DIR/collapsible_span_lint_calls.rs:47:9
+  --> $DIR/collapsible_span_lint_calls.rs:48:9
    |
 LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 LL | |             db.note(note_msg);
diff --git a/tests/ui-internal/custom_ice_message.rs b/tests/ui-internal/custom_ice_message.rs
index 5b30c9d5721..5057a018300 100644
--- a/tests/ui-internal/custom_ice_message.rs
+++ b/tests/ui-internal/custom_ice_message.rs
@@ -4,6 +4,7 @@
 // normalize-stderr-test: "', .*clippy_lints" -> "', clippy_lints"
 
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 
 fn it_looks_like_you_are_trying_to_kill_clippy() {}
 
diff --git a/tests/ui-internal/default_lint.rs b/tests/ui-internal/default_lint.rs
index 053faae02ce..da29aedb2a3 100644
--- a/tests/ui-internal/default_lint.rs
+++ b/tests/ui-internal/default_lint.rs
@@ -1,4 +1,5 @@
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 #![feature(rustc_private)]
 
 #[macro_use]
diff --git a/tests/ui-internal/default_lint.stderr b/tests/ui-internal/default_lint.stderr
index 4735573a47d..af6735f4e4d 100644
--- a/tests/ui-internal/default_lint.stderr
+++ b/tests/ui-internal/default_lint.stderr
@@ -1,5 +1,5 @@
 error: the lint `TEST_LINT_DEFAULT` has the default lint description
-  --> $DIR/default_lint.rs:17:1
+  --> $DIR/default_lint.rs:18:1
    |
 LL | / declare_tool_lint! {
 LL | |     pub clippy::TEST_LINT_DEFAULT,
diff --git a/tests/ui-internal/if_chain_style.rs b/tests/ui-internal/if_chain_style.rs
index e064fd188c8..b0d89e038aa 100644
--- a/tests/ui-internal/if_chain_style.rs
+++ b/tests/ui-internal/if_chain_style.rs
@@ -1,5 +1,5 @@
 #![warn(clippy::if_chain_style)]
-#![allow(clippy::no_effect, clippy::nonminimal_bool)]
+#![allow(clippy::no_effect, clippy::nonminimal_bool, clippy::missing_clippy_version_attribute)]
 
 extern crate if_chain;
 
diff --git a/tests/ui-internal/interning_defined_symbol.fixed b/tests/ui-internal/interning_defined_symbol.fixed
index 9ab845a573a..6b7fd6efe39 100644
--- a/tests/ui-internal/interning_defined_symbol.fixed
+++ b/tests/ui-internal/interning_defined_symbol.fixed
@@ -1,5 +1,6 @@
 // run-rustfix
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 #![feature(rustc_private)]
 
 extern crate rustc_span;
diff --git a/tests/ui-internal/interning_defined_symbol.rs b/tests/ui-internal/interning_defined_symbol.rs
index a58e182971d..98d7d7adad1 100644
--- a/tests/ui-internal/interning_defined_symbol.rs
+++ b/tests/ui-internal/interning_defined_symbol.rs
@@ -1,5 +1,6 @@
 // run-rustfix
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 #![feature(rustc_private)]
 
 extern crate rustc_span;
diff --git a/tests/ui-internal/interning_defined_symbol.stderr b/tests/ui-internal/interning_defined_symbol.stderr
index 50c1c268eb1..4e99636e683 100644
--- a/tests/ui-internal/interning_defined_symbol.stderr
+++ b/tests/ui-internal/interning_defined_symbol.stderr
@@ -1,5 +1,5 @@
 error: interning a defined symbol
-  --> $DIR/interning_defined_symbol.rs:17:13
+  --> $DIR/interning_defined_symbol.rs:18:13
    |
 LL |     let _ = Symbol::intern("f32");
    |             ^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::f32`
@@ -12,19 +12,19 @@ LL | #![deny(clippy::internal)]
    = note: `#[deny(clippy::interning_defined_symbol)]` implied by `#[deny(clippy::internal)]`
 
 error: interning a defined symbol
-  --> $DIR/interning_defined_symbol.rs:20:13
+  --> $DIR/interning_defined_symbol.rs:21:13
    |
 LL |     let _ = sym!(f32);
    |             ^^^^^^^^^ help: try: `rustc_span::sym::f32`
 
 error: interning a defined symbol
-  --> $DIR/interning_defined_symbol.rs:23:13
+  --> $DIR/interning_defined_symbol.rs:24:13
    |
 LL |     let _ = Symbol::intern("proc-macro");
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::proc_dash_macro`
 
 error: interning a defined symbol
-  --> $DIR/interning_defined_symbol.rs:26:13
+  --> $DIR/interning_defined_symbol.rs:27:13
    |
 LL |     let _ = Symbol::intern("self");
    |             ^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::kw::SelfLower`
diff --git a/tests/ui-internal/invalid_paths.rs b/tests/ui-internal/invalid_paths.rs
index a3b19c2e394..b823ff7fe37 100644
--- a/tests/ui-internal/invalid_paths.rs
+++ b/tests/ui-internal/invalid_paths.rs
@@ -1,4 +1,5 @@
 #![warn(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 
 mod paths {
     // Good path
diff --git a/tests/ui-internal/invalid_paths.stderr b/tests/ui-internal/invalid_paths.stderr
index 20aa81b98a0..0a8e5427978 100644
--- a/tests/ui-internal/invalid_paths.stderr
+++ b/tests/ui-internal/invalid_paths.stderr
@@ -1,5 +1,5 @@
 error: invalid path
-  --> $DIR/invalid_paths.rs:17:5
+  --> $DIR/invalid_paths.rs:18:5
    |
 LL |     pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"];
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -7,7 +7,7 @@ LL |     pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"];
    = note: `-D clippy::invalid-paths` implied by `-D warnings`
 
 error: invalid path
-  --> $DIR/invalid_paths.rs:20:5
+  --> $DIR/invalid_paths.rs:21:5
    |
 LL |     pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"];
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui-internal/lint_without_lint_pass.rs b/tests/ui-internal/lint_without_lint_pass.rs
index beaef79a340..1fd03cfe36d 100644
--- a/tests/ui-internal/lint_without_lint_pass.rs
+++ b/tests/ui-internal/lint_without_lint_pass.rs
@@ -1,4 +1,5 @@
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 #![feature(rustc_private)]
 
 #[macro_use]
diff --git a/tests/ui-internal/lint_without_lint_pass.stderr b/tests/ui-internal/lint_without_lint_pass.stderr
index e308e13da13..de04920b8e6 100644
--- a/tests/ui-internal/lint_without_lint_pass.stderr
+++ b/tests/ui-internal/lint_without_lint_pass.stderr
@@ -1,5 +1,5 @@
 error: the lint `TEST_LINT` is not added to any `LintPass`
-  --> $DIR/lint_without_lint_pass.rs:11:1
+  --> $DIR/lint_without_lint_pass.rs:12:1
    |
 LL | / declare_tool_lint! {
 LL | |     pub clippy::TEST_LINT,
diff --git a/tests/ui-internal/match_type_on_diag_item.rs b/tests/ui-internal/match_type_on_diag_item.rs
index be7b7a9af19..4b41ff15e80 100644
--- a/tests/ui-internal/match_type_on_diag_item.rs
+++ b/tests/ui-internal/match_type_on_diag_item.rs
@@ -1,4 +1,5 @@
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 #![feature(rustc_private)]
 
 extern crate clippy_utils;
diff --git a/tests/ui-internal/match_type_on_diag_item.stderr b/tests/ui-internal/match_type_on_diag_item.stderr
index bf1d67e6054..e3cb6b6c22e 100644
--- a/tests/ui-internal/match_type_on_diag_item.stderr
+++ b/tests/ui-internal/match_type_on_diag_item.stderr
@@ -1,5 +1,5 @@
 error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item
-  --> $DIR/match_type_on_diag_item.rs:30:17
+  --> $DIR/match_type_on_diag_item.rs:31:17
    |
 LL |         let _ = match_type(cx, ty, &OPTION);
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Option)`
@@ -12,13 +12,13 @@ LL | #![deny(clippy::internal)]
    = note: `#[deny(clippy::match_type_on_diagnostic_item)]` implied by `#[deny(clippy::internal)]`
 
 error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item
-  --> $DIR/match_type_on_diag_item.rs:31:17
+  --> $DIR/match_type_on_diag_item.rs:32:17
    |
 LL |         let _ = match_type(cx, ty, &["core", "result", "Result"]);
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Result)`
 
 error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item
-  --> $DIR/match_type_on_diag_item.rs:34:17
+  --> $DIR/match_type_on_diag_item.rs:35:17
    |
 LL |         let _ = clippy_utils::ty::match_type(cx, ty, rc_path);
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Rc)`
diff --git a/tests/ui-internal/outer_expn_data.fixed b/tests/ui-internal/outer_expn_data.fixed
index b0b3498f057..bb82faf0c90 100644
--- a/tests/ui-internal/outer_expn_data.fixed
+++ b/tests/ui-internal/outer_expn_data.fixed
@@ -1,6 +1,7 @@
 // run-rustfix
 
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 #![feature(rustc_private)]
 
 extern crate rustc_hir;
diff --git a/tests/ui-internal/outer_expn_data.rs b/tests/ui-internal/outer_expn_data.rs
index 55a3fed00d0..187d468b392 100644
--- a/tests/ui-internal/outer_expn_data.rs
+++ b/tests/ui-internal/outer_expn_data.rs
@@ -1,6 +1,7 @@
 // run-rustfix
 
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 #![feature(rustc_private)]
 
 extern crate rustc_hir;
diff --git a/tests/ui-internal/outer_expn_data.stderr b/tests/ui-internal/outer_expn_data.stderr
index 56b6ce1f78e..afef696785e 100644
--- a/tests/ui-internal/outer_expn_data.stderr
+++ b/tests/ui-internal/outer_expn_data.stderr
@@ -1,5 +1,5 @@
 error: usage of `outer_expn().expn_data()`
-  --> $DIR/outer_expn_data.rs:24:34
+  --> $DIR/outer_expn_data.rs:25:34
    |
 LL |         let _ = expr.span.ctxt().outer_expn().expn_data();
    |                                  ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `outer_expn_data()`
diff --git a/tests/ui-internal/unnecessary_symbol_str.fixed b/tests/ui-internal/unnecessary_symbol_str.fixed
index 95b8c6dfe89..4f5336663a8 100644
--- a/tests/ui-internal/unnecessary_symbol_str.fixed
+++ b/tests/ui-internal/unnecessary_symbol_str.fixed
@@ -1,7 +1,11 @@
 // run-rustfix
 #![feature(rustc_private)]
 #![deny(clippy::internal)]
-#![allow(clippy::unnecessary_operation, unused_must_use)]
+#![allow(
+    clippy::unnecessary_operation,
+    unused_must_use,
+    clippy::missing_clippy_version_attribute
+)]
 
 extern crate rustc_span;
 
diff --git a/tests/ui-internal/unnecessary_symbol_str.rs b/tests/ui-internal/unnecessary_symbol_str.rs
index ad6937cf60a..894aa1d3bc6 100644
--- a/tests/ui-internal/unnecessary_symbol_str.rs
+++ b/tests/ui-internal/unnecessary_symbol_str.rs
@@ -1,7 +1,11 @@
 // run-rustfix
 #![feature(rustc_private)]
 #![deny(clippy::internal)]
-#![allow(clippy::unnecessary_operation, unused_must_use)]
+#![allow(
+    clippy::unnecessary_operation,
+    unused_must_use,
+    clippy::missing_clippy_version_attribute
+)]
 
 extern crate rustc_span;
 
diff --git a/tests/ui-internal/unnecessary_symbol_str.stderr b/tests/ui-internal/unnecessary_symbol_str.stderr
index 12e05eaa7a0..75367bf4bc5 100644
--- a/tests/ui-internal/unnecessary_symbol_str.stderr
+++ b/tests/ui-internal/unnecessary_symbol_str.stderr
@@ -1,5 +1,5 @@
 error: unnecessary `Symbol` to string conversion
-  --> $DIR/unnecessary_symbol_str.rs:11:5
+  --> $DIR/unnecessary_symbol_str.rs:15:5
    |
 LL |     Symbol::intern("foo").as_str() == "clippy";
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::sym::clippy`
@@ -12,25 +12,25 @@ LL | #![deny(clippy::internal)]
    = note: `#[deny(clippy::unnecessary_symbol_str)]` implied by `#[deny(clippy::internal)]`
 
 error: unnecessary `Symbol` to string conversion
-  --> $DIR/unnecessary_symbol_str.rs:12:5
+  --> $DIR/unnecessary_symbol_str.rs:16:5
    |
 LL |     Symbol::intern("foo").to_string() == "self";
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::symbol::kw::SelfLower`
 
 error: unnecessary `Symbol` to string conversion
-  --> $DIR/unnecessary_symbol_str.rs:13:5
+  --> $DIR/unnecessary_symbol_str.rs:17:5
    |
 LL |     Symbol::intern("foo").to_ident_string() != "Self";
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") != rustc_span::symbol::kw::SelfUpper`
 
 error: unnecessary `Symbol` to string conversion
-  --> $DIR/unnecessary_symbol_str.rs:14:5
+  --> $DIR/unnecessary_symbol_str.rs:18:5
    |
 LL |     &*Ident::empty().as_str() == "clippy";
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ident::empty().name == rustc_span::sym::clippy`
 
 error: unnecessary `Symbol` to string conversion
-  --> $DIR/unnecessary_symbol_str.rs:15:5
+  --> $DIR/unnecessary_symbol_str.rs:19:5
    |
 LL |     "clippy" == Ident::empty().to_string();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::clippy == Ident::empty().name`
diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html
index 48421150a54..58e8b4f4829 100644
--- a/util/gh-pages/index.html
+++ b/util/gh-pages/index.html
@@ -118,6 +118,12 @@ Otherwise, have a great day =^.^=
             background-color: #777777;
             margin: auto 5px;
         }
+
+        .label-version {
+            background-color: #777777;
+            margin: auto 5px;
+            font-family: monospace;
+        }
     </style>
     <style>
         /* Expanding the mdBoom theme*/
@@ -339,7 +345,11 @@ Otherwise, have a great day =^.^=
                             <span class="label label-default label-applicability">{{lint.applicability.applicability}}</span>
                             <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint_defs/enum.Applicability.html#variants">(?)</a>
                         </div>
-                        <!-- TODO xFrednet 2021-05-19: Somehow collect and show the version See rust-clippy#6492 -->
+                        <!-- Clippy version -->
+                        <div class="lint-additional-info-item">
+                            <span>{{lint.group == "deprecated" ? "Deprecated" : "Added"}} in: </span>
+                            <span class="label label-default label-version">{{lint.version}}</span>
+                        </div>
                         <!-- Open related issues -->
                         <div class="lint-additional-info-item">
                             <a href="https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+{{lint.id}}">Related Issues</a>