Skip to content

Commit

Permalink
feat(migrate): add migration for indentWidth (#476)
Browse files Browse the repository at this point in the history
  • Loading branch information
ematipico authored Oct 4, 2023
1 parent a7ad8f3 commit d804033
Show file tree
Hide file tree
Showing 11 changed files with 355 additions and 39 deletions.
6 changes: 6 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions crates/biome_json_factory/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
pub use crate::generated::JsonSyntaxFactory;
use biome_json_syntax::JsonLanguage;
use biome_rowan::TreeBuilder;

mod generated;
pub use crate::generated::JsonSyntaxFactory;
pub mod make;

// Re-exported for tests
#[doc(hidden)]
pub use biome_json_syntax as syntax;

pub type JsonSyntaxTreeBuilder = TreeBuilder<'static, JsonLanguage, JsonSyntaxFactory>;

pub use generated::node_factory as make;
7 changes: 7 additions & 0 deletions crates/biome_json_factory/src/make.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use biome_json_syntax::{JsonSyntaxKind, JsonSyntaxToken};

pub use crate::generated::node_factory::*;

pub fn ident(text: &str) -> JsonSyntaxToken {
JsonSyntaxToken::new_detached(JsonSyntaxKind::IDENT, text, [], [])
}
19 changes: 13 additions & 6 deletions crates/biome_migrate/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,18 @@ version = "0.0.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
biome_analyze = { workspace = true }
biome_diagnostics = { workspace = true }
biome_json_syntax = { workspace = true }
biome_rowan = { workspace = true }
lazy_static = { workspace = true }
biome_analyze = { workspace = true }
biome_console = { workspace = true }
biome_diagnostics = { workspace = true }
biome_json_factory = { workspace = true }
biome_json_syntax = { workspace = true }
biome_rowan = { workspace = true }
lazy_static = { workspace = true }

[dev-dependencies]
biome_json_parser = { workspace = true }
biome_json_factory = { path = "../biome_json_factory" }
biome_json_parser = { path = "../biome_json_parser" }
biome_service = { path = "../biome_service" }
biome_test_utils = { path = "../biome_test_utils" }
insta = { workspace = true, features = ["glob"] }
tests_macros = { path = "../tests_macros" }
8 changes: 4 additions & 4 deletions crates/biome_migrate/src/analyzers.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::analyzers::rule_set::RuleSet;
use crate::analyzers::indent_size::IndentSize;
use biome_analyze::{GroupCategory, RegistryVisitor, RuleCategory, RuleGroup};
use biome_json_syntax::JsonLanguage;

mod rule_set;
mod indent_size;

pub(crate) struct MigrationGroup;
pub(crate) struct MigrationCategory;
Expand All @@ -14,8 +14,8 @@ impl RuleGroup for MigrationGroup {

fn record_rules<V: RegistryVisitor<Self::Language> + ?Sized>(registry: &mut V) {
// Order here is important, rules should be added from the most old, to the most recent
// v13.0.0
registry.record_rule::<RuleSet>();
// v1.3.0
registry.record_rule::<IndentSize>();
}
}

Expand Down
65 changes: 65 additions & 0 deletions crates/biome_migrate/src/analyzers/indent_size.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use crate::{declare_migration, MigrationAction};
use biome_analyze::context::RuleContext;
use biome_analyze::{ActionCategory, Ast, Rule, RuleAction, RuleDiagnostic};
use biome_console::markup;
use biome_diagnostics::{category, Applicability};
use biome_json_factory::make::{ident, json_member_name};
use biome_json_syntax::JsonMemberName;
use biome_rowan::{AstNode, BatchMutationExt};

declare_migration! {
pub(crate) IndentSize {
version: "1.3.0",
name: "indentSize",
}
}

impl Rule for IndentSize {
type Query = Ast<JsonMemberName>;
type State = ();
type Signals = Option<Self::State>;
type Options = ();

fn run(ctx: &RuleContext<Self>) -> Self::Signals {
let node = ctx.query();

let node_text = node.inner_string_text().ok()?;
if node_text.text() == "indentSize" {
return Some(());
}
None
}

fn diagnostic(ctx: &RuleContext<Self>, _state: &Self::State) -> Option<RuleDiagnostic> {
let node = ctx.query();
Some(
RuleDiagnostic::new(
category!("migrate"),
node.range(),
markup! {
"The option "<Emphasis>"indentSize"</Emphasis>" is deprecated."
}
.to_owned(),
)
.deprecated(),
)
}

fn action(ctx: &RuleContext<Self>, _state: &Self::State) -> Option<MigrationAction> {
let node = ctx.query();
let mut mutation = ctx.root().begin();

let new_node = json_member_name(ident("\"indentWidth\""));
mutation.replace_node(node.clone(), new_node);

Some(RuleAction {
category: ActionCategory::QuickFix,
applicability: Applicability::Always,
message: markup! {
"Use the property "<Emphasis>"indentWidth"</Emphasis>" instead."
}
.to_owned(),
mutation,
})
}
}
25 changes: 0 additions & 25 deletions crates/biome_migrate/src/analyzers/rule_set.rs

This file was deleted.

4 changes: 3 additions & 1 deletion crates/biome_migrate/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::registry::visit_migration_registry;
pub use biome_analyze::ControlFlow;
use biome_analyze::{
AnalysisFilter, Analyzer, AnalyzerContext, AnalyzerOptions, AnalyzerSignal, InspectMatcher,
LanguageRoot, MatchQueryParams, MetadataRegistry, RuleRegistry,
LanguageRoot, MatchQueryParams, MetadataRegistry, RuleAction, RuleRegistry,
};
use biome_diagnostics::Error;
use biome_json_syntax::JsonLanguage;
Expand Down Expand Up @@ -93,6 +93,8 @@ where
analyze_with_inspect_matcher(root, configuration_file_path, |_| {}, emit_signal)
}

