error_reporting.rs (20719B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 5 //! Types used to report parsing errors. 6 7 #![deny(missing_docs)] 8 9 use crate::selector_parser::SelectorImpl; 10 use crate::stylesheets::UrlExtraData; 11 use cssparser::{BasicParseErrorKind, ParseErrorKind, SourceLocation, Token}; 12 use selectors::parser::{Combinator, Component, RelativeSelector, Selector}; 13 use selectors::visitor::{SelectorListKind, SelectorVisitor}; 14 use selectors::SelectorList; 15 use std::fmt; 16 use style_traits::ParseError; 17 18 /// Errors that can be encountered while parsing CSS. 19 #[derive(Debug)] 20 pub enum ContextualParseError<'a> { 21 /// A property declaration was not recognized. 22 UnsupportedPropertyDeclaration(&'a str, ParseError<'a>, &'a [SelectorList<SelectorImpl>]), 23 /// A property descriptor was not recognized. 24 UnsupportedPropertyDescriptor(&'a str, ParseError<'a>), 25 /// A font face descriptor was not recognized. 26 UnsupportedFontFaceDescriptor(&'a str, ParseError<'a>), 27 /// A font feature values descriptor was not recognized. 28 UnsupportedFontFeatureValuesDescriptor(&'a str, ParseError<'a>), 29 /// A font palette values descriptor was not recognized. 30 UnsupportedFontPaletteValuesDescriptor(&'a str, ParseError<'a>), 31 /// A keyframe rule was not valid. 32 InvalidKeyframeRule(&'a str, ParseError<'a>), 33 /// A font feature values rule was not valid. 34 InvalidFontFeatureValuesRule(&'a str, ParseError<'a>), 35 /// A rule was invalid for some reason. 36 InvalidRule(&'a str, ParseError<'a>), 37 /// A rule was not recognized. 38 UnsupportedRule(&'a str, ParseError<'a>), 39 /// A viewport descriptor declaration was not recognized. 40 UnsupportedViewportDescriptorDeclaration(&'a str, ParseError<'a>), 41 /// A counter style descriptor declaration was not recognized. 42 UnsupportedCounterStyleDescriptorDeclaration(&'a str, ParseError<'a>), 43 /// A counter style rule had no symbols. 44 InvalidCounterStyleWithoutSymbols(String), 45 /// A counter style rule had less than two symbols. 46 InvalidCounterStyleNotEnoughSymbols(String), 47 /// A counter style rule did not have additive-symbols. 48 InvalidCounterStyleWithoutAdditiveSymbols, 49 /// A counter style rule had extends with symbols. 50 InvalidCounterStyleExtendsWithSymbols, 51 /// A counter style rule had extends with additive-symbols. 52 InvalidCounterStyleExtendsWithAdditiveSymbols, 53 /// A media rule was invalid for some reason. 54 InvalidMediaRule(&'a str, ParseError<'a>), 55 /// A value was not recognized. 56 UnsupportedValue(&'a str, ParseError<'a>), 57 /// A never-matching `:host` selector was found. 58 NeverMatchingHostSelector(String), 59 } 60 61 impl<'a> fmt::Display for ContextualParseError<'a> { 62 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 63 fn token_to_str(t: &Token, f: &mut fmt::Formatter) -> fmt::Result { 64 match *t { 65 Token::Ident(ref i) => write!(f, "identifier {}", i), 66 Token::AtKeyword(ref kw) => write!(f, "keyword @{}", kw), 67 Token::Hash(ref h) => write!(f, "hash #{}", h), 68 Token::IDHash(ref h) => write!(f, "id selector #{}", h), 69 Token::QuotedString(ref s) => write!(f, "quoted string \"{}\"", s), 70 Token::UnquotedUrl(ref u) => write!(f, "url {}", u), 71 Token::Delim(ref d) => write!(f, "delimiter {}", d), 72 Token::Number { 73 int_value: Some(i), .. 74 } => write!(f, "number {}", i), 75 Token::Number { value, .. } => write!(f, "number {}", value), 76 Token::Percentage { 77 int_value: Some(i), .. 78 } => write!(f, "percentage {}", i), 79 Token::Percentage { unit_value, .. } => { 80 write!(f, "percentage {}", unit_value * 100.) 81 }, 82 Token::Dimension { 83 value, ref unit, .. 84 } => write!(f, "dimension {}{}", value, unit), 85 Token::WhiteSpace(_) => write!(f, "whitespace"), 86 Token::Comment(_) => write!(f, "comment"), 87 Token::Colon => write!(f, "colon (:)"), 88 Token::Semicolon => write!(f, "semicolon (;)"), 89 Token::Comma => write!(f, "comma (,)"), 90 Token::IncludeMatch => write!(f, "include match (~=)"), 91 Token::DashMatch => write!(f, "dash match (|=)"), 92 Token::PrefixMatch => write!(f, "prefix match (^=)"), 93 Token::SuffixMatch => write!(f, "suffix match ($=)"), 94 Token::SubstringMatch => write!(f, "substring match (*=)"), 95 Token::CDO => write!(f, "CDO (<!--)"), 96 Token::CDC => write!(f, "CDC (-->)"), 97 Token::Function(ref name) => write!(f, "function {}", name), 98 Token::ParenthesisBlock => write!(f, "parenthesis ("), 99 Token::SquareBracketBlock => write!(f, "square bracket ["), 100 Token::CurlyBracketBlock => write!(f, "curly bracket {{"), 101 Token::BadUrl(ref _u) => write!(f, "bad url parse error"), 102 Token::BadString(ref _s) => write!(f, "bad string parse error"), 103 Token::CloseParenthesis => write!(f, "unmatched close parenthesis"), 104 Token::CloseSquareBracket => write!(f, "unmatched close square bracket"), 105 Token::CloseCurlyBracket => write!(f, "unmatched close curly bracket"), 106 } 107 } 108 109 fn parse_error_to_str(err: &ParseError, f: &mut fmt::Formatter) -> fmt::Result { 110 match err.kind { 111 ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(ref t)) => { 112 write!(f, "found unexpected ")?; 113 token_to_str(t, f) 114 }, 115 ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput) => { 116 write!(f, "unexpected end of input") 117 }, 118 ParseErrorKind::Basic(BasicParseErrorKind::AtRuleInvalid(ref i)) => { 119 write!(f, "@ rule invalid: {}", i) 120 }, 121 ParseErrorKind::Basic(BasicParseErrorKind::AtRuleBodyInvalid) => { 122 write!(f, "@ rule invalid") 123 }, 124 ParseErrorKind::Basic(BasicParseErrorKind::QualifiedRuleInvalid) => { 125 write!(f, "qualified rule invalid") 126 }, 127 ParseErrorKind::Custom(ref err) => write!(f, "{:?}", err), 128 } 129 } 130 131 match *self { 132 ContextualParseError::UnsupportedPropertyDeclaration(decl, ref err, _selectors) => { 133 write!(f, "Unsupported property declaration: '{}', ", decl)?; 134 parse_error_to_str(err, f) 135 }, 136 ContextualParseError::UnsupportedPropertyDescriptor(decl, ref err) => { 137 write!( 138 f, 139 "Unsupported @property descriptor declaration: '{}', ", 140 decl 141 )?; 142 parse_error_to_str(err, f) 143 }, 144 ContextualParseError::UnsupportedFontFaceDescriptor(decl, ref err) => { 145 write!( 146 f, 147 "Unsupported @font-face descriptor declaration: '{}', ", 148 decl 149 )?; 150 parse_error_to_str(err, f) 151 }, 152 ContextualParseError::UnsupportedFontFeatureValuesDescriptor(decl, ref err) => { 153 write!( 154 f, 155 "Unsupported @font-feature-values descriptor declaration: '{}', ", 156 decl 157 )?; 158 parse_error_to_str(err, f) 159 }, 160 ContextualParseError::UnsupportedFontPaletteValuesDescriptor(decl, ref err) => { 161 write!( 162 f, 163 "Unsupported @font-palette-values descriptor declaration: '{}', ", 164 decl 165 )?; 166 parse_error_to_str(err, f) 167 }, 168 ContextualParseError::InvalidKeyframeRule(rule, ref err) => { 169 write!(f, "Invalid keyframe rule: '{}', ", rule)?; 170 parse_error_to_str(err, f) 171 }, 172 ContextualParseError::InvalidFontFeatureValuesRule(rule, ref err) => { 173 write!(f, "Invalid font feature value rule: '{}', ", rule)?; 174 parse_error_to_str(err, f) 175 }, 176 ContextualParseError::InvalidRule(rule, ref err) => { 177 write!(f, "Invalid rule: '{}', ", rule)?; 178 parse_error_to_str(err, f) 179 }, 180 ContextualParseError::UnsupportedRule(rule, ref err) => { 181 write!(f, "Unsupported rule: '{}', ", rule)?; 182 parse_error_to_str(err, f) 183 }, 184 ContextualParseError::UnsupportedViewportDescriptorDeclaration(decl, ref err) => { 185 write!( 186 f, 187 "Unsupported @viewport descriptor declaration: '{}', ", 188 decl 189 )?; 190 parse_error_to_str(err, f) 191 }, 192 ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(decl, ref err) => { 193 write!( 194 f, 195 "Unsupported @counter-style descriptor declaration: '{}', ", 196 decl 197 )?; 198 parse_error_to_str(err, f) 199 }, 200 ContextualParseError::InvalidCounterStyleWithoutSymbols(ref system) => write!( 201 f, 202 "Invalid @counter-style rule: 'system: {}' without 'symbols'", 203 system 204 ), 205 ContextualParseError::InvalidCounterStyleNotEnoughSymbols(ref system) => write!( 206 f, 207 "Invalid @counter-style rule: 'system: {}' less than two 'symbols'", 208 system 209 ), 210 ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols => write!( 211 f, 212 "Invalid @counter-style rule: 'system: additive' without 'additive-symbols'" 213 ), 214 ContextualParseError::InvalidCounterStyleExtendsWithSymbols => write!( 215 f, 216 "Invalid @counter-style rule: 'system: extends …' with 'symbols'" 217 ), 218 ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols => write!( 219 f, 220 "Invalid @counter-style rule: 'system: extends …' with 'additive-symbols'" 221 ), 222 ContextualParseError::InvalidMediaRule(media_rule, ref err) => { 223 write!(f, "Invalid media rule: {}, ", media_rule)?; 224 parse_error_to_str(err, f) 225 }, 226 ContextualParseError::UnsupportedValue(_value, ref err) => parse_error_to_str(err, f), 227 ContextualParseError::NeverMatchingHostSelector(ref selector) => { 228 write!(f, ":host selector is not featureless: {}", selector) 229 }, 230 } 231 } 232 } 233 234 /// A generic trait for an error reporter. 235 pub trait ParseErrorReporter { 236 /// Called when the style engine detects an error. 237 /// 238 /// Returns the current input being parsed, the source location it was 239 /// reported from, and a message. 240 fn report_error( 241 &self, 242 url: &UrlExtraData, 243 location: SourceLocation, 244 error: ContextualParseError, 245 ); 246 } 247 248 /// An error reporter that uses [the `log` crate](https://github.com/rust-lang-nursery/log) 249 /// at `info` level. 250 /// 251 /// This logging is silent by default, and can be enabled with a `RUST_LOG=style=info` 252 /// environment variable. 253 /// (See [`env_logger`](https://rust-lang-nursery.github.io/log/env_logger/).) 254 #[cfg(feature = "servo")] 255 pub struct RustLogReporter; 256 257 #[cfg(feature = "servo")] 258 impl ParseErrorReporter for RustLogReporter { 259 fn report_error( 260 &self, 261 url: &UrlExtraData, 262 location: SourceLocation, 263 error: ContextualParseError, 264 ) { 265 if log_enabled!(log::Level::Info) { 266 info!( 267 "Url:\t{}\n{}:{} {}", 268 url.as_str(), 269 location.line, 270 location.column, 271 error 272 ) 273 } 274 } 275 } 276 277 /// Any warning a selector may generate. 278 /// TODO(dshin): Bug 1860634 - Merge with never matching host selector warning, which is part of the rule parser. 279 #[repr(u8)] 280 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 281 pub enum SelectorWarningKind { 282 /// Relative Selector with not enough constraint, either outside or inside the selector. e.g. `*:has(.a)`, `.a:has(*)`. 283 /// May cause expensive invalidations for every element inserted and/or removed. 284 UnconstraintedRelativeSelector, 285 /// `:scope` can have 3 meanings, but in all cases, the relationship is defined strictly by an ancestor-descendant 286 /// relationship. This means that any presence of sibling selectors to its right would make it never match. 287 SiblingCombinatorAfterScopeSelector, 288 } 289 290 impl SelectorWarningKind { 291 /// Get all warnings for this selector. 292 pub fn from_selector(selector: &Selector<SelectorImpl>) -> Vec<Self> { 293 let mut result = vec![]; 294 if UnconstrainedRelativeSelectorVisitor::has_warning(selector, 0, false) { 295 result.push(SelectorWarningKind::UnconstraintedRelativeSelector); 296 } 297 if SiblingCombinatorAfterScopeSelectorVisitor::has_warning(selector) { 298 result.push(SelectorWarningKind::SiblingCombinatorAfterScopeSelector); 299 } 300 result 301 } 302 } 303 304 /// Per-compound state for finding unconstrained relative selectors. 305 struct PerCompoundState { 306 /// Is there a relative selector in this compound? 307 relative_selector_found: bool, 308 /// Is this compound constrained in any way? 309 constrained: bool, 310 /// Nested below, or inside relative selector? 311 in_relative_selector: bool, 312 } 313 314 impl PerCompoundState { 315 fn new(in_relative_selector: bool) -> Self { 316 Self { 317 relative_selector_found: false, 318 constrained: false, 319 in_relative_selector, 320 } 321 } 322 } 323 324 /// Visitor to check if there's any unconstrained relative selector. 325 struct UnconstrainedRelativeSelectorVisitor { 326 compound_state: PerCompoundState, 327 } 328 329 impl UnconstrainedRelativeSelectorVisitor { 330 fn new(in_relative_selector: bool) -> Self { 331 Self { 332 compound_state: PerCompoundState::new(in_relative_selector), 333 } 334 } 335 336 fn has_warning( 337 selector: &Selector<SelectorImpl>, 338 offset: usize, 339 in_relative_selector: bool, 340 ) -> bool { 341 let relative_selector = matches!( 342 selector.iter_raw_parse_order_from(0).next().unwrap(), 343 Component::RelativeSelectorAnchor 344 ); 345 debug_assert!( 346 !relative_selector || offset == 0, 347 "Checking relative selector from non-rightmost?" 348 ); 349 let mut visitor = Self::new(in_relative_selector); 350 let mut iter = if relative_selector { 351 selector.iter_skip_relative_selector_anchor() 352 } else { 353 selector.iter_from(offset) 354 }; 355 loop { 356 visitor.compound_state = PerCompoundState::new(in_relative_selector); 357 358 for s in &mut iter { 359 s.visit(&mut visitor); 360 } 361 362 if (visitor.compound_state.relative_selector_found 363 || visitor.compound_state.in_relative_selector) 364 && !visitor.compound_state.constrained 365 { 366 return true; 367 } 368 369 if iter.next_sequence().is_none() { 370 break; 371 } 372 } 373 false 374 } 375 } 376 377 impl SelectorVisitor for UnconstrainedRelativeSelectorVisitor { 378 type Impl = SelectorImpl; 379 380 fn visit_simple_selector(&mut self, c: &Component<Self::Impl>) -> bool { 381 match c { 382 // Deferred to visit_selector_list 383 Component::Is(..) 384 | Component::Where(..) 385 | Component::Negation(..) 386 | Component::Has(..) => (), 387 Component::ExplicitUniversalType => (), 388 _ => self.compound_state.constrained |= true, 389 }; 390 true 391 } 392 393 fn visit_selector_list( 394 &mut self, 395 _list_kind: SelectorListKind, 396 list: &[Selector<Self::Impl>], 397 ) -> bool { 398 let mut all_constrained = true; 399 for s in list { 400 let mut offset = 0; 401 // First, check the rightmost compound for constraint at this level. 402 if !self.compound_state.in_relative_selector { 403 let mut nested = Self::new(false); 404 let mut iter = s.iter(); 405 loop { 406 for c in &mut iter { 407 c.visit(&mut nested); 408 offset += 1; 409 } 410 411 let c = iter.next_sequence(); 412 offset += 1; 413 if c.map_or(true, |c| !c.is_pseudo_element()) { 414 break; 415 } 416 } 417 // Every single selector in the list must be constrained. 418 all_constrained &= nested.compound_state.constrained; 419 } 420 421 if offset >= s.len() { 422 continue; 423 } 424 425 // Then, recurse in to check at the deeper level. 426 if Self::has_warning(s, offset, self.compound_state.in_relative_selector) { 427 self.compound_state.constrained = false; 428 if !self.compound_state.in_relative_selector { 429 self.compound_state.relative_selector_found = true; 430 } 431 return false; 432 } 433 } 434 self.compound_state.constrained |= all_constrained; 435 true 436 } 437 438 fn visit_relative_selector_list(&mut self, list: &[RelativeSelector<Self::Impl>]) -> bool { 439 debug_assert!( 440 !self.compound_state.in_relative_selector, 441 "Nested relative selector" 442 ); 443 self.compound_state.relative_selector_found = true; 444 445 for rs in list { 446 // If the inside is unconstrained, we are unconstrained no matter what. 447 if Self::has_warning(&rs.selector, 0, true) { 448 self.compound_state.constrained = false; 449 return false; 450 } 451 } 452 true 453 } 454 } 455 456 struct SiblingCombinatorAfterScopeSelectorVisitor { 457 right_combinator_is_sibling: bool, 458 found: bool, 459 } 460 461 impl SiblingCombinatorAfterScopeSelectorVisitor { 462 fn new(right_combinator_is_sibling: bool) -> Self { 463 Self { 464 right_combinator_is_sibling, 465 found: false, 466 } 467 } 468 fn has_warning(selector: &Selector<SelectorImpl>) -> bool { 469 if !selector.has_scope_selector() { 470 return false; 471 } 472 let visitor = SiblingCombinatorAfterScopeSelectorVisitor::new(false); 473 visitor.find_never_matching_scope_selector(selector) 474 } 475 476 fn find_never_matching_scope_selector(mut self, selector: &Selector<SelectorImpl>) -> bool { 477 selector.visit(&mut self); 478 self.found 479 } 480 } 481 482 impl SelectorVisitor for SiblingCombinatorAfterScopeSelectorVisitor { 483 type Impl = SelectorImpl; 484 485 fn visit_simple_selector(&mut self, c: &Component<Self::Impl>) -> bool { 486 if !matches!(c, Component::Scope | Component::ImplicitScope) { 487 return true; 488 } 489 // e.g. `:scope ~ .a` will never match. 490 if self.right_combinator_is_sibling { 491 self.found = true; 492 } 493 true 494 } 495 496 fn visit_selector_list( 497 &mut self, 498 _list_kind: SelectorListKind, 499 list: &[Selector<Self::Impl>], 500 ) -> bool { 501 for s in list { 502 let list_visitor = Self::new(self.right_combinator_is_sibling); 503 self.found |= list_visitor.find_never_matching_scope_selector(s); 504 } 505 true 506 } 507 508 fn visit_complex_selector(&mut self, combinator_to_right: Option<Combinator>) -> bool { 509 if let Some(c) = combinator_to_right { 510 // Subject compounds' state is determined by the outer visitor. e.g: When there's `:is(.a .b) ~ .c`, 511 // the inner visitor is assumed to be constructed with right_combinator_is_sibling == true. 512 self.right_combinator_is_sibling = c.is_sibling(); 513 } 514 true 515 } 516 517 // It's harder to discern if use of :scope <sibling-combinator> is invalid - at least for now, defer. 518 }