Skip to content
This repository was archived by the owner on Aug 31, 2023. It is now read-only.

Commit 89bf9ee

Browse files
committed
refactor(rome_service): better messaging for rule options
1 parent 8f46efb commit 89bf9ee

File tree

12 files changed

+1466
-558
lines changed

12 files changed

+1466
-558
lines changed

crates/rome_js_analyze/src/analyzers/nursery/no_nonoctal_decimal_escape.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ declare_rule! {
1313
/// Since ECMAScript 2021, the escape sequences \8 and \9 have been defined as non-octal decimal escape sequences.
1414
/// However, most JavaScript engines consider them to be "useless" escapes. For example:
1515
///
16-
/// ```js
16+
/// ```js,ignore
1717
/// "\8" === "8"; // true
1818
/// "\9" === "9"; // true
1919
/// ```
@@ -34,7 +34,11 @@ declare_rule! {
3434
/// ```
3535
///
3636
/// ```js,expect_diagnostic
37-
/// const x = "Don't use \8 and \9 escapes.";
37+
/// const x = "Don't use \8 escape.";
38+
/// ```
39+
///
40+
/// ```js,expect_diagnostic
41+
/// const x = "Don't use \9 escape.";
3842
/// ```
3943
///
4044
/// ```js,expect_diagnostic

crates/rome_js_analyze/src/options.rs

Lines changed: 82 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ use crate::semantic_analyzers::nursery::use_naming_convention::{
1010
use bpaf::Bpaf;
1111
use rome_analyze::options::RuleOptions;
1212
use rome_analyze::RuleKey;
13-
use rome_deserialize::json::{has_only_known_keys, VisitJsonNode};
13+
use rome_deserialize::json::VisitJsonNode;
1414
use rome_deserialize::{DeserializationDiagnostic, VisitNode};
15-
use rome_json_syntax::{JsonLanguage, JsonSyntaxNode};
16-
use rome_rowan::SyntaxNode;
15+
use rome_json_syntax::{AnyJsonValue, JsonLanguage, JsonMemberName, JsonObjectValue};
16+
use rome_rowan::AstNode;
1717
#[cfg(feature = "schemars")]
1818
use schemars::JsonSchema;
1919
use serde::{Deserialize, Serialize};
@@ -43,13 +43,6 @@ impl FromStr for PossibleOptions {
4343
}
4444

4545
impl PossibleOptions {
46-
const KNOWN_KEYS: &'static [&'static str] = &[
47-
"enumMemberCase",
48-
"hooks",
49-
"maxAllowedComplexity",
50-
"strictCase",
51-
];
52-
5346
pub fn extract_option(&self, rule_key: &RuleKey) -> RuleOptions {
5447
match rule_key.rule_name() {
5548
"noExcessiveComplexity" => {
@@ -79,44 +72,97 @@ impl PossibleOptions {
7972
}
8073
}
8174

82-
impl VisitJsonNode for PossibleOptions {}
83-
impl VisitNode<JsonLanguage> for PossibleOptions {
84-
fn visit_member_name(
75+
impl PossibleOptions {
76+
pub fn map_to_rule_options(
8577
&mut self,
86-
node: &JsonSyntaxNode,
78+
value: &AnyJsonValue,
79+
name: &str,
80+
rule_name: &str,
8781
diagnostics: &mut Vec<DeserializationDiagnostic>,
8882
) -> Option<()> {
89-
has_only_known_keys(node, PossibleOptions::KNOWN_KEYS, diagnostics)
83+
let value = JsonObjectValue::cast_ref(value.syntax()).or_else(|| {
84+
diagnostics.push(DeserializationDiagnostic::new_incorrect_type_for_value(
85+
name,
86+
"object",
87+
value.range(),
88+
));
89+
None
90+
})?;
91+
for element in value.json_member_list() {
92+
let element = element.ok()?;
93+
let key = element.name().ok()?;
94+
let value = element.value().ok()?;
95+
let name = key.inner_string_text().ok()?;
96+
self.validate_key(&key, rule_name, diagnostics)?;
97+
match name.text() {
98+
"hooks" => {
99+
let mut options = HooksOptions::default();
100+
self.map_to_array(&value, &name, &mut options, diagnostics)?;
101+
*self = PossibleOptions::Hooks(options);
102+
}
103+
"maxAllowedComplexity" => {
104+
let mut options = ComplexityOptions::default();
105+
options.visit_map(key.syntax(), value.syntax(), diagnostics)?;
106+
*self = PossibleOptions::Complexity(options);
107+
}
108+
"strictCase" | "enumMemberCase" => {
109+
let mut options = match self {
110+
PossibleOptions::NamingConvention(options) => *options,
111+
_ => NamingConventionOptions::default(),
112+
};
113+
options.visit_map(key.syntax(), value.syntax(), diagnostics)?;
114+
*self = PossibleOptions::NamingConvention(options);
115+
}
116+
117+
_ => (),
118+
}
119+
}
120+
121+
Some(())
90122
}
91123

92-
fn visit_map(
124+
pub fn validate_key(
93125
&mut self,
94-
key: &SyntaxNode<JsonLanguage>,
95-
value: &SyntaxNode<JsonLanguage>,
126+
node: &JsonMemberName,
127+
rule_name: &str,
96128
diagnostics: &mut Vec<DeserializationDiagnostic>,
97129
) -> Option<()> {
98-
let (name, val) = self.get_key_and_value(key, value, diagnostics)?;
99-
match name.text() {
100-
"hooks" => {
101-
let mut options = HooksOptions::default();
102-
self.map_to_array(&val, &name, &mut options, diagnostics)?;
103-
*self = PossibleOptions::Hooks(options);
130+
let key_name = node.inner_string_text().ok()?;
131+
let key_name = key_name.text();
132+
match rule_name {
133+
"useExhaustiveDependencies" | "useHookAtTopLevel" => {
134+
if key_name != "hooks" {
135+
diagnostics.push(DeserializationDiagnostic::new_unknown_key(
136+
key_name,
137+
node.range(),
138+
&["hooks"],
139+
));
140+
}
104141
}
105-
"maxAllowedComplexity" => {
106-
let mut options = ComplexityOptions::default();
107-
options.visit_map(key, value, diagnostics)?;
108-
*self = PossibleOptions::Complexity(options);
142+
"useNamingConvention" => {
143+
if !matches!(key_name, "strictCase" | "enumMemberCase") {
144+
diagnostics.push(DeserializationDiagnostic::new_unknown_key(
145+
key_name,
146+
node.range(),
147+
&["strictCase", "enumMemberCase"],
148+
));
149+
}
109150
}
110-
"strictCase" | "enumMemberCase" => {
111-
let mut options = match self {
112-
PossibleOptions::NamingConvention(options) => *options,
113-
_ => NamingConventionOptions::default(),
114-
};
115-
options.visit_map(key, value, diagnostics)?;
116-
*self = PossibleOptions::NamingConvention(options);
151+
"noExcessiveComplexity" => {
152+
if !matches!(key_name, "maxAllowedComplexity") {
153+
diagnostics.push(DeserializationDiagnostic::new_unknown_key(
154+
key_name,
155+
node.range(),
156+
&["maxAllowedComplexity"],
157+
));
158+
}
117159
}
118-
_ => (),
160+
_ => {}
119161
}
162+
120163
Some(())
121164
}
122165
}
166+
167+
impl VisitJsonNode for PossibleOptions {}
168+
impl VisitNode<JsonLanguage> for PossibleOptions {}

crates/rome_js_analyze/tests/specs/nursery/useExhaustiveDependencies/malformedOptions.js.snap

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,7 @@ malformedOptions.options:9:7 deserialize ━━━━━━━━━━━━━
2222
2323
i Accepted keys
2424
25-
- enumMemberCase
2625
- hooks
27-
- maxAllowedComplexity
28-
- strictCase
2926
3027
3128
```

crates/rome_service/src/configuration/parse/json/linter.rs

Lines changed: 58 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,64 @@ impl VisitNode<JsonLanguage> for LinterConfiguration {
5151
}
5252
}
5353

54+
impl RuleConfiguration {
55+
pub(crate) fn map_rule_configuration(
56+
&mut self,
57+
value: &AnyJsonValue,
58+
key_name: &str,
59+
rule_name: &str,
60+
diagnostics: &mut Vec<DeserializationDiagnostic>,
61+
) -> Option<()> {
62+
let value = JsonObjectValue::cast_ref(value.syntax()).or_else(|| {
63+
diagnostics.push(DeserializationDiagnostic::new_incorrect_type_for_value(
64+
key_name,
65+
"object",
66+
value.range(),
67+
));
68+
None
69+
})?;
70+
for element in value.json_member_list() {
71+
let element = element.ok()?;
72+
let key = element.name().ok()?;
73+
let value = element.value().ok()?;
74+
let (name, value) =
75+
self.get_key_and_value(key.syntax(), value.syntax(), diagnostics)?;
76+
let name_text = name.text();
77+
match name_text {
78+
"level" => {
79+
if let RuleConfiguration::WithOptions(options) = self {
80+
let mut level = RulePlainConfiguration::default();
81+
level.visit_member_value(value.syntax(), diagnostics)?;
82+
options.level = level;
83+
} else {
84+
let mut level = RulePlainConfiguration::default();
85+
level.visit_member_value(value.syntax(), diagnostics)?;
86+
*self = RuleConfiguration::WithOptions(RuleWithOptions {
87+
level,
88+
..RuleWithOptions::default()
89+
})
90+
}
91+
}
92+
"options" => {
93+
let mut possible_options = PossibleOptions::default();
94+
95+
possible_options.map_to_rule_options(&value, name_text, rule_name, diagnostics);
96+
if let RuleConfiguration::WithOptions(options) = self {
97+
options.options = Some(possible_options)
98+
} else {
99+
*self = RuleConfiguration::WithOptions(RuleWithOptions {
100+
options: Some(possible_options),
101+
..RuleWithOptions::default()
102+
})
103+
}
104+
}
105+
_ => {}
106+
}
107+
}
108+
Some(())
109+
}
110+
}
111+
54112
impl VisitJsonNode for RuleConfiguration {}
55113

56114
impl VisitNode<JsonLanguage> for RuleConfiguration {
@@ -82,47 +140,6 @@ impl VisitNode<JsonLanguage> for RuleConfiguration {
82140
}
83141
Some(())
84142
}
85-
86-
fn visit_map(
87-
&mut self,
88-
key: &SyntaxNode<JsonLanguage>,
89-
value: &SyntaxNode<JsonLanguage>,
90-
diagnostics: &mut Vec<DeserializationDiagnostic>,
91-
) -> Option<()> {
92-
let (name, value) = self.get_key_and_value(key, value, diagnostics)?;
93-
let name_text = name.text();
94-
match name_text {
95-
"level" => {
96-
if let RuleConfiguration::WithOptions(options) = self {
97-
let mut level = RulePlainConfiguration::default();
98-
level.visit_member_value(value.syntax(), diagnostics)?;
99-
options.level = level;
100-
} else {
101-
let mut level = RulePlainConfiguration::default();
102-
level.visit_member_value(value.syntax(), diagnostics)?;
103-
*self = RuleConfiguration::WithOptions(RuleWithOptions {
104-
level,
105-
..RuleWithOptions::default()
106-
})
107-
}
108-
}
109-
"options" => {
110-
let mut possible_options = PossibleOptions::default();
111-
self.map_to_object(&value, name_text, &mut possible_options, diagnostics);
112-
if let RuleConfiguration::WithOptions(options) = self {
113-
options.options = Some(possible_options)
114-
} else {
115-
*self = RuleConfiguration::WithOptions(RuleWithOptions {
116-
options: Some(possible_options),
117-
..RuleWithOptions::default()
118-
})
119-
}
120-
}
121-
_ => {}
122-
}
123-
124-
Some(())
125-
}
126143
}
127144

128145
impl VisitJsonNode for RulePlainConfiguration {}

0 commit comments

Comments
 (0)