From ea826d5236c9acee681e53b81ee39505ec78f2bc Mon Sep 17 00:00:00 2001 From: ematipico Date: Mon, 12 Dec 2022 15:41:19 +0000 Subject: [PATCH 1/3] feat(rome_js_analyzer): rule `useValidLang` --- crates/rome_aria/src/iso.rs | 22 +++ crates/rome_aria/src/lib.rs | 2 + crates/rome_aria_metadata/build.rs | 39 +++++- crates/rome_aria_metadata/src/lib.rs | 33 ++++- .../src/categories.rs | 1 + .../src/aria_analyzers/nursery.rs | 3 +- .../aria_analyzers/nursery/use_valid_lang.rs | 129 ++++++++++++++++++ crates/rome_js_analyze/src/aria_services.rs | 23 +++- crates/rome_js_analyze/src/lib.rs | 5 +- .../tests/specs/nursery/useValidLang.jsx | 9 ++ .../tests/specs/nursery/useValidLang.jsx.snap | 85 ++++++++++++ crates/rome_js_syntax/src/jsx_ext.rs | 61 ++++++++- editors/vscode/configuration_schema.json | 11 ++ npm/backend-jsonrpc/src/workspace.ts | 5 + npm/rome/configuration_schema.json | 11 ++ website/src/pages/lint/rules/index.mdx | 6 + website/src/pages/lint/rules/useValidLang.md | 83 +++++++++++ 17 files changed, 520 insertions(+), 8 deletions(-) create mode 100644 crates/rome_aria/src/iso.rs create mode 100644 crates/rome_js_analyze/src/aria_analyzers/nursery/use_valid_lang.rs create mode 100644 crates/rome_js_analyze/tests/specs/nursery/useValidLang.jsx create mode 100644 crates/rome_js_analyze/tests/specs/nursery/useValidLang.jsx.snap create mode 100644 website/src/pages/lint/rules/useValidLang.md diff --git a/crates/rome_aria/src/iso.rs b/crates/rome_aria/src/iso.rs new file mode 100644 index 00000000000..2fe1e9b3e8b --- /dev/null +++ b/crates/rome_aria/src/iso.rs @@ -0,0 +1,22 @@ +use rome_aria_metadata::{IsoCountries, IsoLanguages, ISO_COUNTRIES, ISO_LANGUAGES}; +use std::str::FromStr; + +/// Returns a list of valid ISO countries +pub fn is_valid_country(country: &str) -> bool { + IsoCountries::from_str(country).is_ok() +} + +/// Returns a list of valid ISO languages +pub fn is_valid_language(language: &str) -> bool { + IsoLanguages::from_str(language).is_ok() +} + +/// An array of all available countries +pub fn countries() -> &'static [&'static str] { + &ISO_COUNTRIES +} + +/// An array of all available languages +pub fn languages() -> &'static [&'static str] { + &ISO_LANGUAGES +} diff --git a/crates/rome_aria/src/lib.rs b/crates/rome_aria/src/lib.rs index 3368ec63fc7..71c1db2006a 100644 --- a/crates/rome_aria/src/lib.rs +++ b/crates/rome_aria/src/lib.rs @@ -1,9 +1,11 @@ use std::str::FromStr; +pub mod iso; mod macros; pub mod properties; pub mod roles; +pub use iso::AriaIso; pub use properties::AriaProperties; pub(crate) use roles::AriaRoleDefinition; pub use roles::AriaRoles; diff --git a/crates/rome_aria_metadata/build.rs b/crates/rome_aria_metadata/build.rs index 68eed084288..17b4c6ec6d7 100644 --- a/crates/rome_aria_metadata/build.rs +++ b/crates/rome_aria_metadata/build.rs @@ -145,6 +145,37 @@ pub const ARIA_DOCUMENT_STRUCTURE_ROLES: [&str; 25] = [ "toolbar", ]; +const ISO_COUNTRIES: [&str; 233] = [ + "AF", "AL", "DZ", "AS", "AD", "AO", "AI", "AQ", "AG", "AR", "AM", "AW", "AU", "AT", "AZ", "BS", + "BH", "BD", "BB", "BY", "BE", "BZ", "BJ", "BM", "BT", "BO", "BA", "BW", "BR", "IO", "VG", "BN", + "BG", "BF", "MM", "BI", "KH", "CM", "CA", "CV", "KY", "CF", "TD", "CL", "CN", "CX", "CC", "CO", + "KM", "CK", "CR", "HR", "CU", "CY", "CZ", "CD", "DK", "DJ", "DM", "DO", "EC", "EG", "SV", "GQ", + "ER", "EE", "ET", "FK", "FO", "FJ", "FI", "FR", "PF", "GA", "GM", "GE", "DE", "GH", "GI", "GR", + "GL", "GD", "GU", "GT", "GN", "GW", "GY", "HT", "VA", "HN", "HK", "HU", "IS", "IN", "ID", "IR", + "IQ", "IE", "IM", "IL", "IT", "CI", "JM", "JP", "JE", "JO", "KZ", "KE", "KI", "KW", "KG", "LA", + "LV", "LB", "LS", "LR", "LY", "LI", "LT", "LU", "MO", "MK", "MG", "MW", "MY", "MV", "ML", "MT", + "MH", "MR", "MU", "YT", "MX", "FM", "MD", "MC", "MN", "ME", "MS", "MA", "MZ", "NA", "NR", "NP", + "NL", "AN", "NC", "NZ", "NI", "NE", "NG", "NU", "KP", "MP", "NO", "OM", "PK", "PW", "PA", "PG", + "PY", "PE", "PH", "PN", "PL", "PT", "PR", "QA", "CG", "RO", "RU", "RW", "BL", "SH", "KN", "LC", + "MF", "PM", "VC", "WS", "SM", "ST", "SA", "SN", "RS", "SC", "SL", "SG", "SK", "SI", "SB", "SO", + "ZA", "KR", "ES", "LK", "SD", "SR", "SJ", "SZ", "SE", "CH", "SY", "TW", "TJ", "TZ", "TH", "TL", + "TG", "TK", "TO", "TT", "TN", "TR", "TM", "TC", "TV", "UG", "UA", "AE", "GB", "US", "UY", "VI", + "UZ", "VU", "VE", "VN", "WF", "EH", "YE", "ZM", "ZW", +]; + +const ISO_LANGUAGES: [&str; 150] = [ + "ab", "aa", "af", "sq", "am", "ar", "an", "hy", "as", "ay", "az", "ba", "eu", "bn", "dz", "bh", + "bi", "br", "bg", "my", "be", "km", "ca", "zh", "zh-Hans", "zh-Hant", "co", "hr", "cs", "da", + "nl", "en", "eo", "et", "fo", "fa", "fj", "fi", "fr", "fy", "gl", "gd", "gv", "ka", "de", "el", + "kl", "gn", "gu", "ht", "ha", "he", "iw", "hi", "hu", "is", "io", "id", "in", "ia", "ie", "iu", + "ik", "ga", "it", "ja", "jv", "kn", "ks", "kk", "rw", "ky", "rn", "ko", "ku", "lo", "la", "lv", + "li", "ln", "lt", "mk", "mg", "ms", "ml", "mt", "mi", "mr", "mo", "mn", "na", "ne", "no", "oc", + "or", "om", "ps", "pl", "pt", "pa", "qu", "rm", "ro", "ru", "sm", "sg", "sa", "sr", "sh", "st", + "tn", "sn", "ii", "sd", "si", "ss", "sk", "sl", "so", "es", "su", "sw", "sv", "tl", "tg", "ta", + "tt", "te", "th", "bo", "ti", "to", "ts", "tr", "tk", "tw", "ug", "uk", "ur", "uz", "vi", "vo", + "wa", "cy", "wo", "xh", "yi", "ji", "yo", "zu", +]; + fn main() -> io::Result<()> { let aria_properties = generate_properties(); let aria_roles = generate_roles(); @@ -158,7 +189,7 @@ fn main() -> io::Result<()> { let ast = tokens.to_string(); let out_dir = env::var("OUT_DIR").unwrap(); - fs::write(PathBuf::from(out_dir).join("enums.rs"), ast)?; + fs::write(PathBuf::from(out_dir).join("roles_and_properties.rs"), ast)?; Ok(()) } @@ -200,10 +231,16 @@ fn generate_roles() -> TokenStream { "AriaDocumentStructureRolesEnum", ); + let iso_countries = generate_enums(ISO_COUNTRIES.len(), ISO_COUNTRIES.iter(), "IsoCountries"); + + let iso_languages = generate_enums(ISO_LANGUAGES.len(), ISO_LANGUAGES.iter(), "IsoLanguages"); + quote! { #widget_roles #abstract_roles #document_structure_roles + #iso_countries + #iso_languages } } diff --git a/crates/rome_aria_metadata/src/lib.rs b/crates/rome_aria_metadata/src/lib.rs index d0499b9e433..9caffe47bb5 100644 --- a/crates/rome_aria_metadata/src/lib.rs +++ b/crates/rome_aria_metadata/src/lib.rs @@ -1 +1,32 @@ -include!(concat!(env!("OUT_DIR"), "/enums.rs")); +include!(concat!(env!("OUT_DIR"), "/roles_and_properties.rs")); + +pub const ISO_COUNTRIES: [&str; 233] = [ + "AF", "AL", "DZ", "AS", "AD", "AO", "AI", "AQ", "AG", "AR", "AM", "AW", "AU", "AT", "AZ", "BS", + "BH", "BD", "BB", "BY", "BE", "BZ", "BJ", "BM", "BT", "BO", "BA", "BW", "BR", "IO", "VG", "BN", + "BG", "BF", "MM", "BI", "KH", "CM", "CA", "CV", "KY", "CF", "TD", "CL", "CN", "CX", "CC", "CO", + "KM", "CK", "CR", "HR", "CU", "CY", "CZ", "CD", "DK", "DJ", "DM", "DO", "EC", "EG", "SV", "GQ", + "ER", "EE", "ET", "FK", "FO", "FJ", "FI", "FR", "PF", "GA", "GM", "GE", "DE", "GH", "GI", "GR", + "GL", "GD", "GU", "GT", "GN", "GW", "GY", "HT", "VA", "HN", "HK", "HU", "IS", "IN", "ID", "IR", + "IQ", "IE", "IM", "IL", "IT", "CI", "JM", "JP", "JE", "JO", "KZ", "KE", "KI", "KW", "KG", "LA", + "LV", "LB", "LS", "LR", "LY", "LI", "LT", "LU", "MO", "MK", "MG", "MW", "MY", "MV", "ML", "MT", + "MH", "MR", "MU", "YT", "MX", "FM", "MD", "MC", "MN", "ME", "MS", "MA", "MZ", "NA", "NR", "NP", + "NL", "AN", "NC", "NZ", "NI", "NE", "NG", "NU", "KP", "MP", "NO", "OM", "PK", "PW", "PA", "PG", + "PY", "PE", "PH", "PN", "PL", "PT", "PR", "QA", "CG", "RO", "RU", "RW", "BL", "SH", "KN", "LC", + "MF", "PM", "VC", "WS", "SM", "ST", "SA", "SN", "RS", "SC", "SL", "SG", "SK", "SI", "SB", "SO", + "ZA", "KR", "ES", "LK", "SD", "SR", "SJ", "SZ", "SE", "CH", "SY", "TW", "TJ", "TZ", "TH", "TL", + "TG", "TK", "TO", "TT", "TN", "TR", "TM", "TC", "TV", "UG", "UA", "AE", "GB", "US", "UY", "VI", + "UZ", "VU", "VE", "VN", "WF", "EH", "YE", "ZM", "ZW", +]; + +pub const ISO_LANGUAGES: [&str; 150] = [ + "ab", "aa", "af", "sq", "am", "ar", "an", "hy", "as", "ay", "az", "ba", "eu", "bn", "dz", "bh", + "bi", "br", "bg", "my", "be", "km", "ca", "zh", "zh-Hans", "zh-Hant", "co", "hr", "cs", "da", + "nl", "en", "eo", "et", "fo", "fa", "fj", "fi", "fr", "fy", "gl", "gd", "gv", "ka", "de", "el", + "kl", "gn", "gu", "ht", "ha", "he", "iw", "hi", "hu", "is", "io", "id", "in", "ia", "ie", "iu", + "ik", "ga", "it", "ja", "jv", "kn", "ks", "kk", "rw", "ky", "rn", "ko", "ku", "lo", "la", "lv", + "li", "ln", "lt", "mk", "mg", "ms", "ml", "mt", "mi", "mr", "mo", "mn", "na", "ne", "no", "oc", + "or", "om", "ps", "pl", "pt", "pa", "qu", "rm", "ro", "ru", "sm", "sg", "sa", "sr", "sh", "st", + "tn", "sn", "ii", "sd", "si", "ss", "sk", "sl", "so", "es", "su", "sw", "sv", "tl", "tg", "ta", + "tt", "te", "th", "bo", "ti", "to", "ts", "tr", "tk", "tw", "ug", "uk", "ur", "uz", "vi", "vo", + "wa", "cy", "wo", "xh", "yi", "ji", "yo", "zu", +]; diff --git a/crates/rome_diagnostics_categories/src/categories.rs b/crates/rome_diagnostics_categories/src/categories.rs index 24a4862b146..e80318038d3 100644 --- a/crates/rome_diagnostics_categories/src/categories.rs +++ b/crates/rome_diagnostics_categories/src/categories.rs @@ -72,6 +72,7 @@ define_dategories! { "lint/nursery/useAriaPropTypes": "https://docs.rome.tools/lint/rules/useAriaPropTypes", "lint/nursery/useCamelCase": "https://docs.rome.tools/lint/rules/useCamelCase", "lint/nursery/useConst":"https://docs.rome.tools/lint/rules/useConst", + "lint/nursery/useValidLang":"https://docs.rome.tools/lint/rules/useValidLang", "lint/nursery/useDefaultParameterLast":"https://docs.rome.tools/lint/rules/useDefaultParameterLast", "lint/nursery/useDefaultSwitchClauseLast":"https://docs.rome.tools/lint/rules/useDefaultSwitchClauseLast", "lint/nursery/useEnumInitializers":"https://docs.rome.tools/lint/rules/useEnumInitializers", diff --git a/crates/rome_js_analyze/src/aria_analyzers/nursery.rs b/crates/rome_js_analyze/src/aria_analyzers/nursery.rs index 2653da16241..20703f3e60a 100644 --- a/crates/rome_js_analyze/src/aria_analyzers/nursery.rs +++ b/crates/rome_js_analyze/src/aria_analyzers/nursery.rs @@ -3,4 +3,5 @@ use rome_analyze::declare_group; mod use_aria_prop_types; mod use_aria_props_for_role; -declare_group! { pub (crate) Nursery { name : "nursery" , rules : [self :: use_aria_prop_types :: UseAriaPropTypes , self :: use_aria_props_for_role :: UseAriaPropsForRole ,] } } +mod use_valid_lang; +declare_group! { pub (crate) Nursery { name : "nursery" , rules : [self :: use_aria_prop_types :: UseAriaPropTypes , self :: use_aria_props_for_role :: UseAriaPropsForRole , self :: use_valid_lang :: UseValidLang ,] } } diff --git a/crates/rome_js_analyze/src/aria_analyzers/nursery/use_valid_lang.rs b/crates/rome_js_analyze/src/aria_analyzers/nursery/use_valid_lang.rs new file mode 100644 index 00000000000..b8f7a6c207a --- /dev/null +++ b/crates/rome_js_analyze/src/aria_analyzers/nursery/use_valid_lang.rs @@ -0,0 +1,129 @@ +use crate::aria_services::Aria; +use rome_analyze::context::RuleContext; +use rome_analyze::{declare_rule, Rule, RuleDiagnostic}; +use rome_console::markup; +use rome_js_syntax::jsx_ext::AnyJsxElement; +use rome_rowan::{AstNode, TextRange}; +declare_rule! { + /// Ensure that the attribute passed to the `lang` attribute is a correct ISO language and/or country. + /// + /// ## Examples + /// + /// ### Invalid + /// + /// ```jsx,expect_diagnostic + /// + /// ``` + /// + /// ```jsx,expect_diagnostic + /// + /// ``` + /// + /// ### Valid + /// + /// ```jsx + /// + /// ``` + pub(crate) UseValidLang { + version: "12.0.0", + name: "useValidLang", + recommended: true, + } +} + +enum ErrorKind { + InvalidLanguage, + InvalidCountry, + InvalidValue, +} + +pub(crate) struct UseValidLangState { + error_kind: ErrorKind, + attribute_range: TextRange, +} + +impl Rule for UseValidLang { + type Query = Aria; + type State = UseValidLangState; + type Signals = Option; + type Options = (); + + fn run(ctx: &RuleContext) -> Self::Signals { + let node = ctx.query(); + let element_text = node.name().ok()?.as_jsx_name()?.value_token().ok()?; + if element_text.text_trimmed() == "html" { + let attribute = node.find_attribute_by_name("lang")?; + let attribute_value = attribute.initializer()?.value().ok()?; + let attribute_text = attribute_value.inner_text_value().ok()??; + let mut split_value = attribute_text.text().split('-'); + match (split_value.next(), split_value.next()) { + (Some(language), Some(country)) => { + if !ctx.is_valid_language(language) { + return Some(UseValidLangState { + attribute_range: attribute_value.range(), + error_kind: ErrorKind::InvalidLanguage, + }); + } else if !ctx.is_valid_country(country) { + return Some(UseValidLangState { + attribute_range: attribute_value.range(), + error_kind: ErrorKind::InvalidCountry, + }); + } + } + + (Some(language), None) => { + if !ctx.is_valid_language(language) { + return Some(UseValidLangState { + attribute_range: attribute_value.range(), + error_kind: ErrorKind::InvalidLanguage, + }); + } + } + _ => { + if split_value.next().is_some() { + return Some(UseValidLangState { + attribute_range: attribute_value.range(), + error_kind: ErrorKind::InvalidValue, + }); + } + } + } + } + + None + } + + fn diagnostic(ctx: &RuleContext, state: &Self::State) -> Option { + let mut diagnostic = RuleDiagnostic::new( + rule_category!(), + state.attribute_range, + markup! { + "Provide a valid value for the ""lang"" attribute." + }, + ); + diagnostic = match state.error_kind { + ErrorKind::InvalidLanguage => { + let languages = ctx.iso_language_list(); + let languages = if languages.len() > 15 { + &languages[..15] + } else { + languages + }; + + diagnostic.footer_list("Some of valid languages:", languages) + } + ErrorKind::InvalidCountry => { + let countries = ctx.iso_country_list(); + let countries = if countries.len() > 15 { + &countries[..15] + } else { + countries + }; + + diagnostic.footer_list("Some of valid countries:", countries) + } + ErrorKind::InvalidValue => diagnostic, + };y + Some(diagnostic) + } +} diff --git a/crates/rome_js_analyze/src/aria_services.rs b/crates/rome_js_analyze/src/aria_services.rs index 049144a2b19..2a9f69ad38e 100644 --- a/crates/rome_js_analyze/src/aria_services.rs +++ b/crates/rome_js_analyze/src/aria_services.rs @@ -2,7 +2,8 @@ use rome_analyze::{ FromServices, MissingServicesDiagnostic, Phase, Phases, QueryKey, QueryMatch, Queryable, RuleKey, ServiceBag, }; -use rome_aria::{AriaProperties, AriaRoles}; +use rome_aria::iso::{countries, is_valid_country, is_valid_language, languages}; +use rome_aria::{AriaIso, AriaProperties, AriaRoles}; use rome_js_syntax::JsLanguage; use rome_rowan::AstNode; use std::sync::Arc; @@ -21,6 +22,22 @@ impl AriaServices { pub fn aria_properties(&self) -> &AriaProperties { &self.properties } + + pub fn is_valid_iso_language(&self, language: &str) -> bool { + is_valid_language(language) + } + + pub fn is_valid_iso_country(&self, country: &str) -> bool { + is_valid_country(country) + } + + pub fn iso_country_list(&self) -> &'static [&'static str] { + countries() + } + + pub fn iso_language_list(&self) -> &'static [&'static str] { + languages() + } } impl FromServices for AriaServices { @@ -34,9 +51,13 @@ impl FromServices for AriaServices { let properties: &Arc = services.get_service().ok_or_else(|| { MissingServicesDiagnostic::new(rule_key.rule_name(), &["AriaProperties"]) })?; + let iso: &Arc = services + .get_service() + .ok_or_else(|| MissingServicesDiagnostic::new(rule_key.rule_name(), &["AriaIso"]))?; Ok(Self { roles: roles.clone(), properties: properties.clone(), + iso: iso.clone(), }) } } diff --git a/crates/rome_js_analyze/src/lib.rs b/crates/rome_js_analyze/src/lib.rs index 5129656a82e..a79b82b2f88 100644 --- a/crates/rome_js_analyze/src/lib.rs +++ b/crates/rome_js_analyze/src/lib.rs @@ -9,7 +9,7 @@ use rome_analyze::{ DeserializableRuleOptions, InspectMatcher, LanguageRoot, MatchQueryParams, MetadataRegistry, Phases, RuleAction, RuleRegistry, ServiceBag, SuppressionKind, SyntaxVisitor, }; -use rome_aria::{AriaProperties, AriaRoles}; +use rome_aria::{AriaIso, AriaProperties, AriaRoles}; use rome_diagnostics::{category, Diagnostic, FileId}; use rome_js_syntax::suppression::SuppressionDiagnostic; use rome_js_syntax::{suppression::parse_suppression_comment, JsLanguage}; @@ -174,6 +174,7 @@ where services.insert_service(Arc::new(AriaRoles::default())); services.insert_service(Arc::new(AriaProperties::default())); + services.insert_service(Arc::new(AriaIso::default())); analyzer.run(AnalyzerContext { file_id, root: root.clone(), @@ -226,7 +227,7 @@ mod tests { String::from_utf8(buffer).unwrap() } - const SOURCE: &str = r#""#; + const SOURCE: &str = r#" ;"#; let parsed = parse(SOURCE, FileId::zero(), SourceType::jsx()); diff --git a/crates/rome_js_analyze/tests/specs/nursery/useValidLang.jsx b/crates/rome_js_analyze/tests/specs/nursery/useValidLang.jsx new file mode 100644 index 00000000000..604f72fad44 --- /dev/null +++ b/crates/rome_js_analyze/tests/specs/nursery/useValidLang.jsx @@ -0,0 +1,9 @@ +// invalid +let a = ; +let a = ; + +// valid +let a = ; +let a = ; +let a = ; +let a = ; diff --git a/crates/rome_js_analyze/tests/specs/nursery/useValidLang.jsx.snap b/crates/rome_js_analyze/tests/specs/nursery/useValidLang.jsx.snap new file mode 100644 index 00000000000..7e3cf1baf73 --- /dev/null +++ b/crates/rome_js_analyze/tests/specs/nursery/useValidLang.jsx.snap @@ -0,0 +1,85 @@ +--- +source: crates/rome_js_analyze/tests/spec_tests.rs +expression: useValidLang.jsx +--- +# Input +```js +// invalid +let a = ; +let a = ; + +// valid +let a = ; +let a = ; +let a = ; +let a = ; + +``` + +# Diagnostics +``` +useValidLang.jsx:2:20 lint/nursery/useValidLang ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Provide a valid value for the lang attribute. + + 1 │ // invalid + > 2 │ let a = ; + │ ^^^^^^^ + 3 │ let a = ; + 4 │ + + i Some of valid languages: + + - ab + - aa + - af + - sq + - am + - ar + - an + - hy + - as + - ay + - az + - ba + - eu + - bn + - dz + + +``` + +``` +useValidLang.jsx:3:20 lint/nursery/useValidLang ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Provide a valid value for the lang attribute. + + 1 │ // invalid + 2 │ let a = ; + > 3 │ let a = ; + │ ^^^^^^^^^^ + 4 │ + 5 │ // valid + + i Some of valid countries: + + - AF + - AL + - DZ + - AS + - AD + - AO + - AI + - AQ + - AG + - AR + - AM + - AW + - AU + - AT + - AZ + + +``` + + diff --git a/crates/rome_js_syntax/src/jsx_ext.rs b/crates/rome_js_syntax/src/jsx_ext.rs index c07d24d36e3..9f468b8e391 100644 --- a/crates/rome_js_syntax/src/jsx_ext.rs +++ b/crates/rome_js_syntax/src/jsx_ext.rs @@ -1,8 +1,9 @@ use std::collections::HashSet; use crate::{ - AnyJsxAttribute, AnyJsxElementName, JsSyntaxToken, JsxAttribute, JsxAttributeList, JsxName, - JsxOpeningElement, JsxSelfClosingElement, JsxString, TextSize, + AnyJsExpression, AnyJsLiteralExpression, AnyJsxAttribute, AnyJsxAttributeValue, + AnyJsxElementName, JsSyntaxToken, JsxAttribute, JsxAttributeList, JsxName, JsxOpeningElement, + JsxSelfClosingElement, JsxString, TextSize, }; use rome_rowan::{declare_node_union, AstNode, AstNodeList, SyntaxResult, SyntaxTokenText}; @@ -409,3 +410,59 @@ impl JsxAttribute { .unwrap_or(false) } } + +impl AnyJsxAttributeValue { + /// Retrieves the text value of the attribute + /// + /// If the attribute is not a text or a text-like node, [Node] is returned. + /// + /// ## Examples + /// + /// ``` + /// use rome_js_factory::make::{ident, js_string_literal_expression, jsx_attribute, jsx_attribute_initializer_clause, jsx_expression_attribute_value, jsx_name, jsx_string, token}; + /// use rome_js_syntax::{AnyJsExpression, AnyJsLiteralExpression, AnyJsxAttributeName, AnyJsxAttributeValue, T}; + /// let attribute = AnyJsxAttributeValue::JsxString( + /// jsx_string(ident("en")) + /// ); + /// assert_eq!(attribute.inner_text_value().unwrap().unwrap(), "en"); + /// let attribute = AnyJsxAttributeValue::JsxExpressionAttributeValue( + /// jsx_expression_attribute_value( + /// token(T!['{']), + /// AnyJsExpression::AnyJsLiteralExpression( + /// AnyJsLiteralExpression::JsStringLiteralExpression( + /// js_string_literal_expression(ident("en")) + /// ) + /// ), + /// token(T!['}']), + /// ) + /// ); + /// assert_eq!(attribute.inner_text_value().unwrap().unwrap(), "en"); + /// ``` + pub fn inner_text_value(&self) -> SyntaxResult> { + let result = match self { + AnyJsxAttributeValue::JsxString(string) => Some(string.inner_string_text()?), + AnyJsxAttributeValue::JsxExpressionAttributeValue(expression) => { + match expression.expression()? { + AnyJsExpression::JsTemplateExpression(template) => { + template.elements().iter().next().and_then(|chunk| { + Some( + chunk + .as_js_template_chunk_element()? + .template_chunk_token() + .ok()? + .token_text_trimmed(), + ) + }) + } + AnyJsExpression::AnyJsLiteralExpression( + AnyJsLiteralExpression::JsStringLiteralExpression(string), + ) => Some(string.inner_string_text()?), + _ => None, + } + } + _ => return Ok(None), + }; + + Ok(result) + } +} diff --git a/editors/vscode/configuration_schema.json b/editors/vscode/configuration_schema.json index 56c699ae22b..fa4d5c51d3f 100644 --- a/editors/vscode/configuration_schema.json +++ b/editors/vscode/configuration_schema.json @@ -990,6 +990,17 @@ "type": "null" } ] + }, + "useValidLang": { + "description": "Ensure that the attribute passed to the lang attribute is a correct ISO language and/or country.", + "anyOf": [ + { + "$ref": "#/definitions/RuleConfiguration" + }, + { + "type": "null" + } + ] } } }, diff --git a/npm/backend-jsonrpc/src/workspace.ts b/npm/backend-jsonrpc/src/workspace.ts index 5463b2a33e0..ffb6f417638 100644 --- a/npm/backend-jsonrpc/src/workspace.ts +++ b/npm/backend-jsonrpc/src/workspace.ts @@ -437,6 +437,10 @@ export interface Nursery { * Disallow parseInt() and Number.parseInt() in favor of binary, octal, and hexadecimal literals */ useNumericLiterals?: RuleConfiguration; + /** + * Ensure that the attribute passed to the lang attribute is a correct ISO language and/or country. + */ + useValidLang?: RuleConfiguration; } /** * A list of rules that belong to this group @@ -736,6 +740,7 @@ export type Category = | "lint/nursery/useAriaPropTypes" | "lint/nursery/useCamelCase" | "lint/nursery/useConst" + | "lint/nursery/useValidLang" | "lint/nursery/useDefaultParameterLast" | "lint/nursery/useDefaultSwitchClauseLast" | "lint/nursery/useEnumInitializers" diff --git a/npm/rome/configuration_schema.json b/npm/rome/configuration_schema.json index 56c699ae22b..fa4d5c51d3f 100644 --- a/npm/rome/configuration_schema.json +++ b/npm/rome/configuration_schema.json @@ -990,6 +990,17 @@ "type": "null" } ] + }, + "useValidLang": { + "description": "Ensure that the attribute passed to the lang attribute is a correct ISO language and/or country.", + "anyOf": [ + { + "$ref": "#/definitions/RuleConfiguration" + }, + { + "type": "null" + } + ] } } }, diff --git a/website/src/pages/lint/rules/index.mdx b/website/src/pages/lint/rules/index.mdx index 0ebdb9c92d3..11962347c84 100644 --- a/website/src/pages/lint/rules/index.mdx +++ b/website/src/pages/lint/rules/index.mdx @@ -698,5 +698,11 @@ component functions. Disallow parseInt() and Number.parseInt() in favor of binary, octal, and hexadecimal literals +
+

