+
Skip to content
This repository was archived by the owner on Aug 31, 2023. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions crates/rome_deserialize/src/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ pub trait VisitJsonNode: VisitNode<JsonLanguage> {
) -> Option<(SyntaxTokenText, AnyJsonValue)> {
let member = key.clone().cast::<JsonMemberName>()?;
self.visit_member_name(member.syntax(), diagnostics)?;
let name = member.inner_string_text().ok()?;
let name = member.inner_text().ok()?;
let value = value.clone().cast::<AnyJsonValue>()?;

Some((name, value))
Expand Down Expand Up @@ -100,7 +100,7 @@ pub trait VisitJsonNode: VisitNode<JsonLanguage> {
diagnostics: &mut Vec<DeserializationDiagnostic>,
) -> Option<String> {
JsonStringValue::cast_ref(value.syntax())
.and_then(|node| Some(node.inner_string_text().ok()?.to_string()))
.and_then(|node| Some(node.inner_text().ok()?.to_string()))
.or_else(|| {
diagnostics.push(DeserializationDiagnostic::new_incorrect_type_for_value(
name,
Expand Down Expand Up @@ -328,7 +328,7 @@ pub trait VisitJsonNode: VisitNode<JsonLanguage> {
let element = element.ok()?;
match element {
AnyJsonValue::JsonStringValue(value) => {
elements.insert(value.inner_string_text().ok()?.to_string());
elements.insert(value.inner_text().ok()?.to_string());
}
_ => {
diagnostics.push(DeserializationDiagnostic::new_incorrect_type(
Expand Down Expand Up @@ -437,7 +437,7 @@ pub fn has_only_known_keys(
diagnostics: &mut Vec<DeserializationDiagnostic>,
) -> Option<()> {
node.clone().cast::<JsonMemberName>().and_then(|node| {
let key_name = node.inner_string_text().ok()?;
let key_name = node.inner_text().ok()?;
if allowed_keys.contains(&key_name.text()) {
Some(())
} else {
Expand All @@ -462,7 +462,7 @@ pub fn with_only_known_variants(
diagnostics: &mut Vec<DeserializationDiagnostic>,
) -> Option<JsonStringValue> {
node.clone().cast::<JsonStringValue>().and_then(|node| {
let key_name = node.inner_string_text().ok()?;
let key_name = node.inner_text().ok()?;
if allowed_keys.contains(&key_name.text()) {
Some(node)
} else {
Expand Down
39 changes: 3 additions & 36 deletions crates/rome_formatter/src/builders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,10 +335,7 @@ impl<L: Language, Context> Format<Context> for SyntaxTokenCowSlice<'_, L> {
let relative_range = range - self.token.text_range().start();
let slice = self.token.token_text().slice(relative_range);

f.write_element(FormatElement::SyntaxTokenTextSlice {
slice,
source_position: self.start,
})
f.write_element(FormatElement::SyntaxTokenText(slice))
}
Cow::Owned(text) => f.write_element(FormatElement::DynamicText {
text: text.to_string().into_boxed_str(),
Expand All @@ -354,39 +351,9 @@ impl<L: Language> std::fmt::Debug for SyntaxTokenCowSlice<'_, L> {
}
}

/// Copies a source text 1:1 into the output text.
pub fn syntax_token_text_slice<L: Language>(
token: &SyntaxToken<L>,
range: TextRange,
) -> SyntaxTokenTextSlice {
let relative_range = range - token.text_range().start();
let slice = token.token_text().slice(relative_range);

debug_assert_no_newlines(&slice);

SyntaxTokenTextSlice {
text: slice,
source_position: range.start(),
}
}

pub struct SyntaxTokenTextSlice {
text: SyntaxTokenText,
source_position: TextSize,
}

impl<Context> Format<Context> for SyntaxTokenTextSlice {
impl<Context> Format<Context> for SyntaxTokenText {
fn fmt(&self, f: &mut Formatter<Context>) -> FormatResult<()> {
f.write_element(FormatElement::SyntaxTokenTextSlice {
slice: self.text.clone(),
source_position: self.source_position,
})
}
}

impl std::fmt::Debug for SyntaxTokenTextSlice {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::write!(f, "SyntaxTokenTextSlice({})", self.text)
f.write_element(FormatElement::SyntaxTokenText(self.clone()))
}
}

Expand Down
20 changes: 7 additions & 13 deletions crates/rome_formatter/src/format_element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,7 @@ pub enum FormatElement {

/// A token for a text that is taken as is from the source code (input text and formatted representation are identical).
/// Implementing by taking a slice from a `SyntaxToken` to avoid allocating a new string.
SyntaxTokenTextSlice {
/// The start position of the token in the unformatted source code
source_position: TextSize,
/// The token text
slice: SyntaxTokenText,
},
SyntaxTokenText(SyntaxTokenText),

/// Prevents that line suffixes move past this boundary. Forces the printer to print any pending
/// line suffixes, potentially by inserting a hard line break.
Expand Down Expand Up @@ -75,10 +70,9 @@ impl std::fmt::Debug for FormatElement {
FormatElement::DynamicText { text, .. } => {
fmt.debug_tuple("DynamicText").field(text).finish()
}
FormatElement::SyntaxTokenTextSlice { slice, .. } => fmt
.debug_tuple("SyntaxTokenTextSlice")
.field(slice)
.finish(),
FormatElement::SyntaxTokenText(slice) => {
fmt.debug_tuple("SyntaxTokenText").field(slice).finish()
}
FormatElement::LineSuffixBoundary => write!(fmt, "LineSuffixBoundary"),
FormatElement::BestFitting(best_fitting) => {
fmt.debug_tuple("BestFitting").field(&best_fitting).finish()
Expand Down Expand Up @@ -224,7 +218,7 @@ impl FormatElement {
pub const fn is_text(&self) -> bool {
matches!(
self,
FormatElement::SyntaxTokenTextSlice { .. }
FormatElement::SyntaxTokenText { .. }
| FormatElement::DynamicText { .. }
| FormatElement::StaticText { .. }
)
Expand All @@ -243,7 +237,7 @@ impl FormatElements for FormatElement {
FormatElement::Line(line_mode) => matches!(line_mode, LineMode::Hard | LineMode::Empty),
FormatElement::StaticText { text } => text.contains('\n'),
FormatElement::DynamicText { text, .. } => text.contains('\n'),
FormatElement::SyntaxTokenTextSlice { slice, .. } => slice.contains('\n'),
FormatElement::SyntaxTokenText(slice) => slice.contains('\n'),
FormatElement::Interned(interned) => interned.will_break(),
// Traverse into the most flat version because the content is guaranteed to expand when even
// the most flat version contains some content that forces a break.
Expand Down Expand Up @@ -392,4 +386,4 @@ static_assert!(std::mem::size_of::<crate::format_element::Tag>() == 16usize);

#[cfg(not(debug_assertions))]
#[cfg(target_pointer_width = "64")]
static_assert!(std::mem::size_of::<crate::FormatElement>() == 24usize);
static_assert!(std::mem::size_of::<crate::FormatElement>() == 32usize);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why the size changed.
Moreover, I cannot reproduce this test locally.
Before and after this PR, the size of FormatElement is evaluated to 40 bytes on my machine.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you measure the size with a release build? Some elements contain debug only data

Regressing the size has negative consequences on overall performance and memory consumption

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I accept with your point. However, I just merged SyntaxTokenTextSlice into SyntaxTokenText. This should take the same space.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you measure the size with a release build? Some elements contain debug only data

You are right!

Copy link
Contributor

@strager strager Jul 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Old layout of format_element::FormatElement::SyntaxTokenTextSlice (24 bytes):

#[repr(C)]
// alignment=8 size=24
struct FormatElement_SyntaxTokenTextSlice {
  variant: i8,          // which element of FormatElement is this?
  padding_0: [u8; 3],   // for text_size alignment
  text_size: u32,       // rome_text_size::TextSize::raw

  // Begin rome_rowan::SyntaxTokenText (alignment=8 size=16)
  green_token_ptr: u64, // rome_rowan::GreenToken::ptr (rome_rowan::ThinArc::ptr (pointer))
  range_start: u32,     // rome_text_size::TextRange::start (rome_text_size::TextSize::raw)
  range_end: u32,       // rome_text_size::TextRange::start (rome_text_size::TextSize::raw)
  // End rome_rowan::SyntaxTokenText (alignment=8 size=16)
}

New layout of format_element::FormatElement::SyntaxTokenTextSlice (32 bytes):

#[repr(C)]
// alignment=8 size=32
struct FormatElement_SyntaxTokenTextSlice {
  variant: i8,          // which element of FormatElement is this?
  padding_0: [u8; 7],   // for rome_rowan::SyntaxTokenText alignment

  // Begin rome_rowan::SyntaxTokenText (alignment=8 size=24)
  text_size: u32,       // rome_text_size::TextSize::raw
  padding_1: [u8; 4],   // for green_token_ptr alignment
  green_token_ptr: u64, // rome_rowan::GreenToken::ptr (rome_rowan::ThinArc::ptr (pointer))
  range_start: u32,     // rome_text_size::TextRange::start (rome_text_size::TextSize::raw)
  range_end: u32,       // rome_text_size::TextRange::start (rome_text_size::TextSize::raw)
  // End rome_rowan::SyntaxTokenText (alignment=8 size=24)
}

Before, the space between format_element::FormatElement's discriminator and the rome_rowan::SyntaxTokenText was used to fit text_size. After, this space cannot be used. +4 bytes

After, text_size forces padding inside rome_rowan::SyntaxTokenText. The size of the members is 20 bytes, but because the struct needs to be 8-byte-aligned (because rome_rowan::GreenToken is 8-byte-aligned because pointers are 8-byte-aligned), 4 padding bytes are added. +4 bytes

+4 bytes + +4 bytes = +8 bytes


This patch helped my investigations (building with cargo +nightly build --release):

diff --git a/crates/rome_formatter/src/format_element.rs b/crates/rome_formatter/src/format_element.rs
index fcb0e92d33..3d6d3e3250 100644
--- a/crates/rome_formatter/src/format_element.rs
+++ b/crates/rome_formatter/src/format_element.rs
@@ -16,6 +16,7 @@ use std::rc::Rc;
 ///
 /// Use the helper functions like [crate::builders::space], [crate::builders::soft_line_break] etc. defined in this file to create elements.
 #[derive(Clone, Eq, PartialEq)]
+#[rustc_layout(debug)]
 pub enum FormatElement {
     /// A space token, see [crate::builders::space] for documentation.
     Space,
diff --git a/crates/rome_formatter/src/lib.rs b/crates/rome_formatter/src/lib.rs
index 21a5d90997..c2322681cd 100644
--- a/crates/rome_formatter/src/lib.rs
+++ b/crates/rome_formatter/src/lib.rs
@@ -1,3 +1,5 @@
+#![feature(rustc_attrs)]
+
 //! Infrastructure for code formatting
 //!
 //! This module defines [FormatElement], an IR to format code documents and provides a mean to print
diff --git a/crates/rome_rowan/src/lib.rs b/crates/rome_rowan/src/lib.rs
index b4f5e659fb..7fc598c5fb 100644
--- a/crates/rome_rowan/src/lib.rs
+++ b/crates/rome_rowan/src/lib.rs
@@ -1,3 +1,5 @@
+#![feature(rustc_attrs)]
+
 //! A generic library for lossless syntax trees.
 //! See `examples/s_expressions.rs` for a tutorial.
 #![forbid(
diff --git a/crates/rome_rowan/src/syntax_token_text.rs b/crates/rome_rowan/src/syntax_token_text.rs
index d5eb1ddbf1..7977189bb3 100644
--- a/crates/rome_rowan/src/syntax_token_text.rs
+++ b/crates/rome_rowan/src/syntax_token_text.rs
@@ -5,6 +5,7 @@ use std::{borrow::Borrow, fmt::Formatter};
 
 /// Reference to the text of a SyntaxToken without having to worry about the lifetime of `&str`.
 #[derive(Eq, Clone)]
+#[rustc_layout(debug)]
 pub struct SyntaxTokenText {
     // Using a green token to ensure this type is Send + Sync.
     token: GreenToken,

Copy link
Contributor Author

@Conaclos Conaclos Jul 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the write-up!

I see two possibilities:

  1. Replacing TextRange with a SmallTextRange struct in SyntaxTokenText to shrink SyntaxTokenText to 16 bytes.
    This is based on the observation that a relative range does not need u32 positions. u16 seems enough.

  2. Introducing GreenTokenText and reverting to the original layout of FormatElement (using GreenTokenText).

Any opinions?

4 changes: 2 additions & 2 deletions crates/rome_formatter/src/format_element/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ impl Document {
}
FormatElement::StaticText { text } => text.contains('\n'),
FormatElement::DynamicText { text, .. } => text.contains('\n'),
FormatElement::SyntaxTokenTextSlice { slice, .. } => slice.contains('\n'),
FormatElement::SyntaxTokenText(slice) => slice.contains('\n'),
FormatElement::ExpandParent
| FormatElement::Line(LineMode::Hard | LineMode::Empty) => true,
_ => false,
Expand Down Expand Up @@ -194,7 +194,7 @@ impl Format<IrFormatContext> for &[FormatElement] {
element @ FormatElement::Space
| element @ FormatElement::StaticText { .. }
| element @ FormatElement::DynamicText { .. }
| element @ FormatElement::SyntaxTokenTextSlice { .. } => {
| element @ FormatElement::SyntaxTokenText { .. } => {
if !in_text {
write!(f, [text("\"")])?;
}
Expand Down
9 changes: 4 additions & 5 deletions crates/rome_formatter/src/printer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,9 @@ impl<'a> Printer<'a> {
text,
source_position,
} => self.print_text(text, Some(*source_position)),
FormatElement::SyntaxTokenTextSlice {
slice,
source_position,
} => self.print_text(slice, Some(*source_position)),
FormatElement::SyntaxTokenText(slice) => {
self.print_text(slice, Some(slice.range().start()))
}

FormatElement::Line(line_mode) => {
if args.mode().is_flat()
Expand Down Expand Up @@ -1001,7 +1000,7 @@ impl<'a, 'print> FitsMeasurer<'a, 'print> {

FormatElement::StaticText { text } => return Ok(self.fits_text(text)),
FormatElement::DynamicText { text, .. } => return Ok(self.fits_text(text)),
FormatElement::SyntaxTokenTextSlice { slice, .. } => return Ok(self.fits_text(slice)),
FormatElement::SyntaxTokenText(slice) => return Ok(self.fits_text(slice)),

FormatElement::LineSuffixBoundary => {
if self.state.has_line_suffix {
Expand Down
2 changes: 1 addition & 1 deletion crates/rome_formatter/src/source_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ impl TransformSourceMap {
///
/// The printer creates a source map that allows mapping positions from the newly formatted document
/// back to the locations of the tree. However, the source positions stored in [crate::FormatElement::DynamicText]
/// and [crate::FormatElement::SyntaxTokenTextSlice] are relative to the transformed tree
/// and [crate::FormatElement::SyntaxTokenText] are relative to the transformed tree
/// and not the original tree passed to [crate::format_node].
///
/// This function re-maps the positions from the positions in the transformed tree back to the positions
Expand Down
12 changes: 6 additions & 6 deletions crates/rome_formatter/src/trivia.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,7 @@ where
C: CstFormatContext<Language = L>,
{
fn fmt(&self, f: &mut Formatter<C>) -> FormatResult<()> {
let trimmed_range = self.token.text_trimmed_range();
syntax_token_text_slice(self.token, trimmed_range).fmt(f)
self.token.token_text_trimmed().fmt(f)
}
}
/// Formats the skipped token trivia of a removed token and marks the token as tracked.
Expand Down Expand Up @@ -539,15 +538,16 @@ impl<L: Language> FormatSkippedTokenTrivia<'_, L> {
}
}

let skipped_range =
skipped_range.unwrap_or_else(|| TextRange::empty(self.token.text_range().start()));
let skipped_relative_range = skipped_range
.map(|skipped_range| skipped_range - self.token.text_range().start())
.unwrap_or_default();

f.write_element(FormatElement::Tag(Tag::StartVerbatim(
VerbatimKind::Verbatim {
length: skipped_range.len(),
length: skipped_relative_range.len(),
},
)))?;
write!(f, [syntax_token_text_slice(self.token, skipped_range)])?;
write!(f, [self.token.token_text().slice(skipped_relative_range)])?;
f.write_element(FormatElement::Tag(Tag::EndVerbatim))?;

// Write whitespace separator between skipped/last comment and token
Expand Down
17 changes: 7 additions & 10 deletions crates/rome_js_analyze/src/analyzers/a11y/no_blank_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use rome_console::markup;
use rome_diagnostics::Applicability;
use rome_js_factory::make::{
jsx_attribute, jsx_attribute_initializer_clause, jsx_attribute_list, jsx_ident, jsx_name,
jsx_string, token,
jsx_string, jsx_string_literal, token,
};
use rome_js_syntax::jsx_ext::AnyJsxElement;
use rome_js_syntax::{
Expand Down Expand Up @@ -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()?.as_string_constant() == Some("_blank") {
match rel_attribute {
None => {
if !node.has_trailing_spread_prop(target_attribute.clone()) {
Expand Down Expand Up @@ -112,11 +109,11 @@ impl Rule for NoBlankTarget {
let message = if let Some(rel_attribute) = rel_attribute {
let prev_jsx_attribute = rel_attribute.initializer()?.value().ok()?;
let prev_jsx_string = prev_jsx_attribute.as_jsx_string()?;
let new_text = format!(
"\"noreferrer {}\"",
prev_jsx_string.inner_string_text().ok()?.text()
let new_text = format!("noreferrer {}", prev_jsx_string.inner_text().ok()?.text());
mutation.replace_node(
prev_jsx_string.clone(),
jsx_string(jsx_string_literal(&new_text)),
);
mutation.replace_node(prev_jsx_string.clone(), jsx_string(jsx_ident(&new_text)));

(markup! {
"Add the "<Emphasis>"\"noreferrer\""</Emphasis>" to the existing attribute."
Expand All @@ -133,7 +130,7 @@ impl Rule for NoBlankTarget {
)))
.with_initializer(jsx_attribute_initializer_clause(
token(T![=]),
AnyJsxAttributeValue::JsxString(jsx_string(jsx_ident("\"noreferrer\""))),
AnyJsxAttributeValue::JsxString(jsx_string(jsx_string_literal("noreferrer"))),
))
.build();

Expand Down
14 changes: 6 additions & 8 deletions crates/rome_js_analyze/src/analyzers/a11y/no_redundant_alt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ impl Rule for NoRedundantAlt {
== "false"
}
AnyJsxAttributeValue::JsxString(aria_hidden) => {
aria_hidden.inner_string_text().ok()?.text() == "false"
aria_hidden.inner_text().ok()?.text() == "false"
}
};

Expand All @@ -94,16 +94,14 @@ impl Rule for NoRedundantAlt {
match value.expression().ok()? {
AnyJsExpression::AnyJsLiteralExpression(
AnyJsLiteralExpression::JsStringLiteralExpression(expr),
) => {
is_redundant_alt(expr.inner_string_text().ok()?.to_string()).then_some(alt)
}
) => is_redundant_alt(expr.inner_text().ok()?.text()).then_some(alt),
AnyJsExpression::JsTemplateExpression(expr) => {
let contain_redundant_alt =
expr.elements().into_iter().any(|template_element| {
match template_element {
AnyJsTemplateElement::JsTemplateChunkElement(node) => {
node.template_chunk_token().ok().map_or(false, |token| {
is_redundant_alt(token.text_trimmed().to_string())
is_redundant_alt(token.text_trimmed())
})
}
AnyJsTemplateElement::JsTemplateElement(_) => false,
Expand All @@ -117,8 +115,8 @@ impl Rule for NoRedundantAlt {
}
}
AnyJsxAttributeValue::JsxString(ref value) => {
let text = value.inner_string_text().ok()?.to_string();
is_redundant_alt(text).then_some(alt)
let inner_text = value.inner_text().ok()?;
is_redundant_alt(inner_text.text()).then_some(alt)
}
}
}
Expand All @@ -141,7 +139,7 @@ impl Rule for NoRedundantAlt {

const REDUNDANT_WORDS: [&str; 3] = ["image", "photo", "picture"];

fn is_redundant_alt(alt: String) -> bool {
fn is_redundant_alt(alt: &str) -> bool {
REDUNDANT_WORDS
.into_iter()
.any(|word| alt.split_whitespace().any(|x| x.to_lowercase() == word))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ impl Rule for NoSvgWithoutTitle {
};

let role_attribute_value = role_attribute.initializer()?.value().ok()?;
let Some(text) = role_attribute_value.as_jsx_string()?.inner_string_text().ok() else {
let Some(text) = role_attribute_value.as_jsx_string()?.inner_text().ok() else {
return Some(())
};

Expand Down
5 changes: 3 additions & 2 deletions crates/rome_js_analyze/src/analyzers/a11y/use_alt_text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.as_string_constant() == Some("image"))
})
}

Expand All @@ -173,7 +173,8 @@ fn has_valid_label(element: &AnyJsxElement, name_to_lookup: &str) -> bool {
return false;
}
attribute.as_static_value().map_or(true, |value| {
!value.is_null_or_undefined() && value.is_not_string_constant("")
!value.is_null_or_undefined()
&& !value.as_string_constant().unwrap_or_default().is_empty()
}) && !element.has_trailing_spread_prop(attribute)
})
}
10 changes: 6 additions & 4 deletions crates/rome_js_analyze/src/analyzers/a11y/use_html_lang.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,12 @@ impl Rule for UseHtmlLang {

if name.text_trimmed() == "html" {
if let Some(lang_attribute) = element.find_attribute_by_name("lang") {
if !lang_attribute
.as_static_value()
.map_or(true, |attribute| attribute.is_not_string_constant(""))
&& !element.has_trailing_spread_prop(lang_attribute)
if !lang_attribute.as_static_value().map_or(true, |attribute| {
!attribute
.as_string_constant()
.unwrap_or_default()
.is_empty()
}) && !element.has_trailing_spread_prop(lang_attribute)
{
return Some(element.syntax().text_trimmed_range());
}
Expand Down
10 changes: 6 additions & 4 deletions crates/rome_js_analyze/src/analyzers/a11y/use_iframe_title.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,12 @@ impl Rule for UseIframeTitle {

if name.text_trimmed() == "iframe" {
if let Some(lang_attribute) = element.find_attribute_by_name("title") {
if !lang_attribute
.as_static_value()
.map_or(true, |attribute| attribute.is_not_string_constant(""))
&& !element.has_trailing_spread_prop(lang_attribute)
if !lang_attribute.as_static_value().map_or(true, |attribute| {
!attribute
.as_string_constant()
.unwrap_or_default()
.is_empty()
}) && !element.has_trailing_spread_prop(lang_attribute)
{
return Some(());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ impl Rule for UseMediaCaption {
.value()
.ok()?
.as_jsx_string()?
.inner_string_text()
.inner_text()
.ok()?
.to_lowercase()
== "captions";
Expand Down
Loading
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载