Skip to content

Commit e24bacd

Browse files
feat(linting): customize severity per rule (#430)
1 parent 31e6ddf commit e24bacd

File tree

38 files changed

+435
-98
lines changed

38 files changed

+435
-98
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/pgt_analyse/src/macros.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ macro_rules! declare_lint_rule {
2424
( $( #[doc = $doc:literal] )+ $vis:vis $id:ident {
2525
version: $version:literal,
2626
name: $name:tt,
27+
severity: $severity:expr_2021,
2728
$( $key:ident: $value:expr_2021, )*
2829
} ) => {
2930

@@ -32,6 +33,7 @@ macro_rules! declare_lint_rule {
3233
$vis $id {
3334
version: $version,
3435
name: $name,
36+
severity: $severity,
3537
$( $key: $value, )*
3638
}
3739
);
@@ -53,6 +55,7 @@ macro_rules! declare_rule {
5355
( $( #[doc = $doc:literal] )+ $vis:vis $id:ident {
5456
version: $version:literal,
5557
name: $name:tt,
58+
severity: $severity:expr_2021,
5659
$( $key:ident: $value:expr_2021, )*
5760
} ) => {
5861
$( #[doc = $doc] )*
@@ -61,7 +64,7 @@ macro_rules! declare_rule {
6164
impl $crate::RuleMeta for $id {
6265
type Group = super::Group;
6366
const METADATA: $crate::RuleMetadata =
64-
$crate::RuleMetadata::new($version, $name, concat!( $( $doc, "\n", )* )) $( .$key($value) )*;
67+
$crate::RuleMetadata::new($version, $name, concat!( $( $doc, "\n", )* ), $severity) $( .$key($value) )*;
6568
}
6669
}
6770
}

crates/pgt_analyse/src/rule.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use pgt_console::{MarkupBuf, markup};
33
use pgt_diagnostics::advice::CodeSuggestionAdvice;
44
use pgt_diagnostics::{
55
Advices, Category, Diagnostic, DiagnosticTags, Location, LogCategory, MessageAndDescription,
6-
Visit,
6+
Severity, Visit,
77
};
88
use pgt_text_size::TextRange;
99
use std::cmp::Ordering;
@@ -31,17 +31,25 @@ pub struct RuleMetadata {
3131
pub recommended: bool,
3232
/// The source URL of the rule
3333
pub sources: &'static [RuleSource],
34+
/// The default severity of the rule
35+
pub severity: Severity,
3436
}
3537

3638
impl RuleMetadata {
37-
pub const fn new(version: &'static str, name: &'static str, docs: &'static str) -> Self {
39+
pub const fn new(
40+
version: &'static str,
41+
name: &'static str,
42+
docs: &'static str,
43+
severity: Severity,
44+
) -> Self {
3845
Self {
3946
deprecated: None,
4047
version,
4148
name,
4249
docs,
4350
sources: &[],
4451
recommended: false,
52+
severity,
4553
}
4654
}
4755

crates/pgt_analyser/CONTRIBUTING.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,11 @@ Let's say we want to create a new **lint** rule called `useMyRuleName`, follow t
5454
1. Run the command
5555

5656
```shell
57-
just new-lintrule safety useMyRuleName
57+
just new-lintrule safety useMyRuleName (<severity>)
5858
```
5959

60+
Where severity is optional but can be "info", "warn", or "error" (default).
61+
6062
The script will generate a bunch of files inside the `pgt_analyser` crate.
6163
Among the other files, you'll find a file called `use_my_new_rule_name.rs` inside the `pgt_analyser/lib/src/lint/safety` folder. You'll implement your rule in this file.
6264

@@ -187,6 +189,7 @@ declare_lint_rule! {
187189
pub(crate) ExampleRule {
188190
version: "next",
189191
name: "myRuleName",
192+
severity: Severity::Error,
190193
recommended: false,
191194
}
192195
}
@@ -206,6 +209,7 @@ declare_lint_rule! {
206209
pub(crate) ExampleRule {
207210
version: "next",
208211
name: "myRuleName",
212+
severity: Severity::Error,
209213
recommended: false,
210214
sources: &[RuleSource::Squawk("ban-drop-column")],
211215
}
@@ -228,6 +232,7 @@ declare_lint_rule! {
228232
pub(crate) ExampleRule {
229233
version: "next",
230234
name: "myRuleName",
235+
severity: Severity::Error,
231236
recommended: false,
232237
}
233238
}
@@ -280,6 +285,7 @@ declare_lint_rule! {
280285
version: "next",
281286
name: "banDropColumn",
282287
recommended: true,
288+
severity: Severity::Error,
283289
sources: &[RuleSource::Squawk("ban-drop-column")],
284290
}
285291
}
@@ -351,6 +357,7 @@ declare_lint_rule! {
351357
version: "next",
352358
name: "banDropColumn",
353359
recommended: true,
360+
severity: Severity::Error,
354361
deprecated: true,
355362
sources: &[RuleSource::Squawk("ban-drop-column")],
356363
}

crates/pgt_analyser/Cargo.toml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ repository.workspace = true
1212
version = "0.0.0"
1313

1414
[dependencies]
15-
pgt_analyse = { workspace = true }
16-
pgt_console = { workspace = true }
17-
pgt_query_ext = { workspace = true }
18-
serde = { workspace = true }
15+
pgt_analyse = { workspace = true }
16+
pgt_console = { workspace = true }
17+
pgt_diagnostics = { workspace = true }
18+
pgt_query_ext = { workspace = true }
19+
serde = { workspace = true }
1920

2021
[dev-dependencies]
2122
insta = { version = "1.42.1" }

crates/pgt_analyser/src/lint/safety.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
use pgt_analyse::declare_lint_group;
44
pub mod adding_required_field;
55
pub mod ban_drop_column;
6+
pub mod ban_drop_database;
67
pub mod ban_drop_not_null;
78
pub mod ban_drop_table;
8-
declare_lint_group! { pub Safety { name : "safety" , rules : [self :: adding_required_field :: AddingRequiredField , self :: ban_drop_column :: BanDropColumn , self :: ban_drop_not_null :: BanDropNotNull , self :: ban_drop_table :: BanDropTable ,] } }
9+
pub mod ban_truncate_cascade;
10+
declare_lint_group! { pub Safety { name : "safety" , rules : [self :: adding_required_field :: AddingRequiredField , self :: ban_drop_column :: BanDropColumn , self :: ban_drop_database :: BanDropDatabase , self :: ban_drop_not_null :: BanDropNotNull , self :: ban_drop_table :: BanDropTable , self :: ban_truncate_cascade :: BanTruncateCascade ,] } }

crates/pgt_analyser/src/lint/safety/adding_required_field.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use pgt_analyse::{Rule, RuleDiagnostic, RuleSource, context::RuleContext, declare_lint_rule};
22
use pgt_console::markup;
3+
use pgt_diagnostics::Severity;
34

45
declare_lint_rule! {
56
/// Adding a new column that is NOT NULL and has no default value to an existing table effectively makes it required.
@@ -17,6 +18,7 @@ declare_lint_rule! {
1718
pub AddingRequiredField {
1819
version: "next",
1920
name: "addingRequiredField",
21+
severity: Severity::Error,
2022
recommended: false,
2123
sources: &[RuleSource::Squawk("adding-required-field")],
2224
}

crates/pgt_analyser/src/lint/safety/ban_drop_column.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use pgt_analyse::{Rule, RuleDiagnostic, RuleSource, context::RuleContext, declare_lint_rule};
22
use pgt_console::markup;
3+
use pgt_diagnostics::Severity;
34

45
declare_lint_rule! {
56
/// Dropping a column may break existing clients.
@@ -19,6 +20,7 @@ declare_lint_rule! {
1920
pub BanDropColumn {
2021
version: "next",
2122
name: "banDropColumn",
23+
severity: Severity::Warning,
2224
recommended: true,
2325
sources: &[RuleSource::Squawk("ban-drop-column")],
2426
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
use pgt_analyse::{Rule, RuleDiagnostic, RuleSource, context::RuleContext, declare_lint_rule};
2+
use pgt_console::markup;
3+
use pgt_diagnostics::Severity;
4+
5+
declare_lint_rule! {
6+
/// Dropping a database may break existing clients (and everything else, really).
7+
///
8+
/// Make sure that you really want to drop it.
9+
pub BanDropDatabase {
10+
version: "next",
11+
name: "banDropDatabase",
12+
severity: Severity::Warning,
13+
recommended: false,
14+
sources: &[RuleSource::Squawk("ban-drop-database")],
15+
}
16+
}
17+
18+
impl Rule for BanDropDatabase {
19+
type Options = ();
20+
21+
fn run(ctx: &RuleContext<Self>) -> Vec<RuleDiagnostic> {
22+
let mut diagnostics = vec![];
23+
24+
if let pgt_query_ext::NodeEnum::DropdbStmt(_) = &ctx.stmt() {
25+
diagnostics.push(
26+
RuleDiagnostic::new(
27+
rule_category!(),
28+
None,
29+
markup! {
30+
"Dropping a database may break existing clients."
31+
},
32+
)
33+
.detail(None, "You probably don't want to drop your database."),
34+
);
35+
}
36+
37+
diagnostics
38+
}
39+
}

crates/pgt_analyser/src/lint/safety/ban_drop_not_null.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use pgt_analyse::{Rule, RuleDiagnostic, RuleSource, context::RuleContext, declare_lint_rule};
22
use pgt_console::markup;
3+
use pgt_diagnostics::Severity;
34

45
declare_lint_rule! {
56
/// Dropping a NOT NULL constraint may break existing clients.
@@ -18,6 +19,7 @@ declare_lint_rule! {
1819
pub BanDropNotNull {
1920
version: "next",
2021
name: "banDropNotNull",
22+
severity: Severity::Warning,
2123
recommended: true,
2224
sources: &[RuleSource::Squawk("ban-drop-not-null")],
2325

crates/pgt_analyser/src/lint/safety/ban_drop_table.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use pgt_analyse::{Rule, RuleDiagnostic, RuleSource, context::RuleContext, declare_lint_rule};
22
use pgt_console::markup;
3+
use pgt_diagnostics::Severity;
34

45
declare_lint_rule! {
56
/// Dropping a table may break existing clients.
@@ -18,6 +19,7 @@ declare_lint_rule! {
1819
pub BanDropTable {
1920
version: "next",
2021
name: "banDropTable",
22+
severity: Severity::Warning,
2123
recommended: true,
2224
sources: &[RuleSource::Squawk("ban-drop-table")],
2325
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
use pgt_analyse::{Rule, RuleDiagnostic, RuleSource, context::RuleContext, declare_lint_rule};
2+
use pgt_console::markup;
3+
use pgt_diagnostics::Severity;
4+
use pgt_query_ext::protobuf::DropBehavior;
5+
6+
declare_lint_rule! {
7+
/// Using `TRUNCATE`'s `CASCADE` option will truncate any tables that are also foreign-keyed to the specified tables.
8+
///
9+
/// So if you had tables with foreign-keys like:
10+
///
11+
/// `a <- b <- c`
12+
///
13+
/// and ran:
14+
///
15+
/// `truncate a cascade;`
16+
///
17+
/// You'd end up with a, b, & c all being truncated!
18+
///
19+
/// Instead, you can manually specify the tables you want.
20+
///
21+
/// `truncate a, b;`
22+
pub BanTruncateCascade {
23+
version: "next",
24+
name: "banTruncateCascade",
25+
severity: Severity::Error,
26+
recommended: false,
27+
sources: &[RuleSource::Squawk("ban-truncate-cascade")],
28+
}
29+
}
30+
31+
impl Rule for BanTruncateCascade {
32+
type Options = ();
33+
34+
fn run(ctx: &RuleContext<Self>) -> Vec<RuleDiagnostic> {
35+
let mut diagnostics = Vec::new();
36+
37+
if let pgt_query_ext::NodeEnum::TruncateStmt(stmt) = &ctx.stmt() {
38+
if stmt.behavior() == DropBehavior::DropCascade {
39+
diagnostics.push(RuleDiagnostic::new(
40+
rule_category!(),
41+
None,
42+
markup! {
43+
"The `CASCADE` option will also truncate any tables that are foreign-keyed to the specified tables."
44+
},
45+
).detail(None, "Do not use the `CASCADE` option. Instead, specify manually what you want: `TRUNCATE a, b;`."));
46+
}
47+
}
48+
49+
diagnostics
50+
}
51+
}

crates/pgt_analyser/src/options.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ pub type AddingRequiredField =
55
<lint::safety::adding_required_field::AddingRequiredField as pgt_analyse::Rule>::Options;
66
pub type BanDropColumn =
77
<lint::safety::ban_drop_column::BanDropColumn as pgt_analyse::Rule>::Options;
8+
pub type BanDropDatabase =
9+
<lint::safety::ban_drop_database::BanDropDatabase as pgt_analyse::Rule>::Options;
810
pub type BanDropNotNull =
911
<lint::safety::ban_drop_not_null::BanDropNotNull as pgt_analyse::Rule>::Options;
1012
pub type BanDropTable = <lint::safety::ban_drop_table::BanDropTable as pgt_analyse::Rule>::Options;
13+
pub type BanTruncateCascade =
14+
<lint::safety::ban_truncate_cascade::BanTruncateCascade as pgt_analyse::Rule>::Options;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- expect_only_lint/safety/banDropDatabase
2+
drop database all_users;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
source: crates/pgt_analyser/tests/rules_tests.rs
3+
expression: snapshot
4+
---
5+
# Input
6+
```
7+
-- expect_only_lint/safety/banDropDatabase
8+
drop database all_users;
9+
```
10+
11+
# Diagnostics
12+
lint/safety/banDropDatabase ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
13+
14+
× Dropping a database may break existing clients.
15+
16+
i You probably don't want to drop your database.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- expect_only_lint/safety/banTruncateCascade
2+
truncate a cascade;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
source: crates/pgt_analyser/tests/rules_tests.rs
3+
expression: snapshot
4+
---
5+
# Input
6+
```
7+
-- expect_only_lint/safety/banTruncateCascade
8+
truncate a cascade;
9+
```
10+
11+
# Diagnostics
12+
lint/safety/banTruncateCascade ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
13+
14+
× The `CASCADE` option will also truncate any tables that are foreign-keyed to the specified tables.
15+
16+
i Do not use the `CASCADE` option. Instead, specify manually what you want: `TRUNCATE a, b;`.

0 commit comments

Comments
 (0)