+ useValidLang +

+Ensure that the attribute passed to the lang attribute is a correct ISO language and/or country. +
diff --git a/website/src/pages/lint/rules/useValidLang.md b/website/src/pages/lint/rules/useValidLang.md new file mode 100644 index 00000000000..a119ae9eca2 --- /dev/null +++ b/website/src/pages/lint/rules/useValidLang.md @@ -0,0 +1,83 @@ +--- +title: Lint Rule useValidLang +parent: lint/rules/index +--- + +# useValidLang (since v12.0.0) + +Ensure that the attribute passed to the `lang` attribute is a correct ISO language and/or country. + +## Examples + +### Invalid + +```jsx + +``` + +
nursery/useValidLang.js:1:12 lint/nursery/useValidLang ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+   Provide a valid value for the lang attribute.
+  
+  > 1 │ <html lang="lorem" />
+              ^^^^^^^
+    2 │ 
+  
+   Some of valid languages:
+  
+  - ab
+  - aa
+  - af
+  - sq
+  - am
+  - ar
+  - an
+  - hy
+  - as
+  - ay
+  - az
+  - ba
+  - eu
+  - bn
+  - dz
+  
+
+ +```jsx + +``` + +
nursery/useValidLang.js:1:12 lint/nursery/useValidLang ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+   Provide a valid value for the lang attribute.
+  
+  > 1 │ <html lang="en-babab" />
+              ^^^^^^^^^^
+    2 │ 
+  
+   Some of valid countries:
+  
+  - AF
+  - AL
+  - DZ
+  - AS
+  - AD
+  - AO
+  - AI
+  - AQ
+  - AG
+  - AR
+  - AM
+  - AW
+  - AU
+  - AT
+  - AZ
+  
+
+ +### Valid + +```jsx + +``` + From a86de077261143dad504572746043e5854184bf0 Mon Sep 17 00:00:00 2001 From: ematipico Date: Fri, 16 Dec 2022 08:41:43 +0000 Subject: [PATCH 2/3] chore: code suggestions --- crates/rome_aria/src/lib.rs | 1 - .../aria_analyzers/nursery/use_valid_lang.rs | 48 ++++++++++--------- crates/rome_js_analyze/src/aria_services.rs | 6 +-- crates/rome_js_analyze/src/lib.rs | 3 +- .../tests/specs/nursery/useValidLang.jsx | 1 + .../tests/specs/nursery/useValidLang.jsx.snap | 22 +++++++-- .../src/configuration/linter/rules.rs | 31 +++++------- website/src/pages/lint/rules/useValidLang.md | 14 ++++++ 8 files changed, 74 insertions(+), 52 deletions(-) diff --git a/crates/rome_aria/src/lib.rs b/crates/rome_aria/src/lib.rs index 71c1db2006a..00007b9bc51 100644 --- a/crates/rome_aria/src/lib.rs +++ b/crates/rome_aria/src/lib.rs @@ -5,7 +5,6 @@ mod macros; pub mod properties; pub mod roles; -pub use iso::AriaIso; pub use properties::AriaProperties; pub(crate) use roles::AriaRoleDefinition; pub use roles::AriaRoles; diff --git a/crates/rome_js_analyze/src/aria_analyzers/nursery/use_valid_lang.rs b/crates/rome_js_analyze/src/aria_analyzers/nursery/use_valid_lang.rs index b8f7a6c207a..5b2099df7de 100644 --- a/crates/rome_js_analyze/src/aria_analyzers/nursery/use_valid_lang.rs +++ b/crates/rome_js_analyze/src/aria_analyzers/nursery/use_valid_lang.rs @@ -19,6 +19,10 @@ declare_rule! { /// /// ``` /// + /// ```jsx,expect_diagnostic + /// + /// ``` + /// /// ### Valid /// /// ```jsx @@ -31,14 +35,14 @@ declare_rule! { } } -enum ErrorKind { - InvalidLanguage, - InvalidCountry, - InvalidValue, +enum InvalidKind { + Language, + Country, + Value, } pub(crate) struct UseValidLangState { - error_kind: ErrorKind, + invalid_kind: InvalidKind, attribute_range: TextRange, } @@ -58,35 +62,33 @@ impl Rule for UseValidLang { let mut split_value = attribute_text.text().split('-'); match (split_value.next(), split_value.next()) { (Some(language), Some(country)) => { - if !ctx.is_valid_language(language) { + if !ctx.is_valid_iso_language(language) { return Some(UseValidLangState { attribute_range: attribute_value.range(), - error_kind: ErrorKind::InvalidLanguage, + invalid_kind: InvalidKind::Language, }); - } else if !ctx.is_valid_country(country) { + } else if !ctx.is_valid_iso_country(country) { return Some(UseValidLangState { attribute_range: attribute_value.range(), - error_kind: ErrorKind::InvalidCountry, + invalid_kind: InvalidKind::Country, }); - } - } - - (Some(language), None) => { - if !ctx.is_valid_language(language) { + } else if split_value.next().is_some() { return Some(UseValidLangState { attribute_range: attribute_value.range(), - error_kind: ErrorKind::InvalidLanguage, + invalid_kind: InvalidKind::Value, }); } } - _ => { - if split_value.next().is_some() { + + (Some(language), None) => { + if !ctx.is_valid_iso_language(language) { return Some(UseValidLangState { attribute_range: attribute_value.range(), - error_kind: ErrorKind::InvalidValue, + invalid_kind: InvalidKind::Language, }); } } + _ => {} } } @@ -101,8 +103,8 @@ impl Rule for UseValidLang { "Provide a valid value for the ""lang"" attribute." }, ); - diagnostic = match state.error_kind { - ErrorKind::InvalidLanguage => { + diagnostic = match state.invalid_kind { + InvalidKind::Language => { let languages = ctx.iso_language_list(); let languages = if languages.len() > 15 { &languages[..15] @@ -112,7 +114,7 @@ impl Rule for UseValidLang { diagnostic.footer_list("Some of valid languages:", languages) } - ErrorKind::InvalidCountry => { + InvalidKind::Country => { let countries = ctx.iso_country_list(); let countries = if countries.len() > 15 { &countries[..15] @@ -122,8 +124,8 @@ impl Rule for UseValidLang { diagnostic.footer_list("Some of valid countries:", countries) } - ErrorKind::InvalidValue => diagnostic, - };y + InvalidKind::Value => diagnostic, + }; Some(diagnostic) } } diff --git a/crates/rome_js_analyze/src/aria_services.rs b/crates/rome_js_analyze/src/aria_services.rs index 2a9f69ad38e..942aa6b606b 100644 --- a/crates/rome_js_analyze/src/aria_services.rs +++ b/crates/rome_js_analyze/src/aria_services.rs @@ -3,7 +3,7 @@ use rome_analyze::{ RuleKey, ServiceBag, }; use rome_aria::iso::{countries, is_valid_country, is_valid_language, languages}; -use rome_aria::{AriaIso, AriaProperties, AriaRoles}; +use rome_aria::{AriaProperties, AriaRoles}; use rome_js_syntax::JsLanguage; use rome_rowan::AstNode; use std::sync::Arc; @@ -51,13 +51,9 @@ impl FromServices for AriaServices { let properties: &Arc = services.get_service().ok_or_else(|| { MissingServicesDiagnostic::new(rule_key.rule_name(), &["AriaProperties"]) })?; - let iso: &Arc = services - .get_service() - .ok_or_else(|| MissingServicesDiagnostic::new(rule_key.rule_name(), &["AriaIso"]))?; Ok(Self { roles: roles.clone(), properties: properties.clone(), - iso: iso.clone(), }) } } diff --git a/crates/rome_js_analyze/src/lib.rs b/crates/rome_js_analyze/src/lib.rs index a79b82b2f88..aa50e0f01d1 100644 --- a/crates/rome_js_analyze/src/lib.rs +++ b/crates/rome_js_analyze/src/lib.rs @@ -9,7 +9,7 @@ use rome_analyze::{ DeserializableRuleOptions, InspectMatcher, LanguageRoot, MatchQueryParams, MetadataRegistry, Phases, RuleAction, RuleRegistry, ServiceBag, SuppressionKind, SyntaxVisitor, }; -use rome_aria::{AriaIso, AriaProperties, AriaRoles}; +use rome_aria::{AriaProperties, AriaRoles}; use rome_diagnostics::{category, Diagnostic, FileId}; use rome_js_syntax::suppression::SuppressionDiagnostic; use rome_js_syntax::{suppression::parse_suppression_comment, JsLanguage}; @@ -174,7 +174,6 @@ where services.insert_service(Arc::new(AriaRoles::default())); services.insert_service(Arc::new(AriaProperties::default())); - services.insert_service(Arc::new(AriaIso::default())); analyzer.run(AnalyzerContext { file_id, root: root.clone(), diff --git a/crates/rome_js_analyze/tests/specs/nursery/useValidLang.jsx b/crates/rome_js_analyze/tests/specs/nursery/useValidLang.jsx index 604f72fad44..40ae2babad2 100644 --- a/crates/rome_js_analyze/tests/specs/nursery/useValidLang.jsx +++ b/crates/rome_js_analyze/tests/specs/nursery/useValidLang.jsx @@ -1,6 +1,7 @@ // invalid let a = ; let a = ; +let a = ; // valid let a = ; diff --git a/crates/rome_js_analyze/tests/specs/nursery/useValidLang.jsx.snap b/crates/rome_js_analyze/tests/specs/nursery/useValidLang.jsx.snap index 7e3cf1baf73..6265fbedaa4 100644 --- a/crates/rome_js_analyze/tests/specs/nursery/useValidLang.jsx.snap +++ b/crates/rome_js_analyze/tests/specs/nursery/useValidLang.jsx.snap @@ -7,6 +7,7 @@ expression: useValidLang.jsx // invalid let a = ; let a = ; +let a = ; // valid let a = ; @@ -26,7 +27,7 @@ useValidLang.jsx:2:20 lint/nursery/useValidLang ━━━━━━━━━━ > 2 │ let a = ; │ ^^^^^^^ 3 │ let a = ; - 4 │ + 4 │ let a = ; i Some of valid languages: @@ -58,8 +59,8 @@ useValidLang.jsx:3:20 lint/nursery/useValidLang ━━━━━━━━━━ 2 │ let a = ; > 3 │ let a = ; │ ^^^^^^^^^^ - 4 │ - 5 │ // valid + 4 │ let a = ; + 5 │ i Some of valid countries: @@ -82,4 +83,19 @@ useValidLang.jsx:3:20 lint/nursery/useValidLang ━━━━━━━━━━ ``` +``` +useValidLang.jsx:4:20 lint/nursery/useValidLang ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Provide a valid value for the lang attribute. + + 2 │ let a = ; + 3 │ let a = ; + > 4 │ let a = ; + │ ^^^^^^^^^^^^^^^^^ + 5 │ + 6 │ // valid + + +``` + diff --git a/crates/rome_service/src/configuration/linter/rules.rs b/crates/rome_service/src/configuration/linter/rules.rs index 7ce52e1a462..d614ffbb99f 100644 --- a/crates/rome_service/src/configuration/linter/rules.rs +++ b/crates/rome_service/src/configuration/linter/rules.rs @@ -729,8 +729,6 @@ struct NurserySchema { no_assign_in_expressions: Option, #[doc = "Disallow certain types."] no_banned_types: Option, - #[doc = "Disallow reassigning class members."] - no_class_assign: Option, #[doc = "Disallow comma operator."] no_comma_operator: Option, #[doc = "Disallow TypeScript const enum"] @@ -759,8 +757,6 @@ struct NurserySchema { no_redundant_use_strict: Option, #[doc = "This rule allows you to specify global variable names that you don’t want to use in your application."] no_restricted_globals: Option, - #[doc = "Disallow comparisons where both sides are exactly the same."] - no_self_compare: Option, #[doc = "Disallow returning a value from a setter"] no_setter_return: Option, #[doc = "Disallow comparison of expressions modifying the string case with non-compliant value."] @@ -797,14 +793,15 @@ struct NurserySchema { use_hook_at_top_level: Option, #[doc = "Disallow parseInt() and Number.parseInt() in favor of binary, octal, and hexadecimal literals"] use_numeric_literals: Option, + #[doc = "Ensure that the attribute passed to the lang attribute is a correct ISO language and/or country."] + use_valid_lang: Option, } impl Nursery { const CATEGORY_NAME: &'static str = "nursery"; - pub(crate) const CATEGORY_RULES: [&'static str; 37] = [ + pub(crate) const CATEGORY_RULES: [&'static str; 36] = [ "noAccessKey", "noAssignInExpressions", "noBannedTypes", - "noClassAssign", "noCommaOperator", "noConstEnum", "noConstructorReturn", @@ -819,7 +816,6 @@ impl Nursery { "noRedundantAlt", "noRedundantUseStrict", "noRestrictedGlobals", - "noSelfCompare", "noSetterReturn", "noStringCaseMismatch", "noUnsafeFinally", @@ -838,11 +834,11 @@ impl Nursery { "useExponentiationOperator", "useHookAtTopLevel", "useNumericLiterals", + "useValidLang", ]; - const RECOMMENDED_RULES: [&'static str; 28] = [ + const RECOMMENDED_RULES: [&'static str; 27] = [ "noAssignInExpressions", "noBannedTypes", - "noClassAssign", "noCommaOperator", "noConstEnum", "noConstructorReturn", @@ -853,7 +849,6 @@ impl Nursery { "noHeaderScope", "noInvalidConstructorSuper", "noRedundantAlt", - "noSelfCompare", "noSetterReturn", "noStringCaseMismatch", "noUnsafeFinally", @@ -868,8 +863,9 @@ impl Nursery { "useEnumInitializers", "useExhaustiveDependencies", "useNumericLiterals", + "useValidLang", ]; - const RECOMMENDED_RULES_AS_FILTERS: [RuleFilter<'static>; 28] = [ + const RECOMMENDED_RULES_AS_FILTERS: [RuleFilter<'static>; 27] = [ RuleFilter::Rule("nursery", Self::CATEGORY_RULES[1]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[2]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[3]), @@ -881,23 +877,22 @@ impl Nursery { RuleFilter::Rule("nursery", Self::CATEGORY_RULES[9]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[10]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[11]), - RuleFilter::Rule("nursery", Self::CATEGORY_RULES[12]), - RuleFilter::Rule("nursery", Self::CATEGORY_RULES[15]), + RuleFilter::Rule("nursery", Self::CATEGORY_RULES[14]), + RuleFilter::Rule("nursery", Self::CATEGORY_RULES[17]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[18]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[19]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[20]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[21]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[22]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[23]), - RuleFilter::Rule("nursery", Self::CATEGORY_RULES[24]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[25]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[27]), + RuleFilter::Rule("nursery", Self::CATEGORY_RULES[28]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[29]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[30]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[31]), - RuleFilter::Rule("nursery", Self::CATEGORY_RULES[32]), - RuleFilter::Rule("nursery", Self::CATEGORY_RULES[33]), - RuleFilter::Rule("nursery", Self::CATEGORY_RULES[36]), + RuleFilter::Rule("nursery", Self::CATEGORY_RULES[34]), + RuleFilter::Rule("nursery", Self::CATEGORY_RULES[35]), ]; pub(crate) fn is_recommended(&self) -> bool { !matches!(self.recommended, Some(false)) } pub(crate) fn get_enabled_rules(&self) -> IndexSet { @@ -924,7 +919,7 @@ impl Nursery { pub(crate) fn is_recommended_rule(rule_name: &str) -> bool { Self::RECOMMENDED_RULES.contains(&rule_name) } - pub(crate) fn recommended_rules_as_filters() -> [RuleFilter<'static>; 28] { + pub(crate) fn recommended_rules_as_filters() -> [RuleFilter<'static>; 27] { Self::RECOMMENDED_RULES_AS_FILTERS } } diff --git a/website/src/pages/lint/rules/useValidLang.md b/website/src/pages/lint/rules/useValidLang.md index a119ae9eca2..f729cd1e367 100644 --- a/website/src/pages/lint/rules/useValidLang.md +++ b/website/src/pages/lint/rules/useValidLang.md @@ -75,6 +75,20 @@ Ensure that the attribute passed to the `lang` attribute is a correct ISO langua +```jsx + +``` + +
nursery/useValidLang.js:1:12 lint/nursery/useValidLang ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+   Provide a valid value for the lang attribute.
+  
+  > 1 │ <html lang="en-GB-typo" />
+              ^^^^^^^^^^^^
+    2 │ 
+  
+
+ ### Valid ```jsx From 8bb66dedf8269ceadcf92a5f166b7ccbcdddcfc3 Mon Sep 17 00:00:00 2001 From: ematipico Date: Mon, 19 Dec 2022 11:10:23 +0000 Subject: [PATCH 3/3] chore: rebase --- .../src/configuration/linter/rules.rs | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/crates/rome_service/src/configuration/linter/rules.rs b/crates/rome_service/src/configuration/linter/rules.rs index d614ffbb99f..1cf0d1af32a 100644 --- a/crates/rome_service/src/configuration/linter/rules.rs +++ b/crates/rome_service/src/configuration/linter/rules.rs @@ -729,6 +729,8 @@ struct NurserySchema { no_assign_in_expressions: Option, #[doc = "Disallow certain types."] no_banned_types: Option, + #[doc = "Disallow reassigning class members."] + no_class_assign: Option, #[doc = "Disallow comma operator."] no_comma_operator: Option, #[doc = "Disallow TypeScript const enum"] @@ -757,6 +759,8 @@ struct NurserySchema { no_redundant_use_strict: Option, #[doc = "This rule allows you to specify global variable names that you don’t want to use in your application."] no_restricted_globals: Option, + #[doc = "Disallow comparisons where both sides are exactly the same."] + no_self_compare: Option, #[doc = "Disallow returning a value from a setter"] no_setter_return: Option, #[doc = "Disallow comparison of expressions modifying the string case with non-compliant value."] @@ -798,10 +802,11 @@ struct NurserySchema { } impl Nursery { const CATEGORY_NAME: &'static str = "nursery"; - pub(crate) const CATEGORY_RULES: [&'static str; 36] = [ + pub(crate) const CATEGORY_RULES: [&'static str; 38] = [ "noAccessKey", "noAssignInExpressions", "noBannedTypes", + "noClassAssign", "noCommaOperator", "noConstEnum", "noConstructorReturn", @@ -816,6 +821,7 @@ impl Nursery { "noRedundantAlt", "noRedundantUseStrict", "noRestrictedGlobals", + "noSelfCompare", "noSetterReturn", "noStringCaseMismatch", "noUnsafeFinally", @@ -836,9 +842,10 @@ impl Nursery { "useNumericLiterals", "useValidLang", ]; - const RECOMMENDED_RULES: [&'static str; 27] = [ + const RECOMMENDED_RULES: [&'static str; 29] = [ "noAssignInExpressions", "noBannedTypes", + "noClassAssign", "noCommaOperator", "noConstEnum", "noConstructorReturn", @@ -849,6 +856,7 @@ impl Nursery { "noHeaderScope", "noInvalidConstructorSuper", "noRedundantAlt", + "noSelfCompare", "noSetterReturn", "noStringCaseMismatch", "noUnsafeFinally", @@ -865,7 +873,7 @@ impl Nursery { "useNumericLiterals", "useValidLang", ]; - const RECOMMENDED_RULES_AS_FILTERS: [RuleFilter<'static>; 27] = [ + const RECOMMENDED_RULES_AS_FILTERS: [RuleFilter<'static>; 29] = [ RuleFilter::Rule("nursery", Self::CATEGORY_RULES[1]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[2]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[3]), @@ -877,22 +885,24 @@ impl Nursery { RuleFilter::Rule("nursery", Self::CATEGORY_RULES[9]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[10]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[11]), - RuleFilter::Rule("nursery", Self::CATEGORY_RULES[14]), - RuleFilter::Rule("nursery", Self::CATEGORY_RULES[17]), + RuleFilter::Rule("nursery", Self::CATEGORY_RULES[12]), + RuleFilter::Rule("nursery", Self::CATEGORY_RULES[15]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[18]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[19]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[20]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[21]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[22]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[23]), + RuleFilter::Rule("nursery", Self::CATEGORY_RULES[24]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[25]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[27]), - RuleFilter::Rule("nursery", Self::CATEGORY_RULES[28]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[29]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[30]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[31]), - RuleFilter::Rule("nursery", Self::CATEGORY_RULES[34]), - RuleFilter::Rule("nursery", Self::CATEGORY_RULES[35]), + RuleFilter::Rule("nursery", Self::CATEGORY_RULES[32]), + RuleFilter::Rule("nursery", Self::CATEGORY_RULES[33]), + RuleFilter::Rule("nursery", Self::CATEGORY_RULES[36]), + RuleFilter::Rule("nursery", Self::CATEGORY_RULES[37]), ]; pub(crate) fn is_recommended(&self) -> bool { !matches!(self.recommended, Some(false)) } pub(crate) fn get_enabled_rules(&self) -> IndexSet { @@ -919,7 +929,7 @@ impl Nursery { pub(crate) fn is_recommended_rule(rule_name: &str) -> bool { Self::RECOMMENDED_RULES.contains(&rule_name) } - pub(crate) fn recommended_rules_as_filters() -> [RuleFilter<'static>; 27] { + pub(crate) fn recommended_rules_as_filters() -> [RuleFilter<'static>; 29] { Self::RECOMMENDED_RULES_AS_FILTERS } }