diff --git a/crates/squawk/src/main.rs b/crates/squawk/src/main.rs index 2722cb8c..06ee258d 100644 --- a/crates/squawk/src/main.rs +++ b/crates/squawk/src/main.rs @@ -217,7 +217,7 @@ Please open an issue at https://github.com/sbdchd/squawk/issues/new with the log let mut clap_app = Opt::clap(); let is_stdin = !atty::is(Stream::Stdin); let github_annotations = std::env::var("GITHUB_ACTIONS").is_ok() - && !std::env::var("SQUAWK_DISABLE_GITHUB_ANNOTATIONS").is_ok(); + && std::env::var("SQUAWK_DISABLE_GITHUB_ANNOTATIONS").is_err(); match opts.cmd { Some(Command::Server) => { squawk_server::run().context("language server failed")?; diff --git a/crates/squawk_linter/src/ignore.rs b/crates/squawk_linter/src/ignore.rs index 549fe3e1..d4c05b03 100644 --- a/crates/squawk_linter/src/ignore.rs +++ b/crates/squawk_linter/src/ignore.rs @@ -284,7 +284,7 @@ alter table t drop column c cascade; let errors: Vec<_> = linter .lint(parse, sql) .into_iter() - .map(|x| x.code.clone()) + .map(|x| x.code) .collect(); assert!(errors.is_empty()); } @@ -355,7 +355,7 @@ alter table t2 drop column c2 cascade; let errors: Vec<_> = linter .lint(parse, sql) .into_iter() - .map(|x| x.code.clone()) + .map(|x| x.code) .collect(); assert_debug_snapshot!(errors, @r" @@ -379,7 +379,7 @@ alter table t2 drop column c2 cascade; let errors: Vec<_> = linter .lint(parse, sql) .into_iter() - .map(|x| x.code.clone()) + .map(|x| x.code) .collect(); assert_debug_snapshot!(errors, @r" diff --git a/crates/squawk_linter/src/lib.rs b/crates/squawk_linter/src/lib.rs index afbdf08a..558f8060 100644 --- a/crates/squawk_linter/src/lib.rs +++ b/crates/squawk_linter/src/lib.rs @@ -8,6 +8,7 @@ use ignore::find_ignores; use ignore_index::IgnoreIndex; use lazy_static::lazy_static; use rowan::TextRange; +use rowan::TextSize; use serde::{Deserialize, Serialize}; use squawk_syntax::{Parse, SourceFile}; @@ -222,6 +223,36 @@ pub struct Violation { pub message: String, pub text_range: TextRange, pub help: Option, + pub fix: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Fix { + pub title: String, + pub edits: Vec, +} + +impl Fix { + fn new>(title: T, edits: Vec) -> Fix { + Fix { + title: title.into(), + edits, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Edit { + pub text_range: TextRange, + pub text: Option, +} +impl Edit { + fn insert>(text: T, at: TextSize) -> Self { + Self { + text_range: TextRange::new(at, at), + text: Some(text.into()), + } + } } impl Violation { @@ -237,8 +268,14 @@ impl Violation { text_range, message, help: help.into(), + fix: None, } } + + fn with_fix(mut self, fix: Option) -> Violation { + self.fix = fix; + self + } } pub struct LinterSettings { diff --git a/crates/squawk_linter/src/rules/adding_field_with_default.rs b/crates/squawk_linter/src/rules/adding_field_with_default.rs index 98c7a6f0..01f676a6 100644 --- a/crates/squawk_linter/src/rules/adding_field_with_default.rs +++ b/crates/squawk_linter/src/rules/adding_field_with_default.rs @@ -22,7 +22,7 @@ lazy_static! { .split('\n') .map(|x| x.trim()) .filter(|x| !x.is_empty()) - .map(|x| Identifier::new(x)) + .map(Identifier::new) .collect() }; } diff --git a/crates/squawk_linter/src/rules/constraint_missing_not_valid.rs b/crates/squawk_linter/src/rules/constraint_missing_not_valid.rs index 5d824f60..12411b87 100644 --- a/crates/squawk_linter/src/rules/constraint_missing_not_valid.rs +++ b/crates/squawk_linter/src/rules/constraint_missing_not_valid.rs @@ -5,10 +5,7 @@ use squawk_syntax::{ ast::{self, AstNode}, }; -use crate::{ - Linter, Rule, Violation, - identifier::Identifier, -}; +use crate::{Linter, Rule, Violation, identifier::Identifier}; pub fn tables_created_in_transaction( assume_in_transaction: bool, @@ -73,7 +70,8 @@ fn not_valid_validate_in_transaction( if add_constraint.not_valid().is_some() { if let Some(constraint) = add_constraint.constraint() { if let Some(constraint_name) = constraint.name() { - not_valid_names.insert(Identifier::new(&constraint_name.text())); + not_valid_names + .insert(Identifier::new(&constraint_name.text())); } } } diff --git a/crates/squawk_linter/src/rules/prefer_robust_stmts.rs b/crates/squawk_linter/src/rules/prefer_robust_stmts.rs index 3b427408..dccc8ac5 100644 --- a/crates/squawk_linter/src/rules/prefer_robust_stmts.rs +++ b/crates/squawk_linter/src/rules/prefer_robust_stmts.rs @@ -5,10 +5,7 @@ use squawk_syntax::{ ast::{self, AstNode}, }; -use crate::{ - Linter, Rule, Violation, - identifier::Identifier, -}; +use crate::{Edit, Fix, Linter, Rule, Violation, identifier::Identifier}; #[derive(PartialEq)] enum Constraint { @@ -49,7 +46,7 @@ pub(crate) fn prefer_robust_stmts(ctx: &mut Linter, parse: &Parse) { } ast::Stmt::AlterTable(alter_table) => { for action in alter_table.actions() { - let message_type = match &action { + let (message_type, fix) = match &action { ast::AlterTableAction::DropConstraint(drop_constraint) => { if let Some(constraint_name) = drop_constraint.name_ref() { constraint_names.insert( @@ -60,13 +57,32 @@ pub(crate) fn prefer_robust_stmts(ctx: &mut Linter, parse: &Parse) { if drop_constraint.if_exists().is_some() { continue; } - ActionErrorMessage::IfExists + + let fix = if let Some(constraint_token) = + drop_constraint.constraint_token() + { + let at = constraint_token.text_range().end(); + let edit = Edit::insert(" if exists", at); + Some(Fix::new("Insert `if exists`", vec![edit])) + } else { + None + }; + + (ActionErrorMessage::IfExists, fix) } ast::AlterTableAction::AddColumn(add_column) => { if add_column.if_not_exists().is_some() { continue; } - ActionErrorMessage::IfNotExists + + let fix = if let Some(column_token) = add_column.column_token() { + let at = column_token.text_range().end(); + let edit = Edit::insert(" if not exists", at); + Some(Fix::new("Insert `if not exists`", vec![edit])) + } else { + None + }; + (ActionErrorMessage::IfNotExists, fix) } ast::AlterTableAction::ValidateConstraint(validate_constraint) => { if let Some(constraint_name) = validate_constraint.name_ref() { @@ -76,7 +92,7 @@ pub(crate) fn prefer_robust_stmts(ctx: &mut Linter, parse: &Parse) { continue; } } - ActionErrorMessage::None + (ActionErrorMessage::None, None) } ast::AlterTableAction::AddConstraint(add_constraint) => { let constraint = add_constraint.constraint(); @@ -90,15 +106,23 @@ pub(crate) fn prefer_robust_stmts(ctx: &mut Linter, parse: &Parse) { } } } - ActionErrorMessage::None + (ActionErrorMessage::None, None) } ast::AlterTableAction::DropColumn(drop_column) => { if drop_column.if_exists().is_some() { continue; } - ActionErrorMessage::IfExists + + let fix = if let Some(column_token) = drop_column.column_token() { + let at = column_token.text_range().end(); + let edit = Edit::insert(" if exists", at); + Some(Fix::new("Insert `if exists`", vec![edit])) + } else { + None + }; + (ActionErrorMessage::IfExists, fix) } - _ => ActionErrorMessage::None, + _ => (ActionErrorMessage::None, None), }; if inside_transaction { @@ -117,64 +141,106 @@ pub(crate) fn prefer_robust_stmts(ctx: &mut Linter, parse: &Parse) { }, }; - ctx.report(Violation::new( - Rule::PreferRobustStmts, - message, - action.syntax().text_range(), - None, - )); + ctx.report( + Violation::new( + Rule::PreferRobustStmts, + message, + action.syntax().text_range(), + None, + ) + .with_fix(fix), + ); } } ast::Stmt::CreateIndex(create_index) if create_index.if_not_exists().is_none() + && create_index.name().is_some() && (create_index.concurrently_token().is_some() || !inside_transaction) => { + let fix = if let Some(name) = create_index.name() { + let at = name.syntax().text_range().start(); + let edit = Edit::insert("if not exists ", at); + Some(Fix::new("Insert `if not exists`", vec![edit])) + } else { + None + }; ctx.report(Violation::new( Rule::PreferRobustStmts, "Missing `IF NOT EXISTS`, the migration can't be rerun if it fails part way through.".into(), create_index.syntax().text_range(), "Use an explicit name for a concurrently created index".to_string(), - )); + ).with_fix(fix)); } ast::Stmt::CreateTable(create_table) if create_table.if_not_exists().is_none() && !inside_transaction => { + let fix = if let Some(table_token) = create_table.table_token() { + let at = table_token.text_range().end(); + let edit = Edit::insert(" if not exists", at); + Some(Fix::new("Insert `if not exists`", vec![edit])) + } else { + None + }; + ctx.report(Violation::new( Rule::PreferRobustStmts, "Missing `IF NOT EXISTS`, the migration can't be rerun if it fails part way through.".into(), create_table.syntax().text_range(), None, - )); + ).with_fix(fix)); } ast::Stmt::DropIndex(drop_index) if drop_index.if_exists().is_none() && !inside_transaction => { + let fix = if let Some(first_index) = drop_index.paths().next() { + let at = first_index.syntax().text_range().start(); + let edit = Edit::insert("if exists ", at); + Some(Fix::new("Insert `if exists`", vec![edit])) + } else { + None + }; + ctx.report(Violation::new( Rule::PreferRobustStmts, - "Missing `IF NOT EXISTS`, the migration can't be rerun if it fails part way through.".into(), + "Missing `IF EXISTS`, the migration can't be rerun if it fails part way through.".into(), drop_index.syntax().text_range(), None, - )); + ).with_fix(fix)); } ast::Stmt::DropTable(drop_table) if drop_table.if_exists().is_none() && !inside_transaction => { + let fix = if let Some(table_token) = drop_table.table_token() { + let at = table_token.text_range().end(); + let edit = Edit::insert(" if exists", at); + Some(Fix::new("Insert `if exists`", vec![edit])) + } else { + None + }; ctx.report(Violation::new( Rule::PreferRobustStmts, - "Missing `IF NOT EXISTS`, the migration can't be rerun if it fails part way through.".into(), + "Missing `IF EXISTS`, the migration can't be rerun if it fails part way through.".into(), drop_table.syntax().text_range(), None, - )); + ).with_fix(fix)); } ast::Stmt::DropType(drop_type) if drop_type.if_exists().is_none() && !inside_transaction => { + let fix = if let Some(type_token) = drop_type.type_token() { + let at = type_token.text_range().end(); + let edit = Edit::insert(" if exists", at); + Some(Fix::new("Insert `if exists`", vec![edit])) + } else { + None + }; + ctx.report(Violation::new( Rule::PreferRobustStmts, - "Missing `IF NOT EXISTS`, the migration can't be rerun if it fails part way through.".into(), + "Missing `IF EXISTS`, the migration can't be rerun if it fails part way through.".into(), drop_type.syntax().text_range(), None, - )); + ).with_fix(fix)); } _ => (), } @@ -183,9 +249,136 @@ pub(crate) fn prefer_robust_stmts(ctx: &mut Linter, parse: &Parse) { #[cfg(test)] mod test { - use insta::assert_debug_snapshot; + use insta::{assert_debug_snapshot, assert_snapshot}; + + use crate::{Edit, Linter, Rule}; + + fn fix(sql: &str) -> String { + let file = squawk_syntax::SourceFile::parse(sql); + assert_eq!(file.errors().len(), 0, "Shouldn't start with syntax errors"); + let mut linter = Linter::from([Rule::PreferRobustStmts]); + let errors = linter.lint(file, sql); + assert!(errors.len() > 0, "Should start with linter errors"); + + let fixes = errors.into_iter().flat_map(|x| x.fix).collect::>(); + + let mut result = sql.to_string(); + + let mut all_edits: Vec<&Edit> = fixes.iter().flat_map(|fix| &fix.edits).collect(); + + all_edits.sort_by(|a, b| b.text_range.start().cmp(&a.text_range.start())); + + for edit in all_edits { + let start: usize = edit.text_range.start().into(); + let end: usize = edit.text_range.end().into(); + let text = edit.text.as_ref().map_or("", |v| v); + result.replace_range(start..end, text); + } + + let file = squawk_syntax::SourceFile::parse(&result); + assert_eq!( + file.errors().len(), + 0, + "Shouldn't introduce any syntax errors" + ); + let mut linter = Linter::from([Rule::PreferRobustStmts]); + let errors = linter.lint(file, &result); + assert_eq!( + errors.len(), + 0, + "Fixes should remove all the linter errors." + ); + + result + } + + #[test] + fn fix_drop_type_if_exists() { + assert_snapshot!(fix(" +drop type t; +DROP TYPE f; +"), @r" + drop type if exists t; + DROP TYPE if exists f; + "); + } + + #[test] + fn fix_drop_index_if_exists() { + assert_snapshot!(fix(" +drop index i; +DROP INDEX CONCURRENTLY idx; +"), @r" + drop index if exists i; + DROP INDEX CONCURRENTLY if exists idx; + "); + } + + #[test] + fn fix_drop_table_if_exists() { + assert_snapshot!(fix(" +drop table t; +DROP TABLE users; +"), @r" + drop table if exists t; + DROP TABLE if exists users; + "); + } - use crate::{Linter, Rule}; + #[test] + fn fix_create_index_if_not_exists() { + assert_snapshot!(fix(" +create index idx on table (col); +CREATE INDEX CONCURRENTLY idx2 ON users (email); +"), @r" + create index if not exists idx on table (col); + CREATE INDEX CONCURRENTLY if not exists idx2 ON users (email); + "); + } + + #[test] + fn fix_create_table_if_not_exists() { + assert_snapshot!(fix(" +create table t (id int); +CREATE TABLE users (id serial, name text); +"), @r" + create table if not exists t (id int); + CREATE TABLE if not exists users (id serial, name text); + "); + } + + #[test] + fn fix_alter_table_add_column_if_not_exists() { + assert_snapshot!(fix(" +alter table t add column c text; +ALTER TABLE users ADD COLUMN email text; +"), @r" + alter table t add column if not exists c text; + ALTER TABLE users ADD COLUMN if not exists email text; + "); + } + + #[test] + fn fix_alter_table_drop_column_if_exists() { + assert_snapshot!(fix(" +alter table t drop column c; +ALTER TABLE users DROP COLUMN email; +"), @r" + alter table t drop column if exists c; + ALTER TABLE users DROP COLUMN if exists email; + "); + } + + #[test] + fn fix_alter_table_drop_constraint_if_exists() { + assert_snapshot!(fix(" +alter table t drop constraint c; +ALTER TABLE users DROP CONSTRAINT pk_users; +"), @r" + alter table t drop constraint if exists c; + ALTER TABLE users DROP CONSTRAINT if exists pk_users; + "); + } #[test] fn drop_before_end_ok() { @@ -403,7 +596,7 @@ CREATE INDEX CONCURRENTLY ON "table_name" ("field_name"); } #[test] - fn create_index_concurrently_muli_stmts_err() { + fn create_index_concurrently_without_name_ok() { let sql = r#" CREATE INDEX CONCURRENTLY ON "table_name" ("field_name"); CREATE INDEX CONCURRENTLY ON "table_name" ("field_name"); @@ -412,8 +605,7 @@ CREATE INDEX CONCURRENTLY ON "table_name" ("field_name"); let mut linter = Linter::from([Rule::PreferRobustStmts]); linter.settings.assume_in_transaction = true; let errors = linter.lint(file, sql); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_eq!(errors.len(), 0); } #[test] @@ -516,7 +708,7 @@ ALTER TABLE "core_foo" DROP CONSTRAINT "core_foo_idx"; } #[test] - fn create_index_concurrently_unnamed_err() { + fn create_index_concurrently_unnamed_ok() { let sql = r#" select 1; -- so we don't skip checking CREATE INDEX CONCURRENTLY ON "table_name" ("field_name"); @@ -524,8 +716,7 @@ CREATE INDEX CONCURRENTLY ON "table_name" ("field_name"); let file = squawk_syntax::SourceFile::parse(sql); let mut linter = Linter::from([Rule::PreferRobustStmts]); let errors = linter.lint(file, sql); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_eq!(errors.len(), 0); } #[test] diff --git a/crates/squawk_linter/src/rules/prefer_text_field.rs b/crates/squawk_linter/src/rules/prefer_text_field.rs index 45a0ade6..e4ff5375 100644 --- a/crates/squawk_linter/src/rules/prefer_text_field.rs +++ b/crates/squawk_linter/src/rules/prefer_text_field.rs @@ -35,10 +35,12 @@ fn is_not_allowed_varchar(ty: &ast::Type) -> bool { return false; }; // if we don't have any args, then it's the same as `text` - Identifier::new(ty_name.as_str()) == Identifier::new("varchar") && path_type.arg_list().is_some() + Identifier::new(ty_name.as_str()) == Identifier::new("varchar") + && path_type.arg_list().is_some() } ast::Type::CharType(char_type) => { - Identifier::new(&char_type.text()) == Identifier::new("varchar") && char_type.arg_list().is_some() + Identifier::new(&char_type.text()) == Identifier::new("varchar") + && char_type.arg_list().is_some() } ast::Type::BitType(_) => false, ast::Type::DoubleType(_) => false, diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__add_numbers_ok.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__add_numbers_ok.snap index 6014ae82..50764fb9 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__add_numbers_ok.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__add_numbers_ok.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Add the column as nullable, backfill existing rows, and add a trigger to update the column on write instead.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__arbitrary_func_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__arbitrary_func_err.snap index f271e43a..6b05a2ed 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__arbitrary_func_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__arbitrary_func_err.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Add the column as nullable, backfill existing rows, and add a trigger to update the column on write instead.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_random_with_args_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_random_with_args_err.snap index 829200f7..06442ff2 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_random_with_args_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_random_with_args_err.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Add the column as nullable, backfill existing rows, and add a trigger to update the column on write instead.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_uuid_error.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_uuid_error.snap index 5b2b77cd..9f57f336 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_uuid_error.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_uuid_error.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Add the column as nullable, backfill existing rows, and add a trigger to update the column on write instead.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_uuid_error_multi_stmt.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_uuid_error_multi_stmt.snap index 848a93b3..5196c975 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_uuid_error_multi_stmt.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_uuid_error_multi_stmt.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Add the column as nullable, backfill existing rows, and add a trigger to update the column on write instead.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_volatile_func_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_volatile_func_err.snap index 2ee281e2..16889061 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_volatile_func_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_volatile_func_err.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Add the column as nullable, backfill existing rows, and add a trigger to update the column on write instead.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__generated_stored_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__generated_stored_err.snap index d9242981..ee14c327 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__generated_stored_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__generated_stored_err.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Add the column as nullable, backfill existing rows, and add a trigger to update the column on write instead.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_not_null_field__test__regression_gh_issue_519.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_not_null_field__test__regression_gh_issue_519.snap index 54cbadd9..ed9a3ff1 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_not_null_field__test__regression_gh_issue_519.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_not_null_field__test__regression_gh_issue_519.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Make the field nullable and use a `CHECK` constraint instead.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_not_null_field__test__set_not_null.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_not_null_field__test__set_not_null.snap index 64f3b9ea..51e9855c 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_not_null_field__test__set_not_null.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_not_null_field__test__set_not_null.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Make the field nullable and use a `CHECK` constraint instead.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_primary_key_constraint__test__plain_primary_key.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_primary_key_constraint__test__plain_primary_key.snap index 31900665..8b2a448c 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_primary_key_constraint__test__plain_primary_key.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_primary_key_constraint__test__plain_primary_key.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Add the `PRIMARY KEY` constraint `USING` an index.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_primary_key_constraint__test__serial_primary_key.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_primary_key_constraint__test__serial_primary_key.snap index 5d3a6838..f201d162 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_primary_key_constraint__test__serial_primary_key.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_primary_key_constraint__test__serial_primary_key.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Add the `PRIMARY KEY` constraint `USING` an index.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_required_field__test__not_null_without_default.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_required_field__test__not_null_without_default.snap index 3c78474e..08a38cfd 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_required_field__test__not_null_without_default.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_required_field__test__not_null_without_default.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Make the field nullable or add a non-VOLATILE DEFAULT", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_alter_domain_with_add_constraint__test__err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_alter_domain_with_add_constraint__test__err.snap index c09b190c..368ac139 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_alter_domain_with_add_constraint__test__err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_alter_domain_with_add_constraint__test__err.snap @@ -8,5 +8,6 @@ expression: errors message: "Domains with constraints have poor support for online migrations. Use table and column constraints instead.", text_range: 31..79, help: None, + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__all_the_types.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__all_the_types.snap index f3aa674d..d751a492 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__all_the_types.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__all_the_types.snap @@ -8,35 +8,41 @@ expression: errors message: "Using `character` is likey a mistake and should almost always be replaced by `text` or `varchar`.", text_range: 59..68, help: None, + fix: None, }, Violation { code: BanCharField, message: "Using `character` is likey a mistake and should almost always be replaced by `text` or `varchar`.", text_range: 76..90, help: None, + fix: None, }, Violation { code: BanCharField, message: "Using `character` is likey a mistake and should almost always be replaced by `text` or `varchar`.", text_range: 98..102, help: None, + fix: None, }, Violation { code: BanCharField, message: "Using `character` is likey a mistake and should almost always be replaced by `text` or `varchar`.", text_range: 110..119, help: None, + fix: None, }, Violation { code: BanCharField, message: "Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`.", text_range: 265..280, help: None, + fix: None, }, Violation { code: BanCharField, message: "Using `character` is likey a mistake and should almost always be replaced by `text` or `varchar`.", text_range: 288..292, help: None, + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__alter_table_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__alter_table_err.snap index 5eaf71d8..ec0fe2c8 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__alter_table_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__alter_table_err.snap @@ -8,5 +8,6 @@ expression: errors message: "Using `character` is likey a mistake and should almost always be replaced by `text` or `varchar`.", text_range: 28..32, help: None, + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__array_char_type_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__array_char_type_err.snap index e4ff2781..9b1cfdc7 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__array_char_type_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__array_char_type_err.snap @@ -8,5 +8,6 @@ expression: errors message: "Using `character` is likey a mistake and should almost always be replaced by `text` or `varchar`.", text_range: 22..26, help: None, + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__creating_table_with_char_errors.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__creating_table_with_char_errors.snap index fc5421ee..c070a32f 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__creating_table_with_char_errors.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__creating_table_with_char_errors.snap @@ -8,23 +8,27 @@ expression: errors message: "Using `character` is likey a mistake and should almost always be replaced by `text` or `varchar`.", text_range: 77..86, help: None, + fix: None, }, Violation { code: BanCharField, message: "Using `character` is likey a mistake and should almost always be replaced by `text` or `varchar`.", text_range: 108..122, help: None, + fix: None, }, Violation { code: BanCharField, message: "Using `character` is likey a mistake and should almost always be replaced by `text` or `varchar`.", text_range: 147..151, help: None, + fix: None, }, Violation { code: BanCharField, message: "Using `character` is likey a mistake and should almost always be replaced by `text` or `varchar`.", text_range: 174..183, help: None, + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_concurrent_index_creation_in_transaction__test__assuming_in_transaction_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_concurrent_index_creation_in_transaction__test__assuming_in_transaction_err.snap index a6508a0f..0d42ea57 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_concurrent_index_creation_in_transaction__test__assuming_in_transaction_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_concurrent_index_creation_in_transaction__test__assuming_in_transaction_err.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Build the index outside any transactions.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_concurrent_index_creation_in_transaction__test__ban_concurrent_index_creation_in_transaction_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_concurrent_index_creation_in_transaction__test__ban_concurrent_index_creation_in_transaction_err.snap index e1173894..f16b21b9 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_concurrent_index_creation_in_transaction__test__ban_concurrent_index_creation_in_transaction_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_concurrent_index_creation_in_transaction__test__ban_concurrent_index_creation_in_transaction_err.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Build the index outside any transactions.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_create_domain_with_constraint__test__err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_create_domain_with_constraint__test__err.snap index d67c0c1d..7e3316fe 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_create_domain_with_constraint__test__err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_create_domain_with_constraint__test__err.snap @@ -8,5 +8,6 @@ expression: errors message: "Domains with constraints have poor support for online migrations. Use table and column constraints instead.", text_range: 46..63, help: None, + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_create_domain_with_constraint__test__err_with_multiple_constraints.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_create_domain_with_constraint__test__err_with_multiple_constraints.snap index da9972b9..708d8d91 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_create_domain_with_constraint__test__err_with_multiple_constraints.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_create_domain_with_constraint__test__err_with_multiple_constraints.snap @@ -8,5 +8,6 @@ expression: errors message: "Domains with constraints have poor support for online migrations. Use table and column constraints instead.", text_range: 22..48, help: None, + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_column__test__err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_column__test__err.snap index d9158092..a75a5baf 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_column__test__err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_column__test__err.snap @@ -8,5 +8,6 @@ expression: errors message: "Dropping a column may break existing clients.", text_range: 23..52, help: None, + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_database__test__ban_drop_database.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_database__test__ban_drop_database.snap index 17a8ed09..f6e18540 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_database__test__ban_drop_database.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_database__test__ban_drop_database.snap @@ -8,17 +8,20 @@ expression: errors message: "Dropping a database may break existing clients.", text_range: 9..35, help: None, + fix: None, }, Violation { code: BanDropDatabase, message: "Dropping a database may break existing clients.", text_range: 45..81, help: None, + fix: None, }, Violation { code: BanDropDatabase, message: "Dropping a database may break existing clients.", text_range: 91..127, help: None, + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_not_null__test__err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_not_null__test__err.snap index 860f5451..f050cf78 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_not_null__test__err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_not_null__test__err.snap @@ -8,5 +8,6 @@ expression: errors message: "Dropping a `NOT NULL` constraint may break existing clients.", text_range: 46..59, help: None, + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_table__test__err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_table__test__err.snap index c966fdea..067309b2 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_table__test__err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_table__test__err.snap @@ -8,17 +8,20 @@ expression: errors message: "Dropping a table may break existing clients.", text_range: 1..24, help: None, + fix: None, }, Violation { code: BanDropTable, message: "Dropping a table may break existing clients.", text_range: 26..59, help: None, + fix: None, }, Violation { code: BanDropTable, message: "Dropping a table may break existing clients.", text_range: 61..94, help: None, + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_truncate_cascade__test__err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_truncate_cascade__test__err.snap index 0166dcaf..d4243942 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_truncate_cascade__test__err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_truncate_cascade__test__err.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Remove the `CASCADE` and specify exactly which tables you want to truncate.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__changing_column_type__test__another_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__changing_column_type__test__another_err.snap index 229b1062..b255650e 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__changing_column_type__test__another_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__changing_column_type__test__another_err.snap @@ -8,11 +8,13 @@ expression: errors message: "Changing a column type requires an `ACCESS EXCLUSIVE` lock on the table which blocks reads and writes while the table is rewritten. Changing the type of the column may also break other clients reading from the table.", text_range: 88..131, help: None, + fix: None, }, Violation { code: ChangingColumnType, message: "Changing a column type requires an `ACCESS EXCLUSIVE` lock on the table which blocks reads and writes while the table is rewritten. Changing the type of the column may also break other clients reading from the table.", text_range: 178..205, help: None, + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__changing_column_type__test__err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__changing_column_type__test__err.snap index 0073ae44..b37002a9 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__changing_column_type__test__err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__changing_column_type__test__err.snap @@ -8,5 +8,6 @@ expression: errors message: "Changing a column type requires an `ACCESS EXCLUSIVE` lock on the table which blocks reads and writes while the table is rewritten. Changing the type of the column may also break other clients reading from the table.", text_range: 92..121, help: None, + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__adding_check_constraint_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__adding_check_constraint_err.snap index 0dfffea9..e7047dd3 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__adding_check_constraint_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__adding_check_constraint_err.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Use `NOT VALID` with a later `VALIDATE CONSTRAINT` call.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__adding_fk_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__adding_fk_err.snap index 3b46cd10..af200c62 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__adding_fk_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__adding_fk_err.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Use `NOT VALID` with a later `VALIDATE CONSTRAINT` call.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__not_valid_validate_assume_transaction_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__not_valid_validate_assume_transaction_err.snap index a2c13a2e..64886572 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__not_valid_validate_assume_transaction_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__not_valid_validate_assume_transaction_err.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Add constraint as `NOT VALID` in one transaction and `VALIDATE CONSTRAINT` in a separate transaction.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__not_valid_validate_transaction_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__not_valid_validate_transaction_err.snap index ae177fee..788986de 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__not_valid_validate_transaction_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__not_valid_validate_transaction_err.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Add constraint as `NOT VALID` in one transaction and `VALIDATE CONSTRAINT` in a separate transaction.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__not_valid_validate_with_assume_in_transaction_with_explicit_commit_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__not_valid_validate_with_assume_in_transaction_with_explicit_commit_err.snap index a2c13a2e..64886572 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__not_valid_validate_with_assume_in_transaction_with_explicit_commit_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__not_valid_validate_with_assume_in_transaction_with_explicit_commit_err.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Add constraint as `NOT VALID` in one transaction and `VALIDATE CONSTRAINT` in a separate transaction.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__disallow_unique_constraint__test__adding_unique_constraint_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__disallow_unique_constraint__test__adding_unique_constraint_err.snap index 072c18e4..5f9dca16 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__disallow_unique_constraint__test__adding_unique_constraint_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__disallow_unique_constraint__test__adding_unique_constraint_err.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Create an index `CONCURRENTLY` and create the constraint using the index.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__disallow_unique_constraint__test__unique_constraint_inline_add_column_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__disallow_unique_constraint__test__unique_constraint_inline_add_column_err.snap index 479ba3cf..2ae89e29 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__disallow_unique_constraint__test__unique_constraint_inline_add_column_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__disallow_unique_constraint__test__unique_constraint_inline_add_column_err.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Create an index `CONCURRENTLY` and create the constraint using the index.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__disallow_unique_constraint__test__unique_constraint_inline_add_column_unique_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__disallow_unique_constraint__test__unique_constraint_inline_add_column_unique_err.snap index b0f88051..ab126ca4 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__disallow_unique_constraint__test__unique_constraint_inline_add_column_unique_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__disallow_unique_constraint__test__unique_constraint_inline_add_column_unique_err.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Create an index `CONCURRENTLY` and create the constraint using the index.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_bigint_over_int__test__err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_bigint_over_int__test__err.snap index 69408f63..56acd739 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_bigint_over_int__test__err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_bigint_over_int__test__err.snap @@ -10,6 +10,7 @@ expression: errors help: Some( "Use 64-bit integer values instead to prevent hitting this limit.", ), + fix: None, }, Violation { code: PreferBigintOverInt, @@ -18,6 +19,7 @@ expression: errors help: Some( "Use 64-bit integer values instead to prevent hitting this limit.", ), + fix: None, }, Violation { code: PreferBigintOverInt, @@ -26,6 +28,7 @@ expression: errors help: Some( "Use 64-bit integer values instead to prevent hitting this limit.", ), + fix: None, }, Violation { code: PreferBigintOverInt, @@ -34,5 +37,6 @@ expression: errors help: Some( "Use 64-bit integer values instead to prevent hitting this limit.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_bigint_over_smallint__test__err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_bigint_over_smallint__test__err.snap index 5028a58c..1b758e72 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_bigint_over_smallint__test__err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_bigint_over_smallint__test__err.snap @@ -10,6 +10,7 @@ expression: errors help: Some( "Use 64-bit integer values instead to prevent hitting this limit.", ), + fix: None, }, Violation { code: PreferBigintOverSmallint, @@ -18,6 +19,7 @@ expression: errors help: Some( "Use 64-bit integer values instead to prevent hitting this limit.", ), + fix: None, }, Violation { code: PreferBigintOverSmallint, @@ -26,6 +28,7 @@ expression: errors help: Some( "Use 64-bit integer values instead to prevent hitting this limit.", ), + fix: None, }, Violation { code: PreferBigintOverSmallint, @@ -34,5 +37,6 @@ expression: errors help: Some( "Use 64-bit integer values instead to prevent hitting this limit.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_identity__test__err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_identity__test__err.snap index 0b91bb68..492516e3 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_identity__test__err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_identity__test__err.snap @@ -1,6 +1,5 @@ --- source: crates/squawk_linter/src/rules/prefer_identity.rs -assertion_line: 86 expression: errors --- [ @@ -11,6 +10,7 @@ expression: errors help: Some( "Use an `IDENTITY` column instead.", ), + fix: None, }, Violation { code: PreferIdentity, @@ -19,6 +19,7 @@ expression: errors help: Some( "Use an `IDENTITY` column instead.", ), + fix: None, }, Violation { code: PreferIdentity, @@ -27,6 +28,7 @@ expression: errors help: Some( "Use an `IDENTITY` column instead.", ), + fix: None, }, Violation { code: PreferIdentity, @@ -35,6 +37,7 @@ expression: errors help: Some( "Use an `IDENTITY` column instead.", ), + fix: None, }, Violation { code: PreferIdentity, @@ -43,6 +46,7 @@ expression: errors help: Some( "Use an `IDENTITY` column instead.", ), + fix: None, }, Violation { code: PreferIdentity, @@ -51,6 +55,7 @@ expression: errors help: Some( "Use an `IDENTITY` column instead.", ), + fix: None, }, Violation { code: PreferIdentity, @@ -59,5 +64,6 @@ expression: errors help: Some( "Use an `IDENTITY` column instead.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_identity__test__ok_when_quoted.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_identity__test__ok_when_quoted.snap index c6c66aa5..08d31d86 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_identity__test__ok_when_quoted.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_identity__test__ok_when_quoted.snap @@ -10,6 +10,7 @@ expression: errors help: Some( "Use an `IDENTITY` column instead.", ), + fix: None, }, Violation { code: PreferIdentity, @@ -18,5 +19,6 @@ expression: errors help: Some( "Use an `IDENTITY` column instead.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_column_set_not_null.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_column_set_not_null.snap index 4380370a..a0c9f90e 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_column_set_not_null.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_column_set_not_null.snap @@ -8,5 +8,6 @@ expression: errors message: "Missing transaction, the migration can't be rerun if it fails part way through.", text_range: 54..81, help: None, + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_table_drop_column_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_table_drop_column_err.snap index acc9f26a..3ca9971e 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_table_drop_column_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_table_drop_column_err.snap @@ -8,5 +8,18 @@ expression: errors message: "Missing `IF EXISTS`, the migration can't be rerun if it fails part way through.", text_range: 54..75, help: None, + fix: Some( + Fix { + title: "Insert `if exists`", + edits: [ + Edit { + text_range: 65..65, + text: Some( + " if exists", + ), + }, + ], + }, + ), }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_table_drop_constraint_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_table_drop_constraint_err.snap index 35e6946a..4d5fffb7 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_table_drop_constraint_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_table_drop_constraint_err.snap @@ -8,5 +8,18 @@ expression: errors message: "Missing `IF EXISTS`, the migration can't be rerun if it fails part way through.", text_range: 63..93, help: None, + fix: Some( + Fix { + title: "Insert `if exists`", + edits: [ + Edit { + text_range: 78..78, + text: Some( + " if exists", + ), + }, + ], + }, + ), }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_table_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_table_err.snap index d37568cd..b666e1d1 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_table_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_table_err.snap @@ -8,5 +8,18 @@ expression: errors message: "Missing `IF NOT EXISTS`, the migration can't be rerun if it fails part way through.", text_range: 63..98, help: None, + fix: Some( + Fix { + title: "Insert `if not exists`", + edits: [ + Edit { + text_range: 73..73, + text: Some( + " if not exists", + ), + }, + ], + }, + ), }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_index_concurrently_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_index_concurrently_err.snap index 04c9fb41..963ddbeb 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_index_concurrently_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_index_concurrently_err.snap @@ -10,5 +10,18 @@ expression: errors help: Some( "Use an explicit name for a concurrently created index", ), + fix: Some( + Fix { + title: "Insert `if not exists`", + edits: [ + Edit { + text_range: 66..66, + text: Some( + "if not exists ", + ), + }, + ], + }, + ), }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_index_concurrently_muli_stmts_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_index_concurrently_muli_stmts_err.snap deleted file mode 100644 index cc254e82..00000000 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_index_concurrently_muli_stmts_err.snap +++ /dev/null @@ -1,22 +0,0 @@ ---- -source: crates/squawk_linter/src/rules/prefer_robust_stmts.rs -expression: errors ---- -[ - Violation { - code: PreferRobustStmts, - message: "Missing `IF NOT EXISTS`, the migration can't be rerun if it fails part way through.", - text_range: 1..57, - help: Some( - "Use an explicit name for a concurrently created index", - ), - }, - Violation { - code: PreferRobustStmts, - message: "Missing `IF NOT EXISTS`, the migration can't be rerun if it fails part way through.", - text_range: 59..115, - help: Some( - "Use an explicit name for a concurrently created index", - ), - }, -] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_index_concurrently_unnamed_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_index_concurrently_unnamed_err.snap deleted file mode 100644 index 99b97f52..00000000 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_index_concurrently_unnamed_err.snap +++ /dev/null @@ -1,14 +0,0 @@ ---- -source: crates/squawk_linter/src/rules/prefer_robust_stmts.rs -expression: errors ---- -[ - Violation { - code: PreferRobustStmts, - message: "Missing `IF NOT EXISTS`, the migration can't be rerun if it fails part way through.", - text_range: 40..96, - help: Some( - "Use an explicit name for a concurrently created index", - ), - }, -] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_table_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_table_err.snap index 4d7fd451..d395caef 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_table_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_table_err.snap @@ -8,5 +8,18 @@ expression: errors message: "Missing `IF NOT EXISTS`, the migration can't be rerun if it fails part way through.", text_range: 11..122, help: None, + fix: Some( + Fix { + title: "Insert `if not exists`", + edits: [ + Edit { + text_range: 52..52, + text: Some( + " if not exists", + ), + }, + ], + }, + ), }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__disable_row_level_security_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__disable_row_level_security_err.snap index d0947f39..be7cc6e1 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__disable_row_level_security_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__disable_row_level_security_err.snap @@ -8,5 +8,6 @@ expression: errors message: "Missing transaction, the migration can't be rerun if it fails part way through.", text_range: 63..89, help: None, + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__double_add_after_drop_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__double_add_after_drop_err.snap index c3188e6e..b07be17e 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__double_add_after_drop_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__double_add_after_drop_err.snap @@ -8,5 +8,6 @@ expression: errors message: "Missing `IF NOT EXISTS`, the migration can't be rerun if it fails part way through.", text_range: 240..297, help: None, + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__drop_index_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__drop_index_err.snap index fc971399..7997ae42 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__drop_index_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__drop_index_err.snap @@ -5,8 +5,21 @@ expression: errors [ Violation { code: PreferRobustStmts, - message: "Missing `IF NOT EXISTS`, the migration can't be rerun if it fails part way through.", + message: "Missing `IF EXISTS`, the migration can't be rerun if it fails part way through.", text_range: 40..75, help: None, + fix: Some( + Fix { + title: "Insert `if exists`", + edits: [ + Edit { + text_range: 64..64, + text: Some( + "if exists ", + ), + }, + ], + }, + ), }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__enable_row_level_security_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__enable_row_level_security_err.snap index 767b4305..abec043d 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__enable_row_level_security_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__enable_row_level_security_err.snap @@ -8,5 +8,6 @@ expression: errors message: "Missing transaction, the migration can't be rerun if it fails part way through.", text_range: 63..88, help: None, + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__enable_row_level_security_without_exists_check_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__enable_row_level_security_without_exists_check_err.snap index a2272c37..7fbb59c4 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__enable_row_level_security_without_exists_check_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__enable_row_level_security_without_exists_check_err.snap @@ -8,5 +8,6 @@ expression: errors message: "Missing transaction, the migration can't be rerun if it fails part way through.", text_range: 53..78, help: None, + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__adding_column_non_text_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__adding_column_non_text_err.snap index c3c077cf..e7f672c3 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__adding_column_non_text_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__adding_column_non_text_err.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Use a `TEXT` field with a `CHECK` constraint.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__create_table_with_pgcatalog_varchar_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__create_table_with_pgcatalog_varchar_err.snap index e4ee461a..e7a14f0f 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__create_table_with_pgcatalog_varchar_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__create_table_with_pgcatalog_varchar_err.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Use a `TEXT` field with a `CHECK` constraint.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__create_table_with_varchar_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__create_table_with_varchar_err.snap index 9bbdef6a..d83aa957 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__create_table_with_varchar_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__create_table_with_varchar_err.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Use a `TEXT` field with a `CHECK` constraint.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__increase_varchar_size_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__increase_varchar_size_err.snap index 2b792804..c7b20817 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__increase_varchar_size_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__increase_varchar_size_err.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Use a `TEXT` field with a `CHECK` constraint.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_timestamptz__test__alter_table_with_timestamp_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_timestamptz__test__alter_table_with_timestamp_err.snap index e5d68a61..35185d17 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_timestamptz__test__alter_table_with_timestamp_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_timestamptz__test__alter_table_with_timestamp_err.snap @@ -10,6 +10,7 @@ expression: errors help: Some( "Use timestamptz instead of timestamp for your column type.", ), + fix: None, }, Violation { code: PreferTimestampTz, @@ -18,5 +19,6 @@ expression: errors help: Some( "Use timestamptz instead of timestamp for your column type.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_timestamptz__test__create_table_with_timestamp_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_timestamptz__test__create_table_with_timestamp_err.snap index d913322e..1e44382c 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_timestamptz__test__create_table_with_timestamp_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_timestamptz__test__create_table_with_timestamp_err.snap @@ -10,6 +10,7 @@ expression: errors help: Some( "Use timestamptz instead of timestamp for your column type.", ), + fix: None, }, Violation { code: PreferTimestampTz, @@ -18,5 +19,6 @@ expression: errors help: Some( "Use timestamptz instead of timestamp for your column type.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__renaming_column__test__err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__renaming_column__test__err.snap index 2c4bed06..f2d10f27 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__renaming_column__test__err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__renaming_column__test__err.snap @@ -8,5 +8,6 @@ expression: errors message: "Renaming a column may break existing clients.", text_range: 26..74, help: None, + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__renaming_table__test__err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__renaming_table__test__err.snap index 7461e6fa..65e6026d 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__renaming_table__test__err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__renaming_table__test__err.snap @@ -8,5 +8,6 @@ expression: errors message: "Renaming a table may break existing clients.", text_range: 26..52, help: None, + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__require_concurrent_index_creation__test__adding_index_non_concurrently_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__require_concurrent_index_creation__test__adding_index_non_concurrently_err.snap index ea881b2f..a9972c9e 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__require_concurrent_index_creation__test__adding_index_non_concurrently_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__require_concurrent_index_creation__test__adding_index_non_concurrently_err.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Use `CONCURRENTLY` to avoid blocking writes.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__require_concurrent_index_deletion__test__drop_index_missing_concurrently_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__require_concurrent_index_deletion__test__drop_index_missing_concurrently_err.snap index ab820050..3b3e1df7 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__require_concurrent_index_deletion__test__drop_index_missing_concurrently_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__require_concurrent_index_deletion__test__drop_index_missing_concurrently_err.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Drop the index `CONCURRENTLY`.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__begin_assume_transaction_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__begin_assume_transaction_err.snap index 1514d4d8..78e27c4a 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__begin_assume_transaction_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__begin_assume_transaction_err.snap @@ -10,6 +10,7 @@ expression: errors help: Some( "Put migration statements in separate files to have them be in separate transactions or don't use the assume-in-transaction setting.", ), + fix: None, }, Violation { code: TransactionNesting, @@ -18,6 +19,7 @@ expression: errors help: Some( "Put migration statements in separate files to have them be in separate transactions or don't use the assume-in-transaction setting.", ), + fix: None, }, Violation { code: TransactionNesting, @@ -26,5 +28,6 @@ expression: errors help: Some( "Put migration statements in separate files to have them be in separate transactions or don't use the assume-in-transaction setting.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__begin_repeated_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__begin_repeated_err.snap index 804d7973..c5ecc608 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__begin_repeated_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__begin_repeated_err.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Put migration statements in separate files to have them be in separate transactions or don't use the assume-in-transaction setting.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__commit_repeated_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__commit_repeated_err.snap index bdb1e945..211c4420 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__commit_repeated_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__commit_repeated_err.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "`BEGIN` a transaction at an earlier point in the migration or remove this statement.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__commit_with_assume_in_transaction_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__commit_with_assume_in_transaction_err.snap index d05823c1..c4250fde 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__commit_with_assume_in_transaction_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__commit_with_assume_in_transaction_err.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Put migration statements in separate files to have them be in separate transactions or don't use the assume-in-transaction setting.", ), + fix: None, }, ] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__rollback_with_assume_in_transaction_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__rollback_with_assume_in_transaction_err.snap index b371afd5..cec58281 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__rollback_with_assume_in_transaction_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__rollback_with_assume_in_transaction_err.snap @@ -10,5 +10,6 @@ expression: errors help: Some( "Put migration statements in separate files to have them be in separate transactions or don't use the assume-in-transaction setting.", ), + fix: None, }, ] diff --git a/crates/squawk_server/src/lib.rs b/crates/squawk_server/src/lib.rs index d0ff0786..c10bcac2 100644 --- a/crates/squawk_server/src/lib.rs +++ b/crates/squawk_server/src/lib.rs @@ -1,18 +1,22 @@ -use anyhow::Result; +use anyhow::{Context, Result}; use line_index::LineIndex; use log::info; use lsp_server::{Connection, Message, Notification, Response}; use lsp_types::{ - CodeDescription, Diagnostic, DiagnosticSeverity, DidChangeTextDocumentParams, - DidCloseTextDocumentParams, DidOpenTextDocumentParams, GotoDefinitionParams, - GotoDefinitionResponse, InitializeParams, Location, Position, PublishDiagnosticsParams, Range, - ServerCapabilities, TextDocumentSyncCapability, TextDocumentSyncKind, Url, + CodeAction, CodeActionKind, CodeActionOptions, CodeActionOrCommand, CodeActionParams, + CodeActionProviderCapability, CodeActionResponse, CodeDescription, Diagnostic, + DiagnosticSeverity, DidChangeTextDocumentParams, DidCloseTextDocumentParams, + DidOpenTextDocumentParams, GotoDefinitionParams, GotoDefinitionResponse, InitializeParams, + Location, Position, PublishDiagnosticsParams, Range, ServerCapabilities, + TextDocumentSyncCapability, TextDocumentSyncKind, TextEdit, Url, WorkDoneProgressOptions, + WorkspaceEdit, notification::{ DidChangeTextDocument, DidCloseTextDocument, DidOpenTextDocument, Notification as _, PublishDiagnostics, }, - request::{GotoDefinition, Request}, + request::{CodeActionRequest, GotoDefinition, Request}, }; +use serde::{Deserialize, Serialize}; use squawk_linter::Linter; use squawk_syntax::{Parse, SourceFile}; use std::collections::HashMap; @@ -32,6 +36,13 @@ pub fn run() -> Result<()> { text_document_sync: Some(TextDocumentSyncCapability::Kind( TextDocumentSyncKind::INCREMENTAL, )), + code_action_provider: Some(CodeActionProviderCapability::Options(CodeActionOptions { + code_action_kinds: Some(vec![CodeActionKind::QUICKFIX]), + work_done_progress_options: WorkDoneProgressOptions { + work_done_progress: None, + }, + resolve_provider: None, + })), // definition_provider: Some(OneOf::Left(true)), ..Default::default() }) @@ -73,6 +84,9 @@ fn main_loop(connection: Connection, params: serde_json::Value) -> Result<()> { GotoDefinition::METHOD => { handle_goto_definition(&connection, req)?; } + CodeActionRequest::METHOD => { + handle_code_action(&connection, req, &documents)?; + } "squawk/syntaxTree" => { handle_syntax_tree(&connection, req, &documents)?; } @@ -128,6 +142,54 @@ fn handle_goto_definition(connection: &Connection, req: lsp_server::Request) -> Ok(()) } +fn handle_code_action( + connection: &Connection, + req: lsp_server::Request, + _documents: &HashMap, +) -> Result<()> { + let params: CodeActionParams = serde_json::from_value(req.params)?; + let uri = params.text_document.uri; + + let mut actions = Vec::new(); + + for mut diagnostic in params.context.diagnostics { + if let Some(data) = diagnostic.data.take() { + let associated_data: AssociatedDiagnosticData = + serde_json::from_value(data).context("deserializing diagnostic data")?; + + let fix_action = CodeAction { + title: associated_data.title, + kind: Some(CodeActionKind::QUICKFIX), + diagnostics: Some(vec![diagnostic.clone()]), + edit: Some(WorkspaceEdit { + changes: Some({ + let mut changes = HashMap::new(); + changes.insert(uri.clone(), associated_data.edits); + changes + }), + ..Default::default() + }), + command: None, + is_preferred: Some(true), + disabled: None, + data: None, + }; + + actions.push(CodeActionOrCommand::CodeAction(fix_action)); + }; + } + + let result: CodeActionResponse = actions; + let resp = Response { + id: req.id, + result: Some(serde_json::to_value(&result).unwrap()), + error: None, + }; + + connection.sender.send(Message::Response(resp))?; + Ok(()) +} + fn publish_diagnostics( connection: &Connection, uri: Url, @@ -223,6 +285,18 @@ fn handle_did_close( Ok(()) } +// Based on Ruff's setup for LSP diagnostic edits +// see: https://github.com/astral-sh/ruff/blob/1a368b0bf97c3d0246390679166bbd2d589acf39/crates/ruff_server/src/lint.rs#L31 +/// This is serialized on the diagnostic `data` field. +#[derive(Serialize, Deserialize, Debug, Clone)] +struct AssociatedDiagnosticData { + /// The message describing what the fix does, if it exists, or the diagnostic name otherwise. + title: String, + /// Edits to fix the diagnostic. If this is empty, a fix + /// does not exist. + edits: Vec, +} + fn lint(content: &str) -> Vec { let parse: Parse = SourceFile::parse(content); let parse_errors = parse.errors(); @@ -271,6 +345,27 @@ fn lint(content: &str) -> Vec { end_line_col.col += 1; } + let data = if let Some(fix) = violation.fix { + Some(AssociatedDiagnosticData { + title: fix.title, + edits: fix + .edits + .into_iter() + .filter_map(|x| { + let start_line = line_index.try_line_col(x.text_range.start())?; + let end_line = line_index.try_line_col(x.text_range.end())?; + let range = Range::new( + Position::new(start_line.line, start_line.col), + Position::new(end_line.line, end_line.col), + ); + Some(TextEdit::new(range, x.text.unwrap_or_default())) + }) + .collect(), + }) + } else { + None + }; + let diagnostic = Diagnostic { range: Range::new( Position::new(start_line_col.line, start_line_col.col), @@ -285,6 +380,7 @@ fn lint(content: &str) -> Vec { }), source: Some("squawk".to_string()), message: violation.message, + data: data.map(|d| serde_json::to_value(d).unwrap()), ..Default::default() }; diagnostics.push(diagnostic); @@ -306,7 +402,7 @@ fn handle_syntax_tree( let params: SyntaxTreeParams = serde_json::from_value(req.params)?; let uri = params.text_document.uri; - info!("Generating syntax tree for: {}", uri); + info!("Generating syntax tree for: {uri}"); let content = documents.get(&uri).map_or("", |doc| &doc.content); @@ -337,7 +433,7 @@ fn handle_tokens( let params: TokensParams = serde_json::from_value(req.params)?; let uri = params.text_document.uri; - info!("Generating tokens for: {}", uri); + info!("Generating tokens for: {uri}"); let content = documents.get(&uri).map_or("", |doc| &doc.content); diff --git a/crates/squawk_syntax/src/ast/generated/nodes.rs b/crates/squawk_syntax/src/ast/generated/nodes.rs index c02c927c..0ec92287 100644 --- a/crates/squawk_syntax/src/ast/generated/nodes.rs +++ b/crates/squawk_syntax/src/ast/generated/nodes.rs @@ -3346,10 +3346,6 @@ pub struct DropConstraint { pub(crate) syntax: SyntaxNode, } impl DropConstraint { - #[inline] - pub fn constraint(&self) -> Option { - support::child(&self.syntax) - } #[inline] pub fn if_exists(&self) -> Option { support::child(&self.syntax) @@ -3363,6 +3359,10 @@ impl DropConstraint { support::token(&self.syntax, SyntaxKind::CASCADE_KW) } #[inline] + pub fn constraint_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::CONSTRAINT_KW) + } + #[inline] pub fn drop_token(&self) -> Option { support::token(&self.syntax, SyntaxKind::DROP_KW) } diff --git a/crates/squawk_syntax/src/postgresql.ungram b/crates/squawk_syntax/src/postgresql.ungram index 8b561850..524939d2 100644 --- a/crates/squawk_syntax/src/postgresql.ungram +++ b/crates/squawk_syntax/src/postgresql.ungram @@ -824,7 +824,7 @@ AddConstraint = Enforced? DropConstraint = - 'drop' Constraint IfExists? NameRef ('restrict' | 'cascade')? + 'drop' 'constraint' IfExists? NameRef ('restrict' | 'cascade')? RenameConstraint = 'rename' 'constraint' NameRef 'to' Name