From 5884f3b7e817decd55d64d3f73cd63b6cbcd7abd Mon Sep 17 00:00:00 2001 From: Victorien Elvinger Date: Thu, 27 Jul 2023 12:38:41 +0200 Subject: [PATCH] refactor(rome_syntax): imrpove inner_string_text --- .../src/analyzers/a11y/no_blank_target.rs | 5 +- .../src/analyzers/a11y/use_alt_text.rs | 2 +- .../nursery/no_control_characters_in_regex.rs | 7 +- .../src/analyzers/nursery/no_self_assign.rs | 27 ++- .../nursery/use_literal_enum_members.rs | 9 +- .../src/analyzers/nursery/use_literal_keys.rs | 2 +- .../analyzers/style/use_enum_initializers.rs | 9 +- .../suspicious/no_duplicate_class_members.rs | 8 +- .../suspicious/no_duplicate_object_keys.rs | 17 +- .../suspicious/no_prototype_builtins.rs | 2 +- crates/rome_js_analyze/src/aria_services.rs | 10 +- crates/rome_js_analyze/src/react.rs | 4 +- crates/rome_js_analyze/src/react/hooks.rs | 10 +- .../correctness/no_global_object_calls.rs | 2 +- .../nursery/use_naming_convention.rs | 58 +++--- .../style/no_shouty_constants.rs | 2 +- crates/rome_js_analyze/src/utils.rs | 4 +- crates/rome_js_factory/src/make.rs | 10 + .../src/js/expressions/call_arguments.rs | 78 ++++--- crates/rome_js_syntax/src/directive_ext.rs | 21 +- crates/rome_js_syntax/src/expr_ext.rs | 156 ++++++-------- crates/rome_js_syntax/src/import_ext.rs | 21 +- crates/rome_js_syntax/src/jsx_ext.rs | 28 +-- crates/rome_js_syntax/src/lib.rs | 33 +-- crates/rome_js_syntax/src/static_value.rs | 195 ++++++------------ crates/rome_json_syntax/src/lib.rs | 14 +- crates/rome_json_syntax/src/member_ext.rs | 21 +- crates/rome_json_syntax/src/string_ext.rs | 21 +- 28 files changed, 310 insertions(+), 466 deletions(-) diff --git a/crates/rome_js_analyze/src/analyzers/a11y/no_blank_target.rs b/crates/rome_js_analyze/src/analyzers/a11y/no_blank_target.rs index 86365ea71e5..8bef8f143fc 100644 --- a/crates/rome_js_analyze/src/analyzers/a11y/no_blank_target.rs +++ b/crates/rome_js_analyze/src/analyzers/a11y/no_blank_target.rs @@ -75,10 +75,7 @@ impl Rule for NoBlankTarget { let target_attribute = node.find_attribute_by_name("target")?; let rel_attribute = node.find_attribute_by_name("rel"); - if target_attribute - .as_static_value()? - .is_string_constant("_blank") - { + if target_attribute.as_static_value()?.text() == "_blank" { match rel_attribute { None => { if !node.has_trailing_spread_prop(target_attribute.clone()) { diff --git a/crates/rome_js_analyze/src/analyzers/a11y/use_alt_text.rs b/crates/rome_js_analyze/src/analyzers/a11y/use_alt_text.rs index 8b8228d1003..8abaf114883 100644 --- a/crates/rome_js_analyze/src/analyzers/a11y/use_alt_text.rs +++ b/crates/rome_js_analyze/src/analyzers/a11y/use_alt_text.rs @@ -146,7 +146,7 @@ fn has_type_image_attribute(element: &AnyJsxElement) -> bool { .map_or(false, |attribute| { attribute .as_static_value() - .map_or(false, |value| value.is_string_constant("image")) + .map_or(false, |value| value.text() == "image") }) } diff --git a/crates/rome_js_analyze/src/analyzers/nursery/no_control_characters_in_regex.rs b/crates/rome_js_analyze/src/analyzers/nursery/no_control_characters_in_regex.rs index 6c1c3888355..3f27f201791 100644 --- a/crates/rome_js_analyze/src/analyzers/nursery/no_control_characters_in_regex.rs +++ b/crates/rome_js_analyze/src/analyzers/nursery/no_control_characters_in_regex.rs @@ -175,12 +175,7 @@ fn collect_control_characters_from_expression( callee: &AnyJsExpression, js_call_arguments: &JsCallArguments, ) -> Option> { - let js_identifier = match callee { - AnyJsExpression::JsIdentifierExpression(js_identifier) => js_identifier, - _ => return None, - }; - - if js_identifier.name().ok()?.has_name("RegExp") { + if callee.as_js_reference_identifier()?.has_name("RegExp") { let mut args = js_call_arguments.args().iter(); let raw_pattern = args .next() diff --git a/crates/rome_js_analyze/src/analyzers/nursery/no_self_assign.rs b/crates/rome_js_analyze/src/analyzers/nursery/no_self_assign.rs index 0d202a9d179..3c07748555e 100644 --- a/crates/rome_js_analyze/src/analyzers/nursery/no_self_assign.rs +++ b/crates/rome_js_analyze/src/analyzers/nursery/no_self_assign.rs @@ -1,11 +1,11 @@ use rome_analyze::{context::RuleContext, declare_rule, Ast, Rule, RuleDiagnostic}; use rome_console::markup; use rome_js_syntax::{ - AnyJsArrayAssignmentPatternElement, AnyJsArrayElement, AnyJsAssignment, AnyJsAssignmentPattern, - AnyJsExpression, AnyJsLiteralExpression, AnyJsName, AnyJsObjectAssignmentPatternMember, - AnyJsObjectMember, JsAssignmentExpression, JsAssignmentOperator, JsCallExpression, - JsComputedMemberAssignment, JsComputedMemberExpression, JsIdentifierAssignment, - JsIdentifierExpression, JsLanguage, JsName, JsPrivateName, JsReferenceIdentifier, + inner_string_text, AnyJsArrayAssignmentPatternElement, AnyJsArrayElement, AnyJsAssignment, + AnyJsAssignmentPattern, AnyJsExpression, AnyJsLiteralExpression, AnyJsName, + AnyJsObjectAssignmentPatternMember, AnyJsObjectMember, JsAssignmentExpression, + JsAssignmentOperator, JsCallExpression, JsComputedMemberAssignment, JsComputedMemberExpression, + JsIdentifierAssignment, JsLanguage, JsName, JsPrivateName, JsReferenceIdentifier, JsStaticMemberAssignment, JsStaticMemberExpression, JsSyntaxToken, }; use rome_rowan::{ @@ -421,7 +421,9 @@ impl AnyJsAssignmentExpressionLikeIterator { fn from_computed_member_assignment(source: JsComputedMemberAssignment) -> SyntaxResult { Ok(Self { source_member: source.member().and_then(|expression| match expression { - AnyJsExpression::JsIdentifierExpression(node) => Ok(AnyNameLike::from(node)), + AnyJsExpression::JsIdentifierExpression(node) => { + Ok(AnyNameLike::from(node.name()?)) + } _ => Err(SyntaxError::MissingRequiredChild), })?, source_object: source.object()?, @@ -433,7 +435,9 @@ impl AnyJsAssignmentExpressionLikeIterator { fn from_computed_member_expression(source: JsComputedMemberExpression) -> SyntaxResult { Ok(Self { source_member: source.member().and_then(|expression| match expression { - AnyJsExpression::JsIdentifierExpression(node) => Ok(AnyNameLike::from(node)), + AnyJsExpression::JsIdentifierExpression(node) => { + Ok(AnyNameLike::from(node.name()?)) + } _ => Err(SyntaxError::MissingRequiredChild), })?, source_object: source.object()?, @@ -539,7 +543,7 @@ enum AnyAssignmentLike { } declare_node_union! { - pub(crate) AnyNameLike = AnyJsName | JsReferenceIdentifier | JsIdentifierExpression | AnyJsLiteralExpression + pub(crate) AnyNameLike = AnyJsName | JsReferenceIdentifier | AnyJsLiteralExpression } declare_node_union! { @@ -715,11 +719,6 @@ impl TryFrom<(AnyNameLike, AnyNameLike)> for IdentifiersLike { AnyNameLike::JsReferenceIdentifier(right), ) => Ok(Self::References(left, right)), - ( - AnyNameLike::JsIdentifierExpression(left), - AnyNameLike::JsIdentifierExpression(right), - ) => Ok(Self::References(left.name()?, right.name()?)), - ( AnyNameLike::AnyJsLiteralExpression(left), AnyNameLike::AnyJsLiteralExpression(right), @@ -808,7 +807,7 @@ fn with_same_identifiers(identifiers_like: &IdentifiersLike) -> Option<()> { }, }; - if left_value.text_trimmed() == right_value.text_trimmed() { + if inner_string_text(&left_value) == inner_string_text(&right_value) { Some(()) } else { None diff --git a/crates/rome_js_analyze/src/analyzers/nursery/use_literal_enum_members.rs b/crates/rome_js_analyze/src/analyzers/nursery/use_literal_enum_members.rs index 0c5ce3f18cf..17fb4af8118 100644 --- a/crates/rome_js_analyze/src/analyzers/nursery/use_literal_enum_members.rs +++ b/crates/rome_js_analyze/src/analyzers/nursery/use_literal_enum_members.rs @@ -103,7 +103,7 @@ impl Rule for UseLiteralEnumMembers { }; if let Ok(name) = enum_member.name() { if let Some(name) = name.name() { - enum_member_names.insert(name.text().to_string()); + enum_member_names.insert(name.to_string()); } } } @@ -208,11 +208,8 @@ fn is_enum_member_reference( (move || { // Allow reference to previous member name namespaced by the enum name let object = expr.object().ok()?.omit_parentheses(); - let object = object.as_js_identifier_expression()?; - Some( - object.name().ok()?.has_name(enum_name) - && enum_member_names.contains(expr.member_name()?.text()), - ) + let object = object.as_js_reference_identifier()?; + Some(object.has_name(enum_name) && enum_member_names.contains(expr.member_name()?.text())) })() .unwrap_or_default() } diff --git a/crates/rome_js_analyze/src/analyzers/nursery/use_literal_keys.rs b/crates/rome_js_analyze/src/analyzers/nursery/use_literal_keys.rs index 999d9945981..5f9a530c6ec 100644 --- a/crates/rome_js_analyze/src/analyzers/nursery/use_literal_keys.rs +++ b/crates/rome_js_analyze/src/analyzers/nursery/use_literal_keys.rs @@ -65,7 +65,7 @@ impl Rule for UseLiteralKeys { if member.value().ok()?.kind() == JsSyntaxKind::JS_STRING_LITERAL { let name = member.name().ok()?; if is_js_ident(&name) { - return Some((member.range(), name)); + return Some((member.range(), name.to_string())); } } return None; diff --git a/crates/rome_js_analyze/src/analyzers/style/use_enum_initializers.rs b/crates/rome_js_analyze/src/analyzers/style/use_enum_initializers.rs index dcd2f48e02d..a01ab2efdc5 100644 --- a/crates/rome_js_analyze/src/analyzers/style/use_enum_initializers.rs +++ b/crates/rome_js_analyze/src/analyzers/style/use_enum_initializers.rs @@ -4,9 +4,7 @@ use rome_analyze::{declare_rule, ActionCategory, Ast, Rule, RuleDiagnostic}; use rome_console::markup; use rome_diagnostics::Applicability; use rome_js_factory::make; -use rome_js_syntax::{ - inner_text, AnyJsExpression, AnyJsLiteralExpression, JsSyntaxKind, TsEnumMember, -}; +use rome_js_syntax::{AnyJsExpression, AnyJsLiteralExpression, JsSyntaxKind, TsEnumMember}; use rome_rowan::{AstNode, BatchMutationExt, Direction}; declare_rule! { @@ -119,9 +117,8 @@ impl Rule for UseEnumInitializers { )) } AnyJsLiteralExpression::JsStringLiteralExpression(expr) => { - let prev_enum_delim_val = expr.value_token().ok()?; - let prev_enum_val = inner_text(&prev_enum_delim_val); - if prev_name.text() == prev_enum_val { + let prev_enum_val = expr.inner_string_text().ok()?; + if prev_name.name() == Some(prev_enum_val) { let enum_name = enum_member.name().ok()?.text(); Some(AnyJsLiteralExpression::JsStringLiteralExpression( make::js_string_literal_expression(make::js_string_literal(&enum_name)), diff --git a/crates/rome_js_analyze/src/analyzers/suspicious/no_duplicate_class_members.rs b/crates/rome_js_analyze/src/analyzers/suspicious/no_duplicate_class_members.rs index 1fe08f93ee0..53e2a151c84 100644 --- a/crates/rome_js_analyze/src/analyzers/suspicious/no_duplicate_class_members.rs +++ b/crates/rome_js_analyze/src/analyzers/suspicious/no_duplicate_class_members.rs @@ -5,8 +5,8 @@ use rome_js_syntax::{ AnyJsClassMemberName, JsClassMemberList, JsGetterClassMember, JsMethodClassMember, JsPropertyClassMember, JsSetterClassMember, JsStaticModifier, JsSyntaxList, TextRange, }; -use rome_rowan::AstNodeList; use rome_rowan::{declare_node_union, AstNode}; +use rome_rowan::{AstNodeList, TokenText}; declare_rule! { /// Disallow duplicate class members. @@ -91,7 +91,7 @@ declare_rule! { } } -fn get_member_name(node: &AnyJsClassMemberName) -> Option { +fn get_member_name(node: &AnyJsClassMemberName) -> Option { match node { AnyJsClassMemberName::JsLiteralMemberName(node) => node.name().ok(), _ => None, @@ -186,7 +186,7 @@ impl Rule for NoDuplicateClassMembers { let member_definition = AnyClassMemberDefinition::cast_ref(member.syntax())?; let member_name_node = member_definition.name()?; let member_state = MemberState { - name: get_member_name(&member_name_node)?, + name: get_member_name(&member_name_node)?.to_string(), is_static: is_static_member(member_definition.modifiers_list()), }; @@ -215,7 +215,7 @@ impl Rule for NoDuplicateClassMembers { state.range(), format!( "Duplicate class member name {:?}", - get_member_name(&state.name()?)? + get_member_name(&state.name()?)?.text() ), ); diff --git a/crates/rome_js_analyze/src/analyzers/suspicious/no_duplicate_object_keys.rs b/crates/rome_js_analyze/src/analyzers/suspicious/no_duplicate_object_keys.rs index bf7360e9301..57d138a3712 100644 --- a/crates/rome_js_analyze/src/analyzers/suspicious/no_duplicate_object_keys.rs +++ b/crates/rome_js_analyze/src/analyzers/suspicious/no_duplicate_object_keys.rs @@ -8,7 +8,7 @@ use rome_js_syntax::{ use rome_js_syntax::{ JsMethodObjectMember, JsPropertyObjectMember, JsShorthandPropertyObjectMember, TextRange, }; -use rome_rowan::{AstNode, BatchMutationExt}; +use rome_rowan::{AstNode, BatchMutationExt, TokenText}; use std::cmp::Ordering; use std::collections::HashMap; use std::fmt::Display; @@ -69,7 +69,7 @@ enum MemberDefinition { ShorthandProperty(JsShorthandPropertyObjectMember), } impl MemberDefinition { - fn name(&self) -> Option { + fn name(&self) -> Option { match self { MemberDefinition::Getter(getter) => { getter.name().ok()?.as_js_literal_member_name()?.name().ok() @@ -86,9 +86,14 @@ impl MemberDefinition { .as_js_literal_member_name()? .name() .ok(), - MemberDefinition::ShorthandProperty(shorthand_property) => { - Some(shorthand_property.name().ok()?.text()) - } + MemberDefinition::ShorthandProperty(shorthand_property) => Some( + shorthand_property + .name() + .ok()? + .value_token() + .ok()? + .token_text_trimmed(), + ), } } @@ -210,7 +215,7 @@ impl Rule for NoDuplicateObjectKeys { fn run(ctx: &RuleContext) -> Self::Signals { let node = ctx.query(); - let mut defined_properties: HashMap = HashMap::new(); + let mut defined_properties: HashMap = HashMap::new(); let mut signals: Self::Signals = Vec::new(); for member_definition in node diff --git a/crates/rome_js_analyze/src/analyzers/suspicious/no_prototype_builtins.rs b/crates/rome_js_analyze/src/analyzers/suspicious/no_prototype_builtins.rs index 74f95741edd..cd44ec5b778 100644 --- a/crates/rome_js_analyze/src/analyzers/suspicious/no_prototype_builtins.rs +++ b/crates/rome_js_analyze/src/analyzers/suspicious/no_prototype_builtins.rs @@ -65,7 +65,7 @@ impl Rule for NoPrototypeBuiltins { let member_name_text = member_name.text(); return is_prototype_builtins(member_name_text).then_some(RuleState { prototype_builtins_method_name: member_name_text.to_string(), - text_range: member_name.token().text_trimmed_range(), + text_range: member_name.range(), }); } None diff --git a/crates/rome_js_analyze/src/aria_services.rs b/crates/rome_js_analyze/src/aria_services.rs index 6fe2025b942..abf736e815d 100644 --- a/crates/rome_js_analyze/src/aria_services.rs +++ b/crates/rome_js_analyze/src/aria_services.rs @@ -74,8 +74,8 @@ mod tests { use crate::aria_services::AriaServices; use rome_aria::{AriaProperties, AriaRoles}; use rome_js_factory::make::{ - ident, jsx_attribute, jsx_attribute_initializer_clause, jsx_attribute_list, jsx_ident, - jsx_name, jsx_string, token, + ident, jsx_attribute, jsx_attribute_initializer_clause, jsx_attribute_list, jsx_name, + jsx_string, jsx_string_literal, token, }; use rome_js_syntax::{AnyJsxAttribute, AnyJsxAttributeName, AnyJsxAttributeValue, T}; use std::sync::Arc; @@ -88,8 +88,8 @@ mod tests { jsx_attribute(AnyJsxAttributeName::JsxName(jsx_name(ident("class")))) .with_initializer(jsx_attribute_initializer_clause( token(T![=]), - AnyJsxAttributeValue::JsxString(jsx_string(jsx_ident( - "\"wrapper document\"", + AnyJsxAttributeValue::JsxString(jsx_string(jsx_string_literal( + "wrapper document", ))), )) .build(), @@ -98,7 +98,7 @@ mod tests { jsx_attribute(AnyJsxAttributeName::JsxName(jsx_name(ident("role")))) .with_initializer(jsx_attribute_initializer_clause( token(T![=]), - AnyJsxAttributeValue::JsxString(jsx_string(jsx_ident("\"article\""))), + AnyJsxAttributeValue::JsxString(jsx_string(jsx_string_literal("article"))), )) .build(), ), diff --git a/crates/rome_js_analyze/src/react.rs b/crates/rome_js_analyze/src/react.rs index 18844e9ff38..0fd305f9ecd 100644 --- a/crates/rome_js_analyze/src/react.rs +++ b/crates/rome_js_analyze/src/react.rs @@ -192,7 +192,7 @@ pub(crate) fn is_react_call_api( let expr = expression.omit_parentheses(); if let Some(callee) = AnyJsMemberExpression::cast_ref(expr.syntax()) { let Some(object) = callee.object().ok() else { return false }; - let Some(reference) = object.omit_parentheses().as_reference_identifier() else { return false }; + let Some(reference) = object.omit_parentheses().as_js_reference_identifier() else { return false }; let Some(member_name) = callee.member_name() else { return false }; if member_name.text() != api_name { return false; @@ -203,7 +203,7 @@ pub(crate) fn is_react_call_api( }; } - if let Some(ident) = expr.as_reference_identifier() { + if let Some(ident) = expr.as_js_reference_identifier() { return model .binding(&ident) .and_then(|it| is_named_react_export(it, lib, api_name)) diff --git a/crates/rome_js_analyze/src/react/hooks.rs b/crates/rome_js_analyze/src/react/hooks.rs index b1aa463e5d2..dae9445d08f 100644 --- a/crates/rome_js_analyze/src/react/hooks.rs +++ b/crates/rome_js_analyze/src/react/hooks.rs @@ -3,7 +3,7 @@ use std::collections::{HashMap, HashSet}; use rome_js_semantic::{Capture, Closure, ClosureExtensions, SemanticModel}; use rome_js_syntax::{ - binding_ext::AnyJsIdentifierBinding, static_value::StaticStringValue, AnyJsExpression, + binding_ext::AnyJsIdentifierBinding, static_value::StaticValue, AnyJsExpression, AnyJsMemberExpression, JsArrayBindingPattern, JsArrayBindingPatternElementList, JsArrowFunctionExpression, JsCallExpression, JsFunctionExpression, JsVariableDeclarator, TextRange, @@ -140,16 +140,14 @@ pub(crate) fn react_hook_with_dependency( model: &SemanticModel, ) -> Option { let expression = call.callee().ok()?; - let name = if let AnyJsExpression::JsIdentifierExpression(identifier) = expression.clone() { - Some(StaticStringValue::Unquoted( - identifier.name().ok()?.value_token().ok()?, - )) + let name = if let Some(identifier) = expression.as_js_reference_identifier() { + Some(StaticValue::String(identifier.value_token().ok()?)) } else if let Some(member_expr) = AnyJsMemberExpression::cast_ref(expression.syntax()) { Some(member_expr.member_name()?) } else { None }?; - let function_name_range = name.token().text_trimmed_range(); + let function_name_range = name.range(); let name = name.text(); // check if the hooks api is imported from the react library diff --git a/crates/rome_js_analyze/src/semantic_analyzers/correctness/no_global_object_calls.rs b/crates/rome_js_analyze/src/semantic_analyzers/correctness/no_global_object_calls.rs index a4190b965c2..e859b3291f7 100644 --- a/crates/rome_js_analyze/src/semantic_analyzers/correctness/no_global_object_calls.rs +++ b/crates/rome_js_analyze/src/semantic_analyzers/correctness/no_global_object_calls.rs @@ -105,7 +105,7 @@ impl Rule for NoGlobalObjectCalls { model .binding(&reference) .is_none() - .then_some((non_callable, name.token().text_trimmed_range())) + .then_some((non_callable, name.range())) } fn diagnostic( diff --git a/crates/rome_js_analyze/src/semantic_analyzers/nursery/use_naming_convention.rs b/crates/rome_js_analyze/src/semantic_analyzers/nursery/use_naming_convention.rs index d807788251d..f717618ee32 100644 --- a/crates/rome_js_analyze/src/semantic_analyzers/nursery/use_naming_convention.rs +++ b/crates/rome_js_analyze/src/semantic_analyzers/nursery/use_naming_convention.rs @@ -17,7 +17,7 @@ use rome_deserialize::{ use rome_diagnostics::Applicability; use rome_js_semantic::CanBeImportedExported; use rome_js_syntax::{ - binding_ext::AnyJsBindingDeclaration, inner_text, AnyJsClassMember, AnyJsObjectMember, + binding_ext::AnyJsBindingDeclaration, inner_string_text, AnyJsClassMember, AnyJsObjectMember, AnyJsVariableDeclaration, AnyTsTypeMember, JsIdentifierBinding, JsLiteralExportName, JsLiteralMemberName, JsPrivateClassMemberName, JsSyntaxKind, JsSyntaxToken, JsVariableDeclarator, JsVariableKind, TsEnumMember, TsIdentifierBinding, TsTypeParameterName, @@ -25,7 +25,7 @@ use rome_js_syntax::{ use rome_js_unicode_table::is_js_ident; use rome_json_syntax::JsonLanguage; use rome_rowan::{ - declare_node_union, AstNode, AstNodeList, BatchMutationExt, SyntaxNode, SyntaxResult, + declare_node_union, AstNode, AstNodeList, BatchMutationExt, SyntaxNode, SyntaxResult, TokenText, }; use serde::{Deserialize, Serialize}; use smallvec::SmallVec; @@ -275,7 +275,7 @@ declare_rule! { } impl Rule for UseNamingConvention { - type Query = Semantic; + type Query = Semantic; type State = State; type Signals = Option; type Options = NamingConventionOptions; @@ -289,8 +289,8 @@ impl Rule for UseNamingConvention { // No naming convention to verify. return None; } - let name_token = node.name_token().ok()?; - let name = inner_text(&name_token); + let name = node.name().ok()?; + let name = name.text(); if !is_js_ident(name) { // ignore non-identifier strings return None; @@ -319,8 +319,8 @@ impl Rule for UseNamingConvention { element, suggested_name, } = state; - let name_token = ctx.query().name_token().ok()?; - let name = inner_text(&name_token); + let name = ctx.query().name().ok()?; + let name = name.text(); let trimmed_name = trim_underscore_dollar(name); let allowed_cases = element.allowed_cases(ctx.options()); let allowed_case_names = allowed_cases @@ -353,7 +353,7 @@ impl Rule for UseNamingConvention { suggested_name, } = state; let renamable = match node { - AnyName::JsIdentifierBinding(binding) => { + AnyIdentifierBindingLike::JsIdentifierBinding(binding) => { if binding.is_exported(model) { return None; } @@ -366,7 +366,7 @@ impl Rule for UseNamingConvention { binding.clone(), )) } - AnyName::TsIdentifierBinding(binding) => { + AnyIdentifierBindingLike::TsIdentifierBinding(binding) => { if binding.is_exported(model) { return None; } @@ -393,8 +393,8 @@ impl Rule for UseNamingConvention { } declare_node_union! { - /// Ast nodes that carries a name. - pub(crate) AnyName = + /// Ast nodes that defines a name. + pub(crate) AnyIdentifierBindingLike = JsIdentifierBinding | JsLiteralMemberName | JsPrivateClassMemberName | @@ -403,17 +403,25 @@ declare_node_union! { TsTypeParameterName } -impl AnyName { +impl AnyIdentifierBindingLike { fn name_token(&self) -> SyntaxResult { match self { - AnyName::JsIdentifierBinding(binding) => binding.name_token(), - AnyName::JsLiteralMemberName(member_name) => member_name.value(), - AnyName::JsPrivateClassMemberName(member_name) => member_name.id_token(), - AnyName::JsLiteralExportName(export_name) => export_name.value(), - AnyName::TsIdentifierBinding(binding) => binding.name_token(), - AnyName::TsTypeParameterName(type_parameter) => type_parameter.ident_token(), + AnyIdentifierBindingLike::JsIdentifierBinding(binding) => binding.name_token(), + AnyIdentifierBindingLike::JsLiteralMemberName(member_name) => member_name.value(), + AnyIdentifierBindingLike::JsPrivateClassMemberName(member_name) => { + member_name.id_token() + } + AnyIdentifierBindingLike::JsLiteralExportName(export_name) => export_name.value(), + AnyIdentifierBindingLike::TsIdentifierBinding(binding) => binding.name_token(), + AnyIdentifierBindingLike::TsTypeParameterName(type_parameter) => { + type_parameter.ident_token() + } } } + + fn name(&self) -> SyntaxResult { + Ok(inner_string_text(&self.name_token()?)) + } } #[derive(Debug)] @@ -618,15 +626,15 @@ enum Named { } impl Named { - fn from_name(js_name: &AnyName) -> Option { + fn from_name(js_name: &AnyIdentifierBindingLike) -> Option { match js_name { - AnyName::JsIdentifierBinding(binding) => { + AnyIdentifierBindingLike::JsIdentifierBinding(binding) => { Named::from_binding_declaration(&binding.declaration()?) } - AnyName::TsIdentifierBinding(binding) => { + AnyIdentifierBindingLike::TsIdentifierBinding(binding) => { Named::from_binding_declaration(&binding.declaration()?) } - AnyName::JsLiteralMemberName(member_name) => { + AnyIdentifierBindingLike::JsLiteralMemberName(member_name) => { if let Some(member) = member_name.parent::() { Named::from_class_member(&member) } else if let Some(member) = member_name.parent::() { @@ -639,10 +647,10 @@ impl Named { None } } - AnyName::JsPrivateClassMemberName(member_name) => { + AnyIdentifierBindingLike::JsPrivateClassMemberName(member_name) => { Named::from_class_member(&member_name.parent::()?) } - AnyName::JsLiteralExportName(export_name) => { + AnyIdentifierBindingLike::JsLiteralExportName(export_name) => { match export_name.syntax().parent()?.kind() { JsSyntaxKind::JS_NAMED_IMPORT_SPECIFIER => Some(Named::ImportSource), JsSyntaxKind::JS_EXPORT_NAMED_FROM_SPECIFIER => Some(Named::ExportSource), @@ -652,7 +660,7 @@ impl Named { _ => None, } } - AnyName::TsTypeParameterName(_) => Some(Named::TypeParameter), + AnyIdentifierBindingLike::TsTypeParameterName(_) => Some(Named::TypeParameter), } } diff --git a/crates/rome_js_analyze/src/semantic_analyzers/style/no_shouty_constants.rs b/crates/rome_js_analyze/src/semantic_analyzers/style/no_shouty_constants.rs index 064f6d1c280..250b34c0c41 100644 --- a/crates/rome_js_analyze/src/semantic_analyzers/style/no_shouty_constants.rs +++ b/crates/rome_js_analyze/src/semantic_analyzers/style/no_shouty_constants.rs @@ -66,7 +66,7 @@ fn is_id_and_string_literal_inner_text_equal( .as_js_string_literal_expression()?; let literal_text = literal.inner_string_text().ok()?; - if id_text.len() != literal_text.len() { + if id_text.len() != literal_text.text().len() { return None; } diff --git a/crates/rome_js_analyze/src/utils.rs b/crates/rome_js_analyze/src/utils.rs index b1d67aca1dc..284d407bf68 100644 --- a/crates/rome_js_analyze/src/utils.rs +++ b/crates/rome_js_analyze/src/utils.rs @@ -1,6 +1,6 @@ use rome_js_factory::make; use rome_js_syntax::{ - inner_text, AnyJsStatement, JsLanguage, JsModuleItemList, JsStatementList, JsSyntaxNode, + inner_string_text, AnyJsStatement, JsLanguage, JsModuleItemList, JsStatementList, JsSyntaxNode, JsVariableDeclaration, JsVariableDeclarator, JsVariableDeclaratorList, JsVariableStatement, T, }; use rome_rowan::{AstNode, AstSeparatedList, BatchMutation, Direction, WalkEvent}; @@ -225,7 +225,7 @@ pub(crate) fn is_node_equal(a_node: &JsSyntaxNode, b_node: &JsSyntaxNode) -> boo (None, Some(_)) | (Some(_), None) => return false, // both are tokens (Some(a), Some(b)) => { - if inner_text(a) != inner_text(b) { + if inner_string_text(a) != inner_string_text(b) { return false; } continue; diff --git a/crates/rome_js_factory/src/make.rs b/crates/rome_js_factory/src/make.rs index 3c3ff864c1c..b32a1cc635f 100644 --- a/crates/rome_js_factory/src/make.rs +++ b/crates/rome_js_factory/src/make.rs @@ -25,6 +25,16 @@ pub fn js_string_literal(text: &str) -> JsSyntaxToken { ) } +/// Create a new string literal token with no attached trivia +pub fn jsx_string_literal(text: &str) -> JsSyntaxToken { + JsSyntaxToken::new_detached( + JsSyntaxKind::JSX_STRING_LITERAL, + &format!("\"{text}\""), + [], + [], + ) +} + /// Create a new string literal token with no attached trivia pub fn js_number_literal(text: N) -> JsSyntaxToken where diff --git a/crates/rome_js_formatter/src/js/expressions/call_arguments.rs b/crates/rome_js_formatter/src/js/expressions/call_arguments.rs index ed3f8e04834..2596c545d9e 100644 --- a/crates/rome_js_formatter/src/js/expressions/call_arguments.rs +++ b/crates/rome_js_formatter/src/js/expressions/call_arguments.rs @@ -950,56 +950,50 @@ fn is_commonjs_or_amd_call( arguments: &JsCallArguments, call: &JsCallExpression, ) -> SyntaxResult { - let callee = call.callee()?; - - Ok(match callee { - AnyJsExpression::JsIdentifierExpression(identifier) => { - let reference = identifier.name()?; - - if reference.has_name("require") { - true - } else if reference.has_name("define") { - let in_statement = call.parent::().is_some(); - - if in_statement { - let args = arguments.args(); - match args.len() { - 1 => true, - 2 => matches!( - args.first(), - Some(Ok(AnyJsCallArgument::AnyJsExpression( - AnyJsExpression::JsArrayExpression(_) - ))) - ), - 3 => { - let mut iter = args.iter(); - let first = iter.next(); - let second = iter.next(); - matches!( - (first, second), - ( - Some(Ok(AnyJsCallArgument::AnyJsExpression( - AnyJsExpression::AnyJsLiteralExpression( - AnyJsLiteralExpression::JsStringLiteralExpression(_) - ) - ))), - Some(Ok(AnyJsCallArgument::AnyJsExpression( - AnyJsExpression::JsArrayExpression(_) - ))) - ) + let Some(reference) = call.callee()?.as_js_reference_identifier() else { + return Ok(false); + }; + let result = match reference.name()?.text() { + "require" => true, + "define" => { + let in_statement = call.parent::().is_some(); + if in_statement { + let args = arguments.args(); + match args.len() { + 1 => true, + 2 => matches!( + args.first(), + Some(Ok(AnyJsCallArgument::AnyJsExpression( + AnyJsExpression::JsArrayExpression(_) + ))) + ), + 3 => { + let mut iter = args.iter(); + let first = iter.next(); + let second = iter.next(); + matches!( + (first, second), + ( + Some(Ok(AnyJsCallArgument::AnyJsExpression( + AnyJsExpression::AnyJsLiteralExpression( + AnyJsLiteralExpression::JsStringLiteralExpression(_) + ) + ))), + Some(Ok(AnyJsCallArgument::AnyJsExpression( + AnyJsExpression::JsArrayExpression(_) + ))) ) - } - _ => false, + ) } - } else { - false + _ => false, } } else { false } } _ => false, - }) + }; + Ok(result) } /// Returns `true` if `arguments` contains a single [multiline template literal argument that starts on its own ](is_multiline_template_starting_on_same_line). diff --git a/crates/rome_js_syntax/src/directive_ext.rs b/crates/rome_js_syntax/src/directive_ext.rs index 694958537ad..98a20473a6c 100644 --- a/crates/rome_js_syntax/src/directive_ext.rs +++ b/crates/rome_js_syntax/src/directive_ext.rs @@ -1,6 +1,6 @@ -use rome_rowan::{SyntaxResult, TextRange, TextSize, TokenText}; +use rome_rowan::{SyntaxResult, TokenText}; -use crate::JsDirective; +use crate::{inner_string_text, JsDirective}; impl JsDirective { /// Get the inner text of a string not including the quotes @@ -21,22 +21,7 @@ impl JsDirective { /// assert_eq!(text, "use strict") /// ``` pub fn inner_string_text(&self) -> SyntaxResult { - let value = self.value_token()?; - let mut text = value.token_text_trimmed(); - - static QUOTES: [char; 2] = ['"', '\'']; - - if text.starts_with(QUOTES) { - let range = TextRange::new(1.into(), text.len()); - text = text.slice(range); - } - - if text.ends_with(QUOTES) { - let range = TextRange::new(0.into(), text.len() - TextSize::from(1)); - text = text.slice(range); - } - - Ok(text) + Ok(inner_string_text(&self.value_token()?)) } } diff --git a/crates/rome_js_syntax/src/expr_ext.rs b/crates/rome_js_syntax/src/expr_ext.rs index 1afe3d34cc4..f4e6b188e4d 100644 --- a/crates/rome_js_syntax/src/expr_ext.rs +++ b/crates/rome_js_syntax/src/expr_ext.rs @@ -1,24 +1,26 @@ //! Extensions for things which are not easily generated in ast expr nodes use crate::numbers::parse_js_number; -use crate::static_value::{QuotedString, StaticStringValue, StaticValue}; +use crate::static_value::StaticValue; use crate::{ - AnyJsCallArgument, AnyJsExpression, AnyJsLiteralExpression, AnyJsObjectMemberName, - AnyJsTemplateElement, JsArrayExpression, JsArrayHole, JsAssignmentExpression, - JsBinaryExpression, JsCallExpression, JsComputedMemberAssignment, JsComputedMemberExpression, - JsLiteralMemberName, JsLogicalExpression, JsNewExpression, JsNumberLiteralExpression, - JsObjectExpression, JsPostUpdateExpression, JsReferenceIdentifier, JsRegexLiteralExpression, - JsStaticMemberExpression, JsStringLiteralExpression, JsSyntaxKind, JsSyntaxToken, - JsTemplateChunkElement, JsTemplateExpression, JsUnaryExpression, OperatorPrecedence, T, + inner_string_text, AnyJsCallArgument, AnyJsExpression, AnyJsLiteralExpression, + AnyJsObjectMemberName, AnyJsTemplateElement, JsArrayExpression, JsArrayHole, + JsAssignmentExpression, JsBinaryExpression, JsCallExpression, JsComputedMemberAssignment, + JsComputedMemberExpression, JsLiteralMemberName, JsLogicalExpression, JsNewExpression, + JsNumberLiteralExpression, JsObjectExpression, JsPostUpdateExpression, JsReferenceIdentifier, + JsRegexLiteralExpression, JsStaticMemberExpression, JsStringLiteralExpression, JsSyntaxKind, + JsSyntaxToken, JsTemplateChunkElement, JsTemplateExpression, JsUnaryExpression, + OperatorPrecedence, T, }; use crate::{JsPreUpdateExpression, JsSyntaxKind::*}; use core::iter; use rome_rowan::{ declare_node_union, AstNode, AstNodeList, AstSeparatedList, NodeOrToken, SyntaxResult, - TextRange, + TextRange, TokenText, }; use std::collections::HashSet; const GLOBAL_THIS: &str = "globalThis"; +const UNDEFINED: &str = "undefined"; const WINDOW: &str = "window"; impl JsReferenceIdentifier { @@ -33,7 +35,7 @@ impl JsReferenceIdentifier { /// assert!(!js_reference_identifier(ident("x")).is_undefined()); /// ``` pub fn is_undefined(&self) -> bool { - self.has_name("undefined") + self.has_name(UNDEFINED) } /// Returns `true` if this identifier refers to the `globalThis` symbol. @@ -65,6 +67,10 @@ impl JsReferenceIdentifier { .map(|token| token.text_trimmed() == name) .unwrap_or_default() } + + pub fn name(&self) -> SyntaxResult { + Ok(self.value_token()?.token_text_trimmed()) + } } impl JsLiteralMemberName { @@ -120,18 +126,10 @@ impl JsLiteralMemberName { /// /// let static_member_name = JsLiteralMemberName::unwrap_cast(node); /// - /// assert_eq!("abcd", static_member_name.name().unwrap()); + /// assert_eq!("abcd", static_member_name.name().unwrap().text()); /// ``` - pub fn name(&self) -> SyntaxResult { - let value = self.value()?; - let name = value.text_trimmed(); - - let result = match value.kind() { - JS_STRING_LITERAL => String::from(&name[1..name.len() - 1]), - _ => String::from(name), - }; - - Ok(result) + pub fn name(&self) -> SyntaxResult { + Ok(inner_string_text(&self.value()?)) } } @@ -278,7 +276,10 @@ impl JsBinaryExpression { self.operator(), Ok(JsBinaryOperator::StrictInequality | JsBinaryOperator::Inequality) ) { - Ok(self.right()?.is_value_null_or_undefined()) + Ok(self + .right()? + .as_static_value() + .map_or(false, |x| x.is_null_or_undefined())) } else { Ok(false) } @@ -520,15 +521,15 @@ impl JsStringLiteralExpression { /// ## Examples /// /// ``` - /// use rome_js_factory::make::{js_string_literal_expression, ident}; + /// use rome_js_factory::make; /// use rome_rowan::TriviaPieceKind; /// - ///let string = js_string_literal_expression(ident("foo") + ///let string = make::js_string_literal_expression(make::js_string_literal("foo") /// .with_leading_trivia(vec![(TriviaPieceKind::Whitespace, " ")])); /// assert_eq!(string.inner_string_text().unwrap().text(), "foo"); /// ``` - pub fn inner_string_text(&self) -> SyntaxResult { - Ok(QuotedString::new(self.value_token()?)) + pub fn inner_string_text(&self) -> SyntaxResult { + Ok(inner_string_text(&self.value_token()?)) } } @@ -722,22 +723,8 @@ impl AnyJsExpression { } /// Return identifier if the expression is an identifier expression. - pub fn as_reference_identifier(&self) -> Option { - self.as_js_identifier_expression() - .and_then(|it| it.name().ok()) - } - - /// Return `true` if the static value match the given string value and it is - /// 1. A string literal - /// 2. A template literal with no substitutions - pub fn is_string_constant(&self, text: &str) -> bool { - self.as_static_value() - .map_or(false, |it| it.is_string_constant(text)) - } - - pub fn is_value_null_or_undefined(&self) -> bool { - self.as_static_value() - .map_or(false, |it| it.is_null_or_undefined()) + pub fn as_js_reference_identifier(&self) -> Option { + self.as_js_identifier_expression()?.name().ok() } pub fn as_static_value(&self) -> Option { @@ -745,39 +732,28 @@ impl AnyJsExpression { AnyJsExpression::AnyJsLiteralExpression(literal) => literal.as_static_value(), AnyJsExpression::JsTemplateExpression(template) => { let element_list = template.elements(); - + if element_list.len() == 0 { + let range = template + .l_tick_token() + .ok()? + .text_trimmed_range() + .add_start(1.into()); + return Some(StaticValue::EmptyString(range)); + } if element_list.len() > 1 { return None; } - - if element_list.len() == 0 { - return Some(StaticValue::TemplateChunk(None)); - } - match element_list.first()? { - AnyJsTemplateElement::JsTemplateChunkElement(element) => Some( - StaticValue::TemplateChunk(Some(element.template_chunk_token().ok()?)), - ), - AnyJsTemplateElement::JsTemplateElement(element) => { - let static_value = element.expression().ok()?.as_static_value(); - match static_value { - Some(StaticValue::Boolean(token)) - | Some(StaticValue::Null(token)) - | Some(StaticValue::Undefined(token)) - | Some(StaticValue::Number(token)) - | Some(StaticValue::BigInt(token)) => { - Some(StaticValue::String(QuotedString::new(token))) - } - _ => static_value, - } + AnyJsTemplateElement::JsTemplateChunkElement(element) => { + Some(StaticValue::String(element.template_chunk_token().ok()?)) } + _ => None, } } AnyJsExpression::JsIdentifierExpression(identifier) => { let identifier_token = identifier.name().ok()?.value_token().ok()?; match identifier_token.text_trimmed() { - "undefined" => Some(StaticValue::Undefined(identifier_token)), - "NaN" => Some(StaticValue::Number(identifier_token)), + UNDEFINED => Some(StaticValue::Undefined(identifier_token)), _ => None, } } @@ -823,9 +799,9 @@ impl AnyJsLiteralExpression { Some(StaticValue::Number(number.value_token().ok()?)) } AnyJsLiteralExpression::JsRegexLiteralExpression(_) => None, - AnyJsLiteralExpression::JsStringLiteralExpression(string) => Some(StaticValue::String( - QuotedString::new(string.value_token().ok()?), - )), + AnyJsLiteralExpression::JsStringLiteralExpression(string) => { + Some(StaticValue::String(string.value_token().ok()?)) + } } } } @@ -949,7 +925,6 @@ impl AnyJsMemberExpression { /// ``` /// use rome_js_syntax::{AnyJsExpression, AnyJsLiteralExpression, AnyJsMemberExpression, T}; /// use rome_js_factory::make; - /// use rome_js_syntax::static_value::{QuotedString, StaticStringValue}; /// /// let math_id = make::js_reference_identifier(make::ident("Math")); /// let math_id = make::js_identifier_expression(math_id); @@ -968,20 +943,18 @@ impl AnyJsMemberExpression { /// let member_name = computed_member.member_name().unwrap(); /// assert_eq!(member_name.text(), "pow"); /// ``` - pub fn member_name(&self) -> Option { + pub fn member_name(&self) -> Option { let value = match self { AnyJsMemberExpression::JsStaticMemberExpression(e) => { - StaticStringValue::Unquoted(e.member().ok()?.as_js_name()?.value_token().ok()?) + StaticValue::String(e.member().ok()?.as_js_name()?.value_token().ok()?) } AnyJsMemberExpression::JsComputedMemberExpression(e) => { let member = e.member().ok()?.omit_parentheses(); - match member.as_static_value()? { - StaticValue::String(quoted_str) => StaticStringValue::Quoted(quoted_str), - StaticValue::TemplateChunk(Some(template_chunk)) => { - StaticStringValue::Unquoted(template_chunk) - } - _ => return None, + let result = member.as_static_value()?; + if !matches!(result, StaticValue::String(_) | StaticValue::EmptyString(_)) { + return None; } + result } }; Some(value) @@ -1016,7 +989,7 @@ impl AnyJsObjectMemberName { /// let computed = AnyJsObjectMemberName::JsComputedMemberName(computed); /// assert_eq!(computed.name().unwrap().text(), "a"); /// ``` - pub fn name(&self) -> Option { + pub fn name(&self) -> Option { let token = match self { AnyJsObjectMemberName::JsComputedMemberName(expr) => { let expr = expr.expression().ok()?; @@ -1035,11 +1008,7 @@ impl AnyJsObjectMemberName { } AnyJsObjectMemberName::JsLiteralMemberName(expr) => expr.value().ok()?, }; - Some(if token.kind() == JsSyntaxKind::JS_STRING_LITERAL { - StaticStringValue::Quoted(QuotedString::new(token)) - } else { - StaticStringValue::Unquoted(token) - }) + Some(inner_string_text(&token)) } } @@ -1051,7 +1020,6 @@ impl AnyJsObjectMemberName { /// ``` /// use rome_js_syntax::{AnyJsExpression, AnyJsLiteralExpression, AnyJsMemberExpression, global_identifier, T}; /// use rome_js_factory::make; -/// use rome_js_syntax::static_value::{QuotedString, StaticStringValue}; /// /// let math_reference = make::js_reference_identifier(make::ident("Math")); /// let math_id = make::js_identifier_expression(math_reference.clone()); @@ -1072,16 +1040,13 @@ impl AnyJsObjectMemberName { /// assert_eq!(name.text(), "Math"); /// assert_eq!(reference, global_this_reference); /// ``` -pub fn global_identifier( - expr: &AnyJsExpression, -) -> Option<(JsReferenceIdentifier, StaticStringValue)> { - if let AnyJsExpression::JsIdentifierExpression(id_expr) = expr { - let reference = id_expr.name().ok()?; - let name = StaticStringValue::Unquoted(reference.value_token().ok()?); +pub fn global_identifier(expr: &AnyJsExpression) -> Option<(JsReferenceIdentifier, StaticValue)> { + if let Some(reference) = expr.as_js_reference_identifier() { + let name = StaticValue::String(reference.value_token().ok()?); return Some((reference, name)); } let Some(member_expr) = AnyJsMemberExpression::cast_ref(expr.syntax()) else { return None; }; - let name: StaticStringValue = member_expr.member_name()?; + let name = member_expr.member_name()?; let mut expr = member_expr.object().ok()?.omit_parentheses(); while let Some(member_expr) = AnyJsMemberExpression::cast_ref(expr.syntax()) { if !matches!(member_expr.member_name()?.text(), GLOBAL_THIS | WINDOW) { @@ -1089,9 +1054,8 @@ pub fn global_identifier( } expr = member_expr.object().ok()?.omit_parentheses(); } - if let AnyJsExpression::JsIdentifierExpression(id_expr) = expr { - let reference = id_expr.name().ok()?; - if reference.has_name(GLOBAL_THIS) || reference.has_name(WINDOW) { + if let Some(reference) = expr.as_js_reference_identifier() { + if matches!(reference.name().ok()?.text(), GLOBAL_THIS | WINDOW) { return Some((reference, name)); } } @@ -1177,7 +1141,7 @@ impl JsCallExpression { pub fn has_callee(&self, name: &str) -> bool { self.callee().map_or(false, |it| { - it.as_reference_identifier() + it.as_js_reference_identifier() .map_or(false, |it| it.has_name(name)) }) } @@ -1186,7 +1150,7 @@ impl JsCallExpression { impl JsNewExpression { pub fn has_callee(&self, name: &str) -> bool { self.callee().map_or(false, |it| { - it.as_reference_identifier() + it.as_js_reference_identifier() .map_or(false, |it| it.has_name(name)) }) } diff --git a/crates/rome_js_syntax/src/import_ext.rs b/crates/rome_js_syntax/src/import_ext.rs index e6885c869ed..5b589d30be8 100644 --- a/crates/rome_js_syntax/src/import_ext.rs +++ b/crates/rome_js_syntax/src/import_ext.rs @@ -1,5 +1,5 @@ -use crate::{AnyJsImportClause, JsImport, JsModuleSource, TextSize}; -use rome_rowan::{SyntaxResult, TextRange, TokenText}; +use crate::{inner_string_text, AnyJsImportClause, JsImport, JsModuleSource}; +use rome_rowan::{SyntaxResult, TokenText}; impl JsImport { /// It checks if the source of an import against the string `source_to_check` @@ -42,21 +42,6 @@ impl JsModuleSource { /// assert_eq!(text.text(), "react"); /// ``` pub fn inner_string_text(&self) -> SyntaxResult { - let value = self.value_token()?; - let mut text = value.token_text_trimmed(); - - static QUOTES: [char; 2] = ['"', '\'']; - - if text.starts_with(QUOTES) { - let range = TextRange::new(1.into(), text.len()); - text = text.slice(range); - } - - if text.ends_with(QUOTES) { - let range = TextRange::new(0.into(), text.len() - TextSize::from(1)); - text = text.slice(range); - } - - Ok(text) + Ok(inner_string_text(&self.value_token()?)) } } diff --git a/crates/rome_js_syntax/src/jsx_ext.rs b/crates/rome_js_syntax/src/jsx_ext.rs index 7bf81724d6e..846cdebc76a 100644 --- a/crates/rome_js_syntax/src/jsx_ext.rs +++ b/crates/rome_js_syntax/src/jsx_ext.rs @@ -1,12 +1,11 @@ use std::collections::HashSet; use crate::{ - static_value::{QuotedString, StaticValue}, - AnyJsxAttribute, AnyJsxAttributeName, AnyJsxAttributeValue, AnyJsxChild, AnyJsxElementName, - JsSyntaxToken, JsxAttribute, JsxAttributeList, JsxElement, JsxName, JsxOpeningElement, - JsxSelfClosingElement, JsxString, + inner_string_text, static_value::StaticValue, AnyJsxAttribute, AnyJsxAttributeName, + AnyJsxAttributeValue, AnyJsxChild, AnyJsxElementName, JsSyntaxToken, JsxAttribute, + JsxAttributeList, JsxElement, JsxName, JsxOpeningElement, JsxSelfClosingElement, JsxString, }; -use rome_rowan::{declare_node_union, AstNode, AstNodeList, SyntaxResult}; +use rome_rowan::{declare_node_union, AstNode, AstNodeList, SyntaxResult, TokenText}; impl JsxString { /// Get the inner text of a string not including the quotes @@ -14,15 +13,15 @@ impl JsxString { /// ## Examples /// /// ``` - /// use rome_js_factory::make::{jsx_ident, jsx_string}; + /// use rome_js_factory::make; /// use rome_rowan::TriviaPieceKind; /// - ///let string = jsx_string(jsx_ident("button").with_leading_trivia(vec![(TriviaPieceKind::Whitespace, " ")])); + ///let string = make::jsx_string(make::jsx_string_literal("button") + /// .with_leading_trivia(vec![(TriviaPieceKind::Whitespace, " ")])); /// assert_eq!(string.inner_string_text().unwrap().text(), "button"); /// ``` - pub fn inner_string_text(&self) -> SyntaxResult { - let value = self.value_token()?; - Ok(QuotedString::new(value)) + pub fn inner_string_text(&self) -> SyntaxResult { + Ok(inner_string_text(&self.value_token()?)) } } @@ -395,9 +394,10 @@ impl AnyJsxElement { pub fn has_truthy_attribute(&self, name_to_lookup: &str) -> bool { self.find_attribute_by_name(name_to_lookup) .map_or(false, |attribute| { - attribute.as_static_value().map_or(true, |value| { - !(value.is_falsy() || value.is_string_constant("false")) - }) && !self.has_trailing_spread_prop(attribute) + attribute + .as_static_value() + .map_or(true, |value| !(value.is_falsy() || value.text() == "false")) + && !self.has_trailing_spread_prop(attribute) }) } } @@ -433,7 +433,7 @@ impl AnyJsxAttributeValue { expression.expression().ok()?.as_static_value() } AnyJsxAttributeValue::JsxString(string) => { - Some(StaticValue::String(string.inner_string_text().ok()?)) + Some(StaticValue::String(string.value_token().ok()?)) } } } diff --git a/crates/rome_js_syntax/src/lib.rs b/crates/rome_js_syntax/src/lib.rs index 3d1b14de746..f45ec01f7d3 100644 --- a/crates/rome_js_syntax/src/lib.rs +++ b/crates/rome_js_syntax/src/lib.rs @@ -29,7 +29,8 @@ pub use function_ext::*; pub use identifier_ext::*; pub use modifier_ext::*; pub use rome_rowan::{ - SyntaxNodeText, TextLen, TextRange, TextSize, TokenAtOffset, TriviaPieceKind, WalkEvent, + SyntaxNodeText, TextLen, TextRange, TextSize, TokenAtOffset, TokenText, TriviaPieceKind, + WalkEvent, }; pub use stmt_ext::*; pub use syntax_node::*; @@ -273,29 +274,35 @@ impl OperatorPrecedence { } } -/// Similar to `JsSyntaxToken::text_trimmed` with the difference that delimiters of string literals are trimmed. +/// Similar to [JsSyntaxToken::text_trimmed()], but removes the quotes of string literals. /// /// ## Examples /// /// ``` -/// use rome_js_syntax::{JsSyntaxKind, JsSyntaxToken, inner_text}; +/// use rome_js_syntax::{JsSyntaxKind, JsSyntaxToken, inner_string_text}; /// -/// let a = JsSyntaxToken::new_detached(JsSyntaxKind::JS_STRING_LITERAL, "'inner_text'", [], []); -/// let b = JsSyntaxToken::new_detached(JsSyntaxKind::JS_STRING_LITERAL, "\"inner_text\"", [], []); -/// assert_eq!(inner_text(&a), inner_text(&b)); +/// let a = JsSyntaxToken::new_detached(JsSyntaxKind::JS_STRING_LITERAL, "'inner_string_text'", [], []); +/// let b = JsSyntaxToken::new_detached(JsSyntaxKind::JS_STRING_LITERAL, "\"inner_string_text\"", [], []); +/// assert_eq!(inner_string_text(&a), inner_string_text(&b)); /// /// let a = JsSyntaxToken::new_detached(JsSyntaxKind::LET_KW, "let", [], []); /// let b = JsSyntaxToken::new_detached(JsSyntaxKind::LET_KW, "let", [], []); -/// assert_eq!(inner_text(&a), inner_text(&b)); +/// assert_eq!(inner_string_text(&a), inner_string_text(&b)); /// /// let a = JsSyntaxToken::new_detached(JsSyntaxKind::LET_KW, "let", [], []); /// let b = JsSyntaxToken::new_detached(JsSyntaxKind::CONST_KW, "const", [], []); -/// assert!(inner_text(&a) != inner_text(&b)); +/// assert!(inner_string_text(&a) != inner_string_text(&b)); /// ``` -pub fn inner_text(token: &JsSyntaxToken) -> &str { - let mut result = token.text_trimmed(); - if token.kind() == JsSyntaxKind::JS_STRING_LITERAL { - result = &result[1..result.len() - 1]; +pub fn inner_string_text(token: &JsSyntaxToken) -> TokenText { + let mut text = token.token_text_trimmed(); + if matches!( + token.kind(), + JsSyntaxKind::JS_STRING_LITERAL | JsSyntaxKind::JSX_STRING_LITERAL + ) { + // remove string delimiters + // SAFETY: string literal token have a delimiters at the start and the end of the string + let range = TextRange::new(1.into(), text.len() - TextSize::from(1)); + text = text.slice(range); } - result + text } diff --git a/crates/rome_js_syntax/src/static_value.rs b/crates/rome_js_syntax/src/static_value.rs index 16fa9f3f150..8b26e7727c9 100644 --- a/crates/rome_js_syntax/src/static_value.rs +++ b/crates/rome_js_syntax/src/static_value.rs @@ -1,94 +1,6 @@ -use crate::JsSyntaxToken; +use rome_rowan::TextRange; -use std::ops::Deref; - -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct QuotedString(JsSyntaxToken); - -/// A string literal that is wrapped in quotes -impl QuotedString { - pub fn new(token: JsSyntaxToken) -> Self { - Self(token) - } - - /// Get the inner text of a string not including the quotes - /// - /// ## Examples - /// - /// ``` - /// use rome_js_syntax::static_value::QuotedString; - /// use rome_js_factory::make::ident; - /// use rome_rowan::TriviaPieceKind; - /// - /// let ident = ident("\"foo\"").with_leading_trivia(vec![(TriviaPieceKind::Whitespace, " ")]); - /// assert_eq!(QuotedString::new(ident).text(), "foo"); - /// ``` - pub fn text(&self) -> &str { - self.0 - .text_trimmed() - .trim_start_matches(['"', '\'']) - .trim_end_matches(['"', '\'']) - } - - /// Get the inner text of a string including the quotes - /// - /// ## Examples - /// - /// ``` - /// use rome_js_syntax::static_value::QuotedString; - /// use rome_js_factory::make::ident; - /// use rome_rowan::TriviaPieceKind; - /// - /// let ident = ident("\"foo\"").with_leading_trivia(vec![(TriviaPieceKind::Whitespace, " ")]); - /// assert_eq!(QuotedString::new(ident).quoted_text(), "\"foo\""); - /// ``` - pub fn quoted_text(&self) -> &str { - self.0.text_trimmed() - } -} - -impl Deref for QuotedString { - type Target = str; - - fn deref(&self) -> &Self::Target { - self.text() - } -} - -/// Represent a JavaScript name which is either an identifier (unquoted string) -/// or a literal string (quoted string), or even a template chuck (unquoted string). -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum StaticStringValue { - Quoted(QuotedString), - Unquoted(JsSyntaxToken), -} - -impl StaticStringValue { - /// Return a string of the static value - /// - /// ## Examples - /// - /// ``` - /// use rome_js_syntax::static_value::{QuotedString, StaticStringValue}; - /// use rome_js_factory::make; - /// - /// let quoted_str = QuotedString::new(make::js_string_literal("str")); - /// assert_eq!(StaticStringValue::Quoted(quoted_str).text(), "str"); - /// ``` - pub fn text(&self) -> &str { - match self { - StaticStringValue::Quoted(quoted_string) => quoted_string.text(), - StaticStringValue::Unquoted(token) => token.text_trimmed(), - } - } - - pub fn token(&self) -> &JsSyntaxToken { - match self { - StaticStringValue::Quoted(token) => &token.0, - StaticStringValue::Unquoted(token) => token, - } - } -} +use crate::{JsSyntaxKind, JsSyntaxToken}; #[derive(Debug, Clone, Eq, PartialEq)] /// static values defined in JavaScript's expressions @@ -98,8 +10,10 @@ pub enum StaticValue { Undefined(JsSyntaxToken), Number(JsSyntaxToken), BigInt(JsSyntaxToken), - String(QuotedString), - TemplateChunk(Option), + // The string can be empty. + String(JsSyntaxToken), + /// This is used to represent the empty template string. + EmptyString(TextRange), } impl StaticValue { @@ -109,22 +23,18 @@ impl StaticValue { /// /// ``` /// use rome_js_syntax::{T, static_value::StaticValue}; - /// use rome_js_factory::make::{js_boolean_literal_expression, token}; + /// use rome_js_factory::make; /// - /// let bool = js_boolean_literal_expression(token(T![false])); - /// assert!(StaticValue::Boolean(bool.value_token().ok().unwrap()).is_falsy()); + /// let bool = make::token(T![false]); + /// assert!(StaticValue::Boolean(bool).is_falsy()); /// ``` pub fn is_falsy(&self) -> bool { match self { StaticValue::Boolean(token) => token.text_trimmed() == "false", - StaticValue::Null(_) => true, - StaticValue::Undefined(_) => true, - StaticValue::Number(token) => matches!(token.text_trimmed(), "0" | "-0" | "+0" | "NaN"), - StaticValue::BigInt(token) => matches!(token.text_trimmed(), "0n"), - StaticValue::String(token) => token.text().is_empty(), - StaticValue::TemplateChunk(token) => token - .as_ref() - .map_or(true, |it| it.text_trimmed().is_empty()), + StaticValue::Null(_) | StaticValue::Undefined(_) | StaticValue::EmptyString(_) => true, + StaticValue::Number(token) => token.text_trimmed() == "0", + StaticValue::BigInt(token) => token.text_trimmed() == "0n", + StaticValue::String(_) => self.text().is_empty(), } } @@ -134,42 +44,53 @@ impl StaticValue { /// /// ``` /// use rome_js_syntax::{T, static_value::StaticValue}; - /// use rome_js_factory::make::{js_boolean_literal_expression, token}; + /// use rome_js_factory::make; /// - /// let bool = js_boolean_literal_expression(token(T![false])); - /// assert_eq!(StaticValue::Boolean(bool.value_token().ok().unwrap()).text(), "false"); + /// let bool = make::token(T![false]); + /// assert_eq!(StaticValue::Boolean(bool).text(), "false"); /// ``` pub fn text(&self) -> &str { match self { - StaticValue::Boolean(token) => token.text_trimmed(), - StaticValue::Null(token) => token.text_trimmed(), - StaticValue::Undefined(token) => token.text_trimmed(), - StaticValue::Number(token) => token.text_trimmed(), - StaticValue::BigInt(token) => token.text_trimmed(), - StaticValue::String(token) => token.text(), - StaticValue::TemplateChunk(token) => token.as_ref().map_or("", |it| it.text_trimmed()), + StaticValue::Boolean(token) + | StaticValue::Null(token) + | StaticValue::Undefined(token) + | StaticValue::Number(token) + | StaticValue::BigInt(token) => token.text_trimmed(), + StaticValue::String(token) => { + let text = token.text_trimmed(); + if matches!( + token.kind(), + JsSyntaxKind::JS_STRING_LITERAL | JsSyntaxKind::JSX_STRING_LITERAL + ) { + // SAFETY: string literal token have a delimiters at the start and the end of the string + return &text[1..text.len() - 1]; + } + text + } + StaticValue::EmptyString(_) => "", } } - /// Return `true` if the static value match the given string value and it is - /// 1. A string literal - /// 2. A template literal with no substitutions + /// Return teh range of the static value. /// /// ## Examples /// /// ``` - /// use rome_js_syntax::static_value::{StaticValue, QuotedString}; - /// use rome_js_factory::make::{js_string_literal_expression, ident}; - /// use rome_rowan::TriviaPieceKind; + /// use rome_js_syntax::{T, static_value::StaticValue}; + /// use rome_js_factory::make; /// - /// let ident = ident("\"foo\"").with_leading_trivia(vec![(TriviaPieceKind::Whitespace, " ")]); - /// let quoted_string = QuotedString::new(ident); - /// assert!(StaticValue::String(quoted_string).is_string_constant("foo")); + /// let bool = make::token(T![false]); + /// assert_eq!(StaticValue::Boolean(bool.clone()).range(), bool.text_trimmed_range()); /// ``` - pub fn is_string_constant(&self, text: &str) -> bool { + pub fn range(&self) -> TextRange { match self { - StaticValue::String(_) | StaticValue::TemplateChunk(_) => self.text() == text, - _ => false, + StaticValue::Boolean(token) + | StaticValue::Null(token) + | StaticValue::Undefined(token) + | StaticValue::Number(token) + | StaticValue::BigInt(token) + | StaticValue::String(token) => token.text_trimmed_range(), + StaticValue::EmptyString(range) => *range, } } @@ -180,17 +101,17 @@ impl StaticValue { /// ## Examples /// /// ``` - /// use rome_js_syntax::static_value::{StaticValue, QuotedString}; - /// use rome_js_factory::make::{js_string_literal_expression, ident}; + /// use rome_js_syntax::static_value::StaticValue; + /// use rome_js_factory::make; /// use rome_rowan::TriviaPieceKind; /// - /// let ident = ident("\"foo\"").with_leading_trivia(vec![(TriviaPieceKind::Whitespace, " ")]); - /// let quoted_string = QuotedString::new(ident); - /// assert!(StaticValue::String(quoted_string).is_not_string_constant("bar")); + /// let str_literal = make::js_string_literal("foo") + /// .with_leading_trivia(vec![(TriviaPieceKind::Whitespace, " ")]); + /// assert!(StaticValue::String(str_literal).is_not_string_constant("bar")); /// ``` pub fn is_not_string_constant(&self, text: &str) -> bool { match self { - StaticValue::String(_) | StaticValue::TemplateChunk(_) => self.text() != text, + StaticValue::String(_) | StaticValue::EmptyString(_) => self.text() != text, _ => false, } } @@ -202,17 +123,17 @@ impl StaticValue { /// ## Examples /// /// ``` - /// use rome_js_syntax::static_value::{StaticValue, QuotedString}; - /// use rome_js_factory::make::{js_string_literal_expression, ident}; + /// use rome_js_syntax::static_value::StaticValue; + /// use rome_js_factory::make; /// use rome_rowan::TriviaPieceKind; /// - /// let ident = ident("\"foo\"").with_leading_trivia(vec![(TriviaPieceKind::Whitespace, " ")]); - /// let quoted_string = QuotedString::new(ident); - /// assert_eq!(StaticValue::String(quoted_string).as_string_constant().unwrap(), "foo"); + /// let str_literal = make::js_string_literal("foo") + /// .with_leading_trivia(vec![(TriviaPieceKind::Whitespace, " ")]); + /// assert_eq!(StaticValue::String(str_literal).as_string_constant().unwrap(), "foo"); /// ``` pub fn as_string_constant(&self) -> Option<&str> { match self { - StaticValue::String(_) | StaticValue::TemplateChunk(_) => Some(self.text()), + StaticValue::String(_) | StaticValue::EmptyString(_) => Some(self.text()), _ => None, } } diff --git a/crates/rome_json_syntax/src/lib.rs b/crates/rome_json_syntax/src/lib.rs index 9d8790cbb9c..95d4bc6a7a5 100644 --- a/crates/rome_json_syntax/src/lib.rs +++ b/crates/rome_json_syntax/src/lib.rs @@ -10,7 +10,7 @@ pub use file_source::JsonFileSource; pub use rome_rowan::{TextLen, TextRange, TextSize, TokenAtOffset, TriviaPieceKind, WalkEvent}; pub use syntax_node::*; -use rome_rowan::RawSyntaxKind; +use rome_rowan::{RawSyntaxKind, TokenText}; impl From for JsonSyntaxKind { fn from(d: u16) -> JsonSyntaxKind { @@ -105,3 +105,15 @@ impl TryFrom for TriviaPieceKind { } } } + +/// Text of `token`, excluding all trivia and removing quotes if `token` is a string literal. +pub fn inner_string_text(token: &JsonSyntaxToken) -> TokenText { + let mut text = token.token_text_trimmed(); + if token.kind() == JsonSyntaxKind::JSON_STRING_LITERAL { + // remove string delimiters + // SAFETY: string literal token have a delimiters at the start and the end of the string + let range = TextRange::new(1.into(), text.len() - TextSize::from(1)); + text = text.slice(range); + } + text +} diff --git a/crates/rome_json_syntax/src/member_ext.rs b/crates/rome_json_syntax/src/member_ext.rs index 850755c1304..f78bf5659af 100644 --- a/crates/rome_json_syntax/src/member_ext.rs +++ b/crates/rome_json_syntax/src/member_ext.rs @@ -1,23 +1,8 @@ -use crate::JsonMemberName; -use rome_rowan::{SyntaxResult, TextRange, TextSize, TokenText}; +use crate::{inner_string_text, JsonMemberName}; +use rome_rowan::{SyntaxResult, TokenText}; impl JsonMemberName { pub fn inner_string_text(&self) -> SyntaxResult { - let value = self.value_token()?; - let mut text = value.token_text_trimmed(); - - static QUOTES: [char; 1] = ['"']; - - if text.starts_with(QUOTES) { - let range = TextRange::new(1.into(), text.len()); - text = text.slice(range); - } - - if text.ends_with(QUOTES) { - let range = TextRange::new(0.into(), text.len() - TextSize::from(1)); - text = text.slice(range); - } - - Ok(text) + Ok(inner_string_text(&self.value_token()?)) } } diff --git a/crates/rome_json_syntax/src/string_ext.rs b/crates/rome_json_syntax/src/string_ext.rs index 2482ae17a99..edf1da8ea56 100644 --- a/crates/rome_json_syntax/src/string_ext.rs +++ b/crates/rome_json_syntax/src/string_ext.rs @@ -1,23 +1,8 @@ -use crate::{JsonStringValue, TextSize}; -use rome_rowan::{SyntaxResult, TextRange, TokenText}; +use crate::{inner_string_text, JsonStringValue}; +use rome_rowan::{SyntaxResult, TokenText}; impl JsonStringValue { pub fn inner_string_text(&self) -> SyntaxResult { - let value = self.value_token()?; - let mut text = value.token_text_trimmed(); - - static QUOTES: [char; 1] = ['"']; - - if text.starts_with(QUOTES) { - let range = TextRange::new(1.into(), text.len()); - text = text.slice(range); - } - - if text.ends_with(QUOTES) { - let range = TextRange::new(0.into(), text.len() - TextSize::from(1)); - text = text.slice(range); - } - - Ok(text) + Ok(inner_string_text(&self.value_token()?)) } }