Add lint to check for invalid clippy:version attributes

This commit is contained in:
xFrednet 2021-10-21 23:00:31 +02:00
parent d647696c1f
commit 9d9d06d557
No known key found for this signature in database
GPG key ID: FCDCBF29AF64D601
7 changed files with 184 additions and 22 deletions

View file

@ -9,6 +9,7 @@ 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),

View file

@ -16,6 +16,8 @@ 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,

View file

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

View file

@ -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,26 @@ declare_clippy_lint! {
"non-idiomatic `if_chain!` usage"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for invalid `clippy::version` attributes
///
/// ```txt
/// +-------------------------------+
/// | Valid values are: |
/// | * "pre 1.29.0" |
/// | * any valid semantic version |
/// +-------------------------------+
/// \
/// \ (^v^)
/// <( )>
/// w w
/// ```
pub INVALID_CLIPPY_VERSION_ATTRIBUTE,
internal,
"found an invalid `clippy::version` attribute"
}
declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
impl EarlyLintPass for ClippyLintsInternal {
@ -351,7 +373,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]);
impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
@ -361,6 +383,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 +482,48 @@ 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. I think
// 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`",
);
}
}
}
/// 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>,

View file

@ -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,
@ -570,25 +570,10 @@ fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
}
fn get_lint_version(cx: &LateContext<'_>, item: &Item<'_>) -> String {
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.as_str().to_string())
} else {
None
}
}
})
.unwrap_or_else(|| VERION_DEFAULT_STR.to_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(

View file

@ -0,0 +1,69 @@
#![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
}
///////////////////////
// Ignored attributes
///////////////////////
declare_tool_lint! {
#[clippy::version]
pub clippy::IGNORED_ONE,
Warn,
"ONE",
report_in_external_macro: true
}
declare_lint_pass!(Pass2 => [VALID_ONE, VALID_TWO, VALID_THREE, INVALID_ONE, INVALID_TWO, IGNORED_ONE]);
fn main() {}

View file

@ -0,0 +1,38 @@
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: aborting due to 2 previous errors