From 3df8f3230136fa64fa12578f7b27e286c7a9cdef Mon Sep 17 00:00:00 2001 From: David Matos Date: Tue, 20 Aug 2024 16:38:54 +0200 Subject: [PATCH 1/2] Implement trait_newly_sealed new lint --- src/lints/trait_newly_sealed.ron | 52 +++++++++++++++++++ src/query.rs | 1 + test_crates/trait_newly_sealed/new/Cargo.toml | 7 +++ test_crates/trait_newly_sealed/new/src/lib.rs | 24 +++++++++ test_crates/trait_newly_sealed/old/Cargo.toml | 7 +++ test_crates/trait_newly_sealed/old/src/lib.rs | 18 +++++++ test_outputs/trait_missing.output.ron | 12 +++++ test_outputs/trait_newly_sealed.output.ron | 25 +++++++++ 8 files changed, 146 insertions(+) create mode 100644 src/lints/trait_newly_sealed.ron create mode 100644 test_crates/trait_newly_sealed/new/Cargo.toml create mode 100644 test_crates/trait_newly_sealed/new/src/lib.rs create mode 100644 test_crates/trait_newly_sealed/old/Cargo.toml create mode 100644 test_crates/trait_newly_sealed/old/src/lib.rs create mode 100644 test_outputs/trait_newly_sealed.output.ron diff --git a/src/lints/trait_newly_sealed.ron b/src/lints/trait_newly_sealed.ron new file mode 100644 index 00000000..5887a745 --- /dev/null +++ b/src/lints/trait_newly_sealed.ron @@ -0,0 +1,52 @@ +SemverQuery( + id: "trait_newly_sealed", + human_readable_name: "pub trait became sealed", + description: "A public trait became sealed, so users of this trait can not implement it anymore", + required_update: Major, + lint_level: Deny, + reference_link: Some("https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed"), + query: r#" + { + CrateDiff { + baseline { + item { + ... on Trait { + visibility_limit @filter(op: "=", value: ["$public"]) @output + sealed @filter(op: "!=", value: ["$true"]) + + importable_path { + path @output @tag + public_api @filter(op: "=", value: ["$true"]) + } + } + } + } + current { + item { + ... on Trait { + visibility_limit @filter(op: "=", value: ["$public"]) + sealed @filter(op: "=", value: ["$true"]) + name @output + + importable_path { + path @filter(op: "=", value: ["%path"]) + public_api @filter(op: "=", value: ["$true"]) + } + + span_: span @optional { + filename @output + begin_line @output + } + } + } + } + } + }"#, + arguments: { + "public": "public", + "true": true, + }, + error_message: "A publicly-visible trait became sealed, so it is no longer allowed to be implemented", + per_result_error_template: Some("trait {{join \"::\" path}} in file {{span_filename}}:{{span_begin_line}}"), + +) diff --git a/src/query.rs b/src/query.rs index 4574b605..c66dcefe 100644 --- a/src/query.rs +++ b/src/query.rs @@ -841,6 +841,7 @@ add_lints!( trait_method_unsafe_removed, trait_missing, trait_must_use_added, + trait_newly_sealed, trait_no_longer_object_safe, trait_now_doc_hidden, trait_removed_associated_constant, diff --git a/test_crates/trait_newly_sealed/new/Cargo.toml b/test_crates/trait_newly_sealed/new/Cargo.toml new file mode 100644 index 00000000..0f1bab6a --- /dev/null +++ b/test_crates/trait_newly_sealed/new/Cargo.toml @@ -0,0 +1,7 @@ +[package] +publish = false +name = "trait_newly_sealed" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/test_crates/trait_newly_sealed/new/src/lib.rs b/test_crates/trait_newly_sealed/new/src/lib.rs new file mode 100644 index 00000000..852d7039 --- /dev/null +++ b/test_crates/trait_newly_sealed/new/src/lib.rs @@ -0,0 +1,24 @@ +mod private { + pub trait Sealed {} + pub struct Token; +} + +// Public trait becomes sealed, should be reported +pub trait TraitBecomesSealed: private::Sealed {} + +// Trait that was not publicly-visible becomes sealed, shouldnt get reported as its still private +trait TraitRemainsPrivateButSealed: private::Sealed {} + +// Method on public trait becomes sealed, should be reported +pub trait TraitMethodBecomesSealed { + fn method(&self, _: private::Token); +} + +// Trait that was not publicly-visible becomes public and sealed, shouldnt be +// reported +pub trait TraitBecomesPublicAndSealed: private::Sealed {} + +// Trait becomes private and sealed. The fact that it's private dominates, +// and should be the only violation that's reported: +// Thus being newly sealed is not the main problem +trait TraitBecomesPrivateAndSealed: private::Sealed {} diff --git a/test_crates/trait_newly_sealed/old/Cargo.toml b/test_crates/trait_newly_sealed/old/Cargo.toml new file mode 100644 index 00000000..0f1bab6a --- /dev/null +++ b/test_crates/trait_newly_sealed/old/Cargo.toml @@ -0,0 +1,7 @@ +[package] +publish = false +name = "trait_newly_sealed" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/test_crates/trait_newly_sealed/old/src/lib.rs b/test_crates/trait_newly_sealed/old/src/lib.rs new file mode 100644 index 00000000..4c574b15 --- /dev/null +++ b/test_crates/trait_newly_sealed/old/src/lib.rs @@ -0,0 +1,18 @@ +// Public trait becomes sealed, should be reported +pub trait TraitBecomesSealed {} + +// Trait that was not publicly-visible becomes sealed, shouldnt get reported as its still private +trait TraitRemainsPrivateButSealed {} + +// Method on public trait becomes sealed, should be reported +pub trait TraitMethodBecomesSealed { + fn method(&self); +} +// Trait that was not publicly-visible becomes public and sealed, shouldnt be +// reported +trait TraitBecomesPublicAndSealed {} + +// Trait becomes private and sealed. The fact that it's private dominates, +// and should be the only violation that's reported: +// Thus being newly sealed is not the main problem +pub trait TraitBecomesPrivateAndSealed {} diff --git a/test_outputs/trait_missing.output.ron b/test_outputs/trait_missing.output.ron index db550e7d..e79fc2be 100644 --- a/test_outputs/trait_missing.output.ron +++ b/test_outputs/trait_missing.output.ron @@ -181,4 +181,16 @@ "visibility_limit": String("public"), }, ], + "./test_crates/trait_newly_sealed/": [ + { + "name": String("TraitBecomesPrivateAndSealed"), + "path": List([ + String("trait_newly_sealed"), + String("TraitBecomesPrivateAndSealed"), + ]), + "span_begin_line": Uint64(18), + "span_filename": String("src/lib.rs"), + "visibility_limit": String("public"), + }, + ], } diff --git a/test_outputs/trait_newly_sealed.output.ron b/test_outputs/trait_newly_sealed.output.ron new file mode 100644 index 00000000..cb4cea31 --- /dev/null +++ b/test_outputs/trait_newly_sealed.output.ron @@ -0,0 +1,25 @@ +{ + "./test_crates/trait_newly_sealed/": [ + { + "name": String("TraitBecomesSealed"), + "path": List([ + String("trait_newly_sealed"), + String("TraitBecomesSealed"), + ]), + "span_begin_line": Uint64(7), + "span_filename": String("src/lib.rs"), + "visibility_limit": String("public"), + }, + { + "name": String("TraitMethodBecomesSealed"), + "path": List([ + String("trait_newly_sealed"), + String("TraitMethodBecomesSealed"), + ]), + "span_begin_line": Uint64(13), + "span_filename": String("src/lib.rs"), + "visibility_limit": String("public"), + }, + ], +} + From eda31c82923893820525613d121678b54b7268a6 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski <2348618+obi1kenobi@users.noreply.github.com> Date: Tue, 20 Aug 2024 18:45:10 -0400 Subject: [PATCH 2/2] Update src/lints/trait_newly_sealed.ron --- src/lints/trait_newly_sealed.ron | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lints/trait_newly_sealed.ron b/src/lints/trait_newly_sealed.ron index 5887a745..0c1ca177 100644 --- a/src/lints/trait_newly_sealed.ron +++ b/src/lints/trait_newly_sealed.ron @@ -46,7 +46,7 @@ SemverQuery( "public": "public", "true": true, }, - error_message: "A publicly-visible trait became sealed, so it is no longer allowed to be implemented", + error_message: "A publicly-visible trait became sealed, so downstream crates are no longer able to implement it", per_result_error_template: Some("trait {{join \"::\" path}} in file {{span_filename}}:{{span_begin_line}}"), )