Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New lint: Inherent method #[must_use] added #283

Merged
merged 12 commits into from
Jan 16, 2023
80 changes: 80 additions & 0 deletions src/lints/inherent_method_must_use_added.ron
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
SemverQuery(
id: "inherent_method_must_use_added",
human_readable_name: "inherent method #[must_use] added",
description: "An inherent method has been marked with #[must_use].",
required_update: Minor,

// TODO: Change the reference link to point to the cargo semver reference
// once it has a section on attribute #[must_use].
reference_link: Some("https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute"),
query: r#"
{
CrateDiff {
current {
item {
... on ImplOwner {
visibility_limit @filter(op: "=", value: ["$public"]) @output
name @tag @output
owner_type: __typename @tag @output

importable_path {
path @tag @output
}

inherent_impl {
method {
method_visibility: visibility_limit @filter(op: "=", value: ["$public"]) @output
method_name: name @tag @output

attribute {
new_attr: raw_attribute @output
content {
base @filter(op: "=", value: ["$must_use"])
}
}

span_: span @optional {
filename @output
begin_line @output
}
}
}
}
}
}
baseline {
item {
... on ImplOwner {
visibility_limit @filter(op: "=", value: ["$public"])
name @filter(op: "=", value: ["%name"])
__typename @filter(op: "=", value: ["%owner_type"])

importable_path {
path @filter(op: "=", value: ["%path"])
}

inherent_impl {
method {
visibility_limit @filter(op: "=", value: ["$public"])
name @filter(op: "=", value: ["%method_name"])

attribute @fold @transform(op: "count") @filter(op: "=", value: ["$zero"]) {
content {
base @filter(op: "=", value: ["$must_use"])
}
}
}
}
}
}
}
}
}"#,
arguments: {
"public": "public",
"must_use": "must_use",
"zero": 0,
},
error_message: "An inherent method is now #[must_use]. Downstream crates that did not use its return value will get a compiler lint.",
per_result_error_template: Some("method {{join \"::\" path}}::{{method_name}} in {{span_filename}}:{{span_begin_line}}"),
)
1 change: 1 addition & 0 deletions src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,7 @@ add_lints!(
function_unsafe_added,
inherent_method_const_removed,
inherent_method_missing,
inherent_method_must_use_added,
inherent_method_unsafe_added,
method_parameter_count_changed,
sized_impl_removed,
Expand Down
7 changes: 7 additions & 0 deletions test_crates/inherent_method_must_use_added/new/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
publish = false
name = "inherent_method_must_use_added"
version = "0.1.0"
edition = "2021"

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
pub enum EnumWithMustUseMethods {
Bar,
}

impl EnumWithMustUseMethods {

// These methods did not have the #[must_use] attribute in the old version.
// Addition of the attribute should be reported.

#[must_use]
pub fn MethodToMustUseMethod(&self) {}

#[must_use = "Foo"]
pub fn MethodToMustUseMessageMethod(&self) {}


// These methods had the #[must_use] attribute in the old version. Changes of
// the attribute, including deletion, should not be reported.

pub fn MustUseMethodToMethod(&self) {}

#[must_use = "Foo"]
pub fn MustUseMethodToMustUseMessageMethod(&self) {}


// These methods had the #[must_use] attribute in the old version.
// They also included the user-defined warning message. Changes of
// the attribute, including deletion, should not be reported.

pub fn MustUseMessageMethodToMethod(&self) {}

#[must_use]
pub fn MustUseMessageMethodToMustUseMethod(&self) {}

#[must_use = "Baz"]
pub fn MustUseMessageMethodToMustUseMessageMethod(&self) {}
}


// This public enum's inherent method did not have the #[must_use] attribute
// in the old version. Because the method is private, adding the attribute
// should NOT be reported.

pub enum EnumWithPrivateMustUseMethods {
Bar,
}

impl EnumWithPrivateMustUseMethods {

#[must_use]
fn PrivateMethodToPrivateMustUseMethod(&self) {}
}


// This enum is private and adding #[must_use] to its inherent method
// should NOT be reported.

enum PrivateEnumWithMustUseMethods {
Bar,
}

impl PrivateEnumWithMustUseMethods {

#[must_use]
fn PrivateEnumMethodToPrivateEnumMustUseMethod(&self) {}
}


// This enum and its inherent method were added in the new version of the
// crate, together with the method's attribute.
// It should NOT be reported by this rule to avoid duplicate lints.
// It should be reported as a new pub type that is part of the crate's API.

pub enum NewEnumWithMustUseMethods {
Bar,
}

impl NewEnumWithMustUseMethods {

#[must_use]
pub fn NewEnumMustUseMethod(&self) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
// This test file pair contains test cases against items changing the type
// between the old and new versions while their names and inherent methods
// remain unchanged.
// This can result in false-positives by reporting an addition of #[must_use]
// to an inherent method while the ImplOwner of the method has changed, making
// the new version method no more related to the old version method.

pub struct EnumToStructWithMustUseMethods {}

impl EnumToStructWithMustUseMethods {

#[must_use]
pub fn MethodToMustUseMethod(&self) {}

#[must_use = "Foo"]
pub fn MethodToMustUseMessageMethod(&self) {}

pub fn MustUseMethodToMethod(&self) {}

#[must_use = "Foo"]
pub fn MustUseMethodToMustUseMessageMethod(&self) {}

pub fn MustUseMessageMethodToMethod(&self) {}

#[must_use]
pub fn MustUseMessageMethodToMustUseMethod(&self) {}

#[must_use = "Baz"]
pub fn MustUseMessageMethodToMustUseMessageMethod(&self) {}
}


pub union EnumToUnionWithMustUseMethods {
bar: usize,
}

impl EnumToUnionWithMustUseMethods {

#[must_use]
pub fn MethodToMustUseMethod(&self) {}

#[must_use = "Foo"]
pub fn MethodToMustUseMessageMethod(&self) {}

pub fn MustUseMethodToMethod(&self) {}

#[must_use = "Foo"]
pub fn MustUseMethodToMustUseMessageMethod(&self) {}

pub fn MustUseMessageMethodToMethod(&self) {}

#[must_use]
pub fn MustUseMessageMethodToMustUseMethod(&self) {}

#[must_use = "Baz"]
pub fn MustUseMessageMethodToMustUseMessageMethod(&self) {}
}


pub enum StructToEnumWithMustUseMethods {
Bar,
}

impl StructToEnumWithMustUseMethods {

#[must_use]
pub fn MethodToMustUseMethod(&self) {}

#[must_use = "Foo"]
pub fn MethodToMustUseMessageMethod(&self) {}

pub fn MustUseMethodToMethod(&self) {}

#[must_use = "Foo"]
pub fn MustUseMethodToMustUseMessageMethod(&self) {}

pub fn MustUseMessageMethodToMethod(&self) {}

#[must_use]
pub fn MustUseMessageMethodToMustUseMethod(&self) {}

#[must_use = "Baz"]
pub fn MustUseMessageMethodToMustUseMessageMethod(&self) {}
}


pub union StructToUnionWithMustUseMethods {
bar: usize,
}

impl StructToUnionWithMustUseMethods {

#[must_use]
pub fn MethodToMustUseMethod(&self) {}

#[must_use = "Foo"]
pub fn MethodToMustUseMessageMethod(&self) {}

pub fn MustUseMethodToMethod(&self) {}

#[must_use = "Foo"]
pub fn MustUseMethodToMustUseMessageMethod(&self) {}

pub fn MustUseMessageMethodToMethod(&self) {}

#[must_use]
pub fn MustUseMessageMethodToMustUseMethod(&self) {}

#[must_use = "Baz"]
pub fn MustUseMessageMethodToMustUseMessageMethod(&self) {}
}


pub enum UnionToEnumWithMustUseMethods {
Bar,
}

impl UnionToEnumWithMustUseMethods {

#[must_use]
pub fn MethodToMustUseMethod(&self) {}

#[must_use = "Foo"]
pub fn MethodToMustUseMessageMethod(&self) {}

pub fn MustUseMethodToMethod(&self) {}

#[must_use = "Foo"]
pub fn MustUseMethodToMustUseMessageMethod(&self) {}

pub fn MustUseMessageMethodToMethod(&self) {}

#[must_use]
pub fn MustUseMessageMethodToMustUseMethod(&self) {}

#[must_use = "Baz"]
pub fn MustUseMessageMethodToMustUseMessageMethod(&self) {}
}


pub struct UnionToStructWithMustUseMethods {}

impl UnionToStructWithMustUseMethods {

#[must_use]
pub fn MethodToMustUseMethod(&self) {}

#[must_use = "Foo"]
pub fn MethodToMustUseMessageMethod(&self) {}

pub fn MustUseMethodToMethod(&self) {}

#[must_use = "Foo"]
pub fn MustUseMethodToMustUseMessageMethod(&self) {}

pub fn MustUseMessageMethodToMethod(&self) {}

#[must_use]
pub fn MustUseMessageMethodToMustUseMethod(&self) {}

#[must_use = "Baz"]
pub fn MustUseMessageMethodToMustUseMessageMethod(&self) {}
}
10 changes: 10 additions & 0 deletions test_crates/inherent_method_must_use_added/new/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// This crate's test cases were separated into smaller files for easier
// management and debugging.

pub mod enum_inherent_method_must_use_added;

pub mod struct_inherent_method_must_use_added;

pub mod union_inherent_method_must_use_added;

pub mod item_type_changed_inherent_method_must_use_added;
Loading