pub(crate) type MigrationAction = RuleAction<JsonLanguage>;

#[cfg(test)]
mod test {
use crate::migrate_configuration;
Expand Down
127 changes: 127 additions & 0 deletions crates/biome_migrate/tests/spec_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
use biome_analyze::{AnalyzerAction, ControlFlow, Never};
use biome_diagnostics::advice::CodeSuggestionAdvice;
use biome_diagnostics::{DiagnosticExt, Severity};
use biome_json_parser::{parse_json, JsonParserOptions};
use biome_json_syntax::JsonLanguage;
use biome_rowan::AstNode;
use biome_test_utils::{
assert_errors_are_absent, code_fix_to_string, diagnostic_to_string,
has_bogus_nodes_or_empty_slots, parse_test_path, register_leak_checker,
write_analyzer_snapshot,
};
use std::ffi::OsStr;
use std::fs::read_to_string;
use std::path::Path;

tests_macros::gen_tests! {"tests/specs/**/*.json", crate::run_test, "module"}

fn run_test(input: &'static str, _: &str, _: &str, _: &str) {
register_leak_checker();

let input_file = Path::new(input);
let file_name = input_file.file_name().and_then(OsStr::to_str).unwrap();

let (group, rule) = parse_test_path(input_file);
if rule == "specs" || rule == "suppression" {
panic!("the test file must be placed in the {rule}/<group-name>/<rule-name>/ directory");
}
if group == "specs" || group == "suppression" {
panic!("the test file must be placed in the {group}/{rule}/<rule-name>/ directory");
}

let mut snapshot = String::new();

let input_code = read_to_string(input_file)
.unwrap_or_else(|err| panic!("failed to read {:?}: {:?}", input_file, err));

let quantity_diagnostics = analyze_and_snap(&mut snapshot, &input_code, file_name, input_file);

insta::with_settings!({
prepend_module_to_snapshot => false,
snapshot_path => input_file.parent().unwrap(),
}, {
insta::assert_snapshot!(file_name, snapshot, file_name);
});

if input_code.contains("/* should not generate diagnostics */") && quantity_diagnostics > 0 {
panic!("This test should not generate diagnostics");
}
}

pub(crate) fn analyze_and_snap(
snapshot: &mut String,
input_code: &str,
file_name: &str,
input_file: &Path,
) -> usize {
let parsed = parse_json(input_code, JsonParserOptions::default());
let root = parsed.tree();

let mut diagnostics = Vec::new();
let mut code_fixes = Vec::new();

let (_, errors) =
biome_migrate::migrate_configuration(&root.value().unwrap(), input_file, |event| {
if let Some(mut diag) = event.diagnostic() {
for action in event.actions() {
if !action.is_suppression() {
check_code_action(input_file, input_code, &action);
diag = diag.add_code_suggestion(CodeSuggestionAdvice::from(action));
}
}

let error = diag.with_severity(Severity::Warning);
diagnostics.push(diagnostic_to_string(file_name, input_code, error));
return ControlFlow::Continue(());
}

for action in event.actions() {
if !action.is_suppression() {
check_code_action(input_file, input_code, &action);
code_fixes.push(code_fix_to_string(input_code, action));
}
}

ControlFlow::<Never>::Continue(())
});

for error in errors {
diagnostics.push(diagnostic_to_string(file_name, input_code, error));
}
write_analyzer_snapshot(
snapshot,
input_code,
diagnostics.as_slice(),
code_fixes.as_slice(),
);

diagnostics.len()
}

fn check_code_action(path: &Path, source: &str, action: &AnalyzerAction<JsonLanguage>) {
let (_, text_edit) = action.mutation.as_text_edits().unwrap_or_default();

let output = text_edit.new_string(source);

let new_tree = action.mutation.clone().commit();

// Checks that applying the text edits returned by the BatchMutation
// returns the same code as printing the modified syntax tree
assert_eq!(new_tree.to_string(), output);

if has_bogus_nodes_or_empty_slots(&new_tree) {
panic!(
"modified tree has bogus nodes or empty slots:\n{new_tree:#?} \n\n {}",
new_tree
)
}

// Checks the returned tree contains no missing children node
if format!("{new_tree:?}").contains("missing (required)") {
panic!("modified tree has missing children:\n{new_tree:#?}")
}

// Re-parse the modified code and panic if the resulting tree has syntax errors
let re_parse = parse_json(&output, JsonParserOptions::default());
assert_errors_are_absent(re_parse.tree().syntax(), re_parse.diagnostics(), path);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"formatter": {
"indentSize": 4
},
"javascript": {
"formatter": {
"indentSize": 4
}
},
"json": {
"formatter": {
"indentSize": 4
}
}
}
Loading

0 comments on commit d804033

Please sign in to comment.