1use cstree::Syntax;
7use omena_interner::{
8 NameKind, intern_class_name, intern_css_ident, intern_custom_property_name, intern_file_path,
9 intern_keyframes_name, intern_mixin_name, intern_property_name, intern_selector_key,
10};
11use omena_syntax::{StyleDialect, SyntaxKind, ident::AuthoredPropertyTextV0};
12use serde::Serialize;
13use std::collections::{BTreeMap, BTreeSet};
14
15use crate::value_names::{CSS_COLOR_FUNCTION_NAMES, VALUES_L4_MATH_FUNCTION_NAMES};
16use crate::{
17 BuiltinDialectExtension, ParsedAnimationFactKind, ParsedCssModuleComposesEdgeKind,
18 ParsedCssModuleComposesFactKind, ParsedCssModuleValueFactKind, ParsedIcssFactKind,
19 ParsedSassModuleEdgeFactKind, ParsedSassSymbolFact, ParsedSassSymbolFactKind,
20 ParsedSelectorFactKind, ParsedStyleFacts, ParsedVariableFactKind, ParsedVariableFactNameV0,
21 SelectorBranch, Token, collect_class_selector_names_from_header, collect_style_facts,
22 css_module_block_scope_marker_in_header, css_module_value_statement_end,
23 declaration_colon_index, find_block_after_header, find_selector_block_after_header, lex,
24 matches_ignore_ascii_case, matching_right_brace, next_non_trivia_token_index_until, parse,
25 previous_non_trivia_token_index, resolve_selector_header, skip_statement_or_unmatched_boundary,
26 skip_trivia_tokens, split_selector_groups, style_wrapper_at_rule, tokenize,
27};
28
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct ParserBoundarySummary {
31 pub product: &'static str,
32 pub tree_model: &'static str,
33 pub parser_track: &'static str,
34 pub dialect_count: usize,
35 pub shared_name_kind_count: usize,
36 pub ready_surfaces: Vec<&'static str>,
37 pub not_ready_surfaces: Vec<&'static str>,
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub struct ParserSemanticNameConsumptionSummaryV0 {
42 pub product: &'static str,
43 pub dialect: StyleDialect,
44 pub semantic_name_count: usize,
45 pub interned_name_count: usize,
46 pub invalid_name_count: usize,
47 pub class_name_count: usize,
48 pub css_ident_count: usize,
49 pub property_name_count: usize,
50 pub selector_key_count: usize,
51 pub custom_property_name_count: usize,
52 pub keyframes_name_count: usize,
53 pub mixin_name_count: usize,
54 pub file_path_count: usize,
55 pub ready_surfaces: Vec<&'static str>,
56}
57
58#[derive(Debug, Clone, PartialEq, Eq)]
59pub struct ParserCstEquivalenceSummaryV0 {
60 pub product: &'static str,
61 pub dialect: StyleDialect,
62 pub root_kind: SyntaxKind,
63 pub parser_node_count: usize,
64 pub parser_token_count: usize,
65 pub typed_wrapper_count: usize,
66 pub source_text_round_trip_ready: bool,
67 pub syntax_kind_round_trip_ready: bool,
68 pub zero_unknown_kind_ready: bool,
69 pub typed_cst_wrapper_ready: bool,
70 pub ready_surfaces: Vec<&'static str>,
71}
72
73#[derive(Debug, Clone, PartialEq, Eq)]
74pub struct ParserPrattValueCoverageSummaryV0 {
75 pub product: &'static str,
76 pub infix_operator_kinds: Vec<SyntaxKind>,
77 pub prefix_operator_kinds: Vec<SyntaxKind>,
78 pub value_expression_node_kinds: Vec<SyntaxKind>,
79 pub specialized_function_family_count: usize,
80 pub css_values_l4_math_function_count: usize,
81 pub css_color_function_count: usize,
82 pub ready_surfaces: Vec<&'static str>,
83 pub next_surfaces: Vec<&'static str>,
84}
85
86#[derive(Debug, Clone, PartialEq, Eq)]
87pub struct ParserRecursiveDescentCoverageSummaryV0 {
88 pub product: &'static str,
89 pub dialect_count: usize,
90 pub entry_point_count: usize,
91 pub selector_surface_count: usize,
92 pub at_rule_surface_count: usize,
93 pub dialect_extension_surface_count: usize,
94 pub recovery_surface_count: usize,
95 pub ready_surfaces: Vec<&'static str>,
96 pub next_surfaces: Vec<&'static str>,
97}
98
99#[derive(Debug, Clone, PartialEq, Eq)]
100pub(crate) struct ParserSemanticNameCandidateV0 {
101 pub(crate) kind: NameKind,
102 pub(crate) text: String,
103}
104
105#[derive(Debug, Clone, Serialize)]
106#[serde(rename_all = "camelCase")]
107pub struct OmenaParserStyleFactsSummaryV0 {
108 pub schema_version: &'static str,
109 pub product: &'static str,
110 pub dialect: &'static str,
111 pub class_selector_names: Vec<String>,
112 pub id_selector_names: Vec<String>,
113 pub placeholder_selector_names: Vec<String>,
114 pub keyframe_names: Vec<String>,
115 pub animation_reference_names: Vec<String>,
116 pub css_module_value_definition_names: Vec<String>,
117 pub css_module_value_reference_names: Vec<String>,
118 pub css_module_value_import_sources: Vec<String>,
119 pub css_module_value_import_edges: Vec<OmenaParserCssModuleValueImportEdgeFactV0>,
120 pub css_module_value_definition_edges: Vec<OmenaParserCssModuleValueDefinitionEdgeFactV0>,
121 pub css_module_composes_target_names: Vec<String>,
122 pub css_module_composes_import_sources: Vec<String>,
123 pub css_module_composes_edges: Vec<OmenaParserCssModuleComposesEdgeFactV0>,
124 pub icss_export_names: Vec<String>,
125 pub icss_import_local_names: Vec<String>,
126 pub icss_import_remote_names: Vec<String>,
127 pub icss_import_sources: Vec<String>,
128 pub icss_import_edges: Vec<OmenaParserIcssImportEdgeFactV0>,
129 pub icss_export_edges: Vec<OmenaParserIcssExportEdgeFactV0>,
130 pub variable_names: Vec<String>,
131 pub sass_symbol_declaration_names: Vec<String>,
132 pub sass_symbol_reference_names: Vec<String>,
133 pub sass_symbol_facts: Vec<OmenaParserSassSymbolFactV0>,
134 pub sass_symbol_resolution: OmenaParserSassSymbolResolutionV0,
135 pub sass_module_use_sources: Vec<String>,
136 pub sass_module_forward_sources: Vec<String>,
137 pub sass_module_import_sources: Vec<String>,
138 pub sass_module_edges: Vec<OmenaParserSassModuleEdgeFactV0>,
139 pub custom_property_names: Vec<AuthoredPropertyTextV0>,
140 pub custom_property_decl_names: Vec<AuthoredPropertyTextV0>,
141 pub custom_property_ref_names: Vec<AuthoredPropertyTextV0>,
142 pub at_rule_names: Vec<String>,
143 pub parser_error_count: usize,
144}
145
146fn authored_custom_property_sequences_same(
147 left: &[AuthoredPropertyTextV0],
148 right: &[AuthoredPropertyTextV0],
149) -> bool {
150 left.len() == right.len()
151 && left
152 .iter()
153 .zip(right)
154 .all(|(left, right)| left.to_custom_key() == right.to_custom_key())
155}
156
157impl PartialEq for OmenaParserStyleFactsSummaryV0 {
158 fn eq(&self, other: &Self) -> bool {
159 self.schema_version == other.schema_version
160 && self.product == other.product
161 && self.dialect == other.dialect
162 && self.class_selector_names == other.class_selector_names
163 && self.id_selector_names == other.id_selector_names
164 && self.placeholder_selector_names == other.placeholder_selector_names
165 && self.keyframe_names == other.keyframe_names
166 && self.animation_reference_names == other.animation_reference_names
167 && self.css_module_value_definition_names == other.css_module_value_definition_names
168 && self.css_module_value_reference_names == other.css_module_value_reference_names
169 && self.css_module_value_import_sources == other.css_module_value_import_sources
170 && self.css_module_value_import_edges == other.css_module_value_import_edges
171 && self.css_module_value_definition_edges == other.css_module_value_definition_edges
172 && self.css_module_composes_target_names == other.css_module_composes_target_names
173 && self.css_module_composes_import_sources == other.css_module_composes_import_sources
174 && self.css_module_composes_edges == other.css_module_composes_edges
175 && self.icss_export_names == other.icss_export_names
176 && self.icss_import_local_names == other.icss_import_local_names
177 && self.icss_import_remote_names == other.icss_import_remote_names
178 && self.icss_import_sources == other.icss_import_sources
179 && self.icss_import_edges == other.icss_import_edges
180 && self.icss_export_edges == other.icss_export_edges
181 && self.variable_names == other.variable_names
182 && self.sass_symbol_declaration_names == other.sass_symbol_declaration_names
183 && self.sass_symbol_reference_names == other.sass_symbol_reference_names
184 && self.sass_symbol_facts == other.sass_symbol_facts
185 && self.sass_symbol_resolution == other.sass_symbol_resolution
186 && self.sass_module_use_sources == other.sass_module_use_sources
187 && self.sass_module_forward_sources == other.sass_module_forward_sources
188 && self.sass_module_import_sources == other.sass_module_import_sources
189 && self.sass_module_edges == other.sass_module_edges
190 && authored_custom_property_sequences_same(
191 &self.custom_property_names,
192 &other.custom_property_names,
193 )
194 && authored_custom_property_sequences_same(
195 &self.custom_property_decl_names,
196 &other.custom_property_decl_names,
197 )
198 && authored_custom_property_sequences_same(
199 &self.custom_property_ref_names,
200 &other.custom_property_ref_names,
201 )
202 && self.at_rule_names == other.at_rule_names
203 && self.parser_error_count == other.parser_error_count
204 }
205}
206
207impl Eq for OmenaParserStyleFactsSummaryV0 {}
208
209#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
210#[serde(rename_all = "camelCase")]
211pub struct OmenaParserCssModuleValueImportEdgeFactV0 {
212 pub remote_name: String,
213 pub local_name: String,
214 pub import_source: String,
215}
216
217#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
218#[serde(rename_all = "camelCase")]
219pub struct OmenaParserCssModuleValueDefinitionEdgeFactV0 {
220 pub definition_name: String,
221 pub reference_names: Vec<String>,
222}
223
224#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
225#[serde(rename_all = "camelCase")]
226pub struct OmenaParserCssModuleComposesEdgeFactV0 {
227 pub kind: &'static str,
228 pub owner_selector_names: Vec<String>,
229 pub target_names: Vec<String>,
230 pub import_source: Option<String>,
231}
232
233#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
234#[serde(rename_all = "camelCase")]
235pub struct OmenaParserIcssImportEdgeFactV0 {
236 pub local_name: String,
237 pub remote_name: String,
238 pub import_source: String,
239}
240
241#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
242#[serde(rename_all = "camelCase")]
243pub struct OmenaParserIcssExportEdgeFactV0 {
244 pub export_name: String,
245 pub reference_names: Vec<String>,
246}
247
248#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
249#[serde(rename_all = "camelCase")]
250pub struct OmenaParserSassSymbolFactV0 {
251 pub kind: &'static str,
252 pub symbol_kind: &'static str,
253 pub name: String,
254 pub role: &'static str,
255 pub namespace: Option<String>,
256}
257
258#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
259#[serde(rename_all = "camelCase")]
260pub struct OmenaParserSassModuleEdgeFactV0 {
261 pub kind: &'static str,
262 pub source: String,
263 pub namespace_kind: Option<&'static str>,
264 pub namespace: Option<String>,
265 pub visibility_filter_kind: Option<&'static str>,
266 pub visibility_filter_names: Vec<String>,
267}
268
269#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
270#[serde(rename_all = "camelCase")]
271pub struct OmenaParserSassSymbolResolutionV0 {
272 pub schema_version: &'static str,
273 pub product: &'static str,
274 pub resolution_scope: &'static str,
275 pub declaration_count: usize,
276 pub reference_count: usize,
277 pub resolved_reference_count: usize,
278 pub unresolved_reference_count: usize,
279 pub edges: Vec<OmenaParserSassSymbolResolutionEdgeV0>,
280 pub capabilities: OmenaParserSassSymbolResolutionCapabilitiesV0,
281}
282
283#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
284#[serde(rename_all = "camelCase")]
285pub struct OmenaParserSassSymbolResolutionEdgeV0 {
286 pub symbol_kind: &'static str,
287 pub name: String,
288 pub namespace: Option<String>,
289 pub reference_kind: &'static str,
290 pub reference_role: &'static str,
291 pub reference_source_order: usize,
292 pub declaration_kind: Option<&'static str>,
293 pub declaration_source_order: Option<usize>,
294 pub status: &'static str,
295}
296
297#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
298#[serde(rename_all = "camelCase")]
299pub struct OmenaParserSassSymbolResolutionCapabilitiesV0 {
300 pub same_file_lexical_resolution_ready: bool,
301 pub declaration_before_reference_ready: bool,
302 pub unresolved_reference_reporting_ready: bool,
303 pub cross_file_module_resolution_ready: bool,
304}
305
306#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
307#[serde(rename_all = "camelCase")]
308pub struct OmenaParserLexSummaryV0 {
309 pub schema_version: &'static str,
310 pub product: &'static str,
311 pub dialect: &'static str,
312 pub tokens: Vec<OmenaParserLexTokenV0>,
313 pub parser_error_count: usize,
314}
315
316#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
317#[serde(rename_all = "camelCase")]
318pub struct OmenaParserLexTokenV0 {
319 pub kind: String,
320 pub text: String,
321 pub start: usize,
322 pub end: usize,
323}
324
325#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
326#[serde(rename_all = "camelCase")]
327pub struct OmenaParserParityLiteSummaryV0 {
328 pub schema_version: &'static str,
329 pub language: &'static str,
330 pub selector_names: Vec<String>,
331 pub keyframes_names: Vec<String>,
332 pub value_decl_names: Vec<String>,
333 pub diagnostic_count: usize,
334 pub rule_count: usize,
335 pub declaration_count: usize,
336 pub grouped_selector_count: usize,
337 pub max_nesting_depth: usize,
338 pub at_rule_kind_counts: OmenaParserAtRuleKindCountsV0,
339 pub declaration_kind_counts: OmenaParserDeclarationKindCountsV0,
340}
341
342#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
343#[serde(rename_all = "camelCase")]
344pub struct OmenaParserAtRuleKindCountsV0 {
345 pub media: usize,
346 pub supports: usize,
347 pub layer: usize,
348 pub keyframes: usize,
349 pub value: usize,
350 pub at_root: usize,
351 pub generic: usize,
352}
353
354#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
355#[serde(rename_all = "camelCase")]
356pub struct OmenaParserDeclarationKindCountsV0 {
357 pub composes: usize,
358 pub animation: usize,
359 pub animation_name: usize,
360 pub generic: usize,
361}
362
363pub fn summarize_pratt_value_parser_coverage() -> ParserPrattValueCoverageSummaryV0 {
364 ParserPrattValueCoverageSummaryV0 {
365 product: "omena-parser.pratt-value-coverage",
366 infix_operator_kinds: vec![
367 SyntaxKind::Plus,
368 SyntaxKind::Minus,
369 SyntaxKind::Star,
370 SyntaxKind::Slash,
371 SyntaxKind::Percent,
372 ],
373 prefix_operator_kinds: vec![SyntaxKind::Plus, SyntaxKind::Minus],
374 value_expression_node_kinds: vec![
375 SyntaxKind::UnaryExpression,
376 SyntaxKind::BinaryExpression,
377 SyntaxKind::ParenthesizedExpression,
378 SyntaxKind::FunctionCall,
379 SyntaxKind::FunctionArguments,
380 SyntaxKind::ValueList,
381 SyntaxKind::ComponentValueList,
382 SyntaxKind::SimpleBlock,
383 SyntaxKind::BogusValue,
384 ],
385 specialized_function_family_count: 10,
386 css_values_l4_math_function_count: VALUES_L4_MATH_FUNCTION_NAMES.len(),
387 css_color_function_count: CSS_COLOR_FUNCTION_NAMES.len(),
388 ready_surfaces: vec![
389 "prattValueParserCore",
390 "prefixUnaryExpressions",
391 "additiveMultiplicativePrecedence",
392 "parenthesizedValueExpressions",
393 "functionArgumentValueLists",
394 "specializedCssValueFunctionFamilies",
395 "valuesL4MathFunctionArityChecks",
396 "varEnvAttrFunctionHeadChecks",
397 "dynamicInterpolationEscapeHatches",
398 "valueBogusRecovery",
399 ],
400 next_surfaces: vec!["fullPropertyValueGrammarRegistry"],
401 }
402}
403
404pub fn summarize_recursive_descent_parser_coverage() -> ParserRecursiveDescentCoverageSummaryV0 {
405 ParserRecursiveDescentCoverageSummaryV0 {
406 product: "omena-parser.recursive-descent-coverage",
407 dialect_count: 4,
408 entry_point_count: 10,
409 selector_surface_count: 12,
410 at_rule_surface_count: 19,
411 dialect_extension_surface_count: 17,
412 recovery_surface_count: 8,
413 ready_surfaces: vec![
414 "recursiveDescentParserCore",
415 "stylesheetRuleDeclarationEntryPoints",
416 "selectorsLevelFourCstNodes",
417 "registeredAtRulePreludeParsers",
418 "cssNestingRuleItems",
419 "scssDialectStatements",
420 "sassIndentedBlocks",
421 "lessDialectStatements",
422 "bogusRecoverySkeleton",
423 "styleFactExtractionSurface",
424 ],
425 next_surfaces: vec!["completeExternalSpecMirror"],
426 }
427}
428
429pub fn summarize_parser_boundary() -> ParserBoundarySummary {
430 ParserBoundarySummary {
431 product: "omena-parser.boundary",
432 tree_model: "cstree-green-root",
433 parser_track: "greenFieldNextToEngineStyleParser",
434 dialect_count: 4,
435 shared_name_kind_count: NameKind::ALL.len(),
436 ready_surfaces: vec![
437 "lexResult",
438 "lexedTokenTextSurface",
439 "parseResult",
440 "panicFreeTokenizer",
441 "cstreeGreenBuilder",
442 "tokenSetRecoveryScaffold",
443 "dialectExtensionScaffold",
444 "recursiveDescentParserCore",
445 "recursiveDescentCoverageSummary",
446 "selectorCstSkeleton",
447 "atRuleRegistrySkeleton",
448 "prattValueExpressionSkeleton",
449 "prattValueParserCore",
450 "prattValueCoverageSummary",
451 "attributeMatcherTokenization",
452 "attributeMatcherCstNodes",
453 "attributeNameValueModifierCstNodes",
454 "specializedValueFunctionCstNodes",
455 "caseInsensitiveFunctionRegistry",
456 "caseInsensitiveAtRuleRegistry",
457 "valueAtomCstNodes",
458 "identifierValueCstNodes",
459 "stringValueCstNodes",
460 "unicodeRangeValueCstNodes",
461 "functionArgumentValueLists",
462 "cssModuleScopeFunctionCstNodes",
463 "cssModuleGlobalSelectorFactFiltering",
464 "cssModuleLocalIdSelectorFacts",
465 "cssModuleValueStyleFacts",
466 "cssModuleValueDeclarationReferenceFacts",
467 "cssModuleComposesStyleFacts",
468 "icssStyleFacts",
469 "animationNameStyleFacts",
470 "animationShorthandStyleFacts",
471 "scssStructuredBlockAtRules",
472 "scssControlPreludeValidation",
473 "scssControlStyleFactExtraction",
474 "scssIncludeContentBlockStyleFacts",
475 "scssSassModuleEdgeStyleFacts",
476 "scssSassSymbolStyleFacts",
477 "scssUtilityAtRules",
478 "scssVariableFlagCstNodes",
479 "scssNestedPropertyCstNodes",
480 "scssModulePreludeSourceValidation",
481 "scssModulePreludeClauseValidation",
482 "scssModuleConfigCstNodes",
483 "scssModuleConfigBogusRecovery",
484 "scssPlaceholderSelectorCstNodes",
485 "lessMixinDeclarationCstNodes",
486 "lessMixinCallCstNodes",
487 "lessMixinGuardCstNodes",
488 "lessExtendPseudoCstNodes",
489 "lessDetachedRulesetCstNodes",
490 "lessNamespaceAccessCstNodes",
491 "lessPropertyVariableTokenization",
492 "lessPropertyVariableCstNodes",
493 "lessEscapedStringTokenization",
494 "lessEscapedStringValueCstNodes",
495 "importantAnnotationTokenization",
496 "urlTokenization",
497 "urlValueCstNodes",
498 "quotedUrlFunctionValueCstNodes",
499 "conditionalAtRulePreludeCstNodes",
500 "supportsAtRulePreludeValidation",
501 "conditionalLevel5AtRuleCstNodes",
502 "mediaQueryCstNodes",
503 "mediaQueryListValidation",
504 "importPreludeCstNodes",
505 "importSourcePreludeValidation",
506 "importTailPreludeValidation",
507 "customMediaPreludeValidation",
508 "propertyAtRuleNameValidation",
509 "namedAtRulePreludeValidation",
510 "containerAtRulePreludeValidation",
511 "charsetNamespaceAtRulePreludeValidation",
512 "keyframesAtRuleNameValidation",
513 "emptyBlockAtRulePreludeValidation",
514 "layerScopePreludeCstNodes",
515 "layerAtRulePreludeValidation",
516 "scopeAtRulePreludeValidation",
517 "pageAtRulePreludeValidation",
518 "pageMarginAtRuleCstNodes",
519 "modernDeclarationAtRuleCstNodes",
520 "fontFeatureValuesAtRuleCstNodes",
521 "fontFeatureValuesPreludeValidation",
522 "keyframeSelectorListValidation",
523 "viewTransitionAtRuleCstNodes",
524 "genericAtRulePreludeCstNodes",
525 "bogusAtRulePreludeCstNodes",
526 "nestingAtRuleCstNodes",
527 "customMediaAtRuleCstNodes",
528 "cssColorFunctionCstNodes",
529 "colorFunctionArgumentChecks",
530 "gradientFunctionCstNodes",
531 "transformFunctionCstNodes",
532 "filterFunctionCstNodes",
533 "imageFunctionCstNodes",
534 "shapeFunctionCstNodes",
535 "envAttrFunctionCstNodes",
536 "mathFunctionCstNodes",
537 "mathFunctionArityChecks",
538 "mathFunctionEmptyArgumentChecks",
539 "varEnvAttrFunctionHeadChecks",
540 "scssInterpolationTokenization",
541 "scssInterpolationCstNodes",
542 "lessInterpolationTokenization",
543 "lessInterpolationCstNodes",
544 "interpolationBogusRecovery",
545 "unicodeRangeTokenization",
546 "badStringTokenRecovery",
547 "badStringValueBogusNodes",
548 "emptyDeclarationValueRecovery",
549 "emptyVariableValueRecovery",
550 "missingSemicolonDeclarationRecovery",
551 "coreBogusPopulationSlice",
552 "dialectBogusPopulationSlice",
553 "cssModuleValueCstNodes",
554 "cssModuleComposesCstNodes",
555 "icssModuleBlockCstNodes",
556 "icssImportSourceValidation",
557 "cssModuleFromClauseSourceValidation",
558 "cssModuleComposesMultipleFromValidation",
559 "cssModuleGlobalComposesValidation",
560 "cssModuleBogusRecovery",
561 "valueListCstNodes",
562 "valueListBogusRecovery",
563 "genericRecoveryBogusNodes",
564 "sassIndentedTokenization",
565 "sassIndentedBlockCstNodes",
566 "sassIndentedStyleFacts",
567 "differentialCorpusSeed",
568 "differentialCorpus",
569 "lightningCssDifferentialCorpusSlice",
570 "lightningCssSelectorIdAndAtRuleDifferentialSlice",
571 "midTypingNoPanicPropertySlice",
572 "deterministicPanicFreeCorpus",
573 "losslessCstTextRoundTripSmoke",
574 "parseResultSourceTextSurface",
575 "parseSourceParseRoundTripSmoke",
576 "typedNumericValueAtomCstNodes",
577 "bracketedValueCstNodes",
578 "importantAnnotationCstNodes",
579 "splitImportantAnnotationCstNodes",
580 "unexpectedValueTokenBogusNodes",
581 "cdoCdcTokenization",
582 "cssIdentifierEscapeTokenization",
583 "nullAndBomInputPreprocessingSlice",
584 "hashDelimiterTokenization",
585 "cssDashIdentTokenization",
586 "signedNumericTokenization",
587 "exponentNumericTokenization",
588 "badUrlWhitespaceRecovery",
589 "parserEntryPointApiSlice",
590 "ruleListEntryPointApiSlice",
591 "componentValueEntryPointApiSlice",
592 "componentValueListEntryPointApiSlice",
593 "commaSeparatedComponentValueListEntryPointApiSlice",
594 "simpleBlockEntryPointApiSlice",
595 "typedCstWrapperSlice",
596 "parserCstEquivalence",
597 "typedBogusCstWrapperSlice",
598 "componentValueCstNodes",
599 "simpleBlockCstNodes",
600 "fullBogusPopulation",
601 "componentValueListCstNodes",
602 "commaSeparatedComponentValueListCstNodes",
603 "customPropertyAnyValueComponentList",
604 "customPropertyValueCstNodes",
605 "functionalPseudoSelectorListCstNodes",
606 "strictNotPseudoSelectorListCstNodes",
607 "nthSelectorOfSelectorListCstNodes",
608 "nthSelectorFormulaCstNodes",
609 "hasRelativeSelectorListCstNodes",
610 "langDirSelectorArgumentCstNodes",
611 "namespaceQualifiedSelectorCstNodes",
612 "selectorFunctionArgumentFactExclusion",
613 "missingBlockCloseBogusTrivia",
614 "initialDialectStatementNodes",
615 "recoveryBogusSkeleton",
616 "styleFactExtractionSurface",
617 "parserSemanticNameConsumption",
618 "productCutoverGate",
619 ],
620 not_ready_surfaces: vec![
621 "completeExternalSpecMirror",
622 "fullPropertyValueGrammarRegistry",
623 ],
624 }
625}
626
627pub fn summarize_omena_parser_style_facts(
628 style_source: &str,
629 dialect: StyleDialect,
630) -> OmenaParserStyleFactsSummaryV0 {
631 let facts = collect_style_facts(style_source, dialect);
632 let sass_symbol_resolution = summarize_omena_parser_sass_symbol_resolution(&facts.sass_symbols);
633 let mut class_selector_names = Vec::new();
634 let mut id_selector_names = Vec::new();
635 let mut placeholder_selector_names = Vec::new();
636 let mut keyframe_names = Vec::new();
637 let mut animation_reference_names = Vec::new();
638 let mut css_module_value_definition_names = BTreeSet::new();
639 let mut css_module_value_reference_names = BTreeSet::new();
640 let mut css_module_value_import_sources = BTreeSet::new();
641 let mut css_module_composes_target_names = BTreeSet::new();
642 let mut css_module_composes_import_sources = BTreeSet::new();
643 let mut icss_export_names = BTreeSet::new();
644 let mut icss_import_local_names = BTreeSet::new();
645 let mut icss_import_remote_names = BTreeSet::new();
646 let mut icss_import_sources = BTreeSet::new();
647 let mut variable_names = BTreeSet::new();
648 let mut sass_symbol_declaration_names = BTreeSet::new();
649 let mut sass_symbol_reference_names = BTreeSet::new();
650 let mut sass_module_use_sources = BTreeSet::new();
651 let mut sass_module_forward_sources = BTreeSet::new();
652 let mut sass_module_import_sources = BTreeSet::new();
653 let mut custom_property_names = BTreeMap::new();
654 let mut custom_property_decl_names = BTreeMap::new();
655 let mut custom_property_ref_names = BTreeMap::new();
656
657 for selector in facts.selectors {
658 match selector.kind {
659 ParsedSelectorFactKind::Class => class_selector_names.push(selector.name),
660 ParsedSelectorFactKind::Id => id_selector_names.push(selector.name),
661 ParsedSelectorFactKind::Placeholder => placeholder_selector_names.push(selector.name),
662 }
663 }
664
665 for variable in facts.variables {
666 match variable.kind {
667 ParsedVariableFactKind::ScssDeclaration
668 | ParsedVariableFactKind::ScssReference
669 | ParsedVariableFactKind::LessDeclaration
670 | ParsedVariableFactKind::LessReference => {
671 let ParsedVariableFactNameV0::NonProperty(name) = variable.name else {
672 continue;
673 };
674 variable_names.insert(name);
675 }
676 ParsedVariableFactKind::CustomPropertyDeclaration
677 | ParsedVariableFactKind::CustomPropertyReference => {
678 let Some(property_key) = variable.property_key else {
679 continue;
680 };
681 let ParsedVariableFactNameV0::CustomProperty(name) = variable.name else {
682 continue;
683 };
684 custom_property_names
685 .entry(property_key.clone())
686 .or_insert_with(|| name.clone());
687 match variable.kind {
688 ParsedVariableFactKind::CustomPropertyDeclaration => {
689 custom_property_decl_names
690 .entry(property_key)
691 .or_insert_with(|| name.clone());
692 }
693 ParsedVariableFactKind::CustomPropertyReference => {
694 custom_property_ref_names
695 .entry(property_key)
696 .or_insert(name);
697 }
698 _ => {}
699 }
700 }
701 }
702 }
703
704 for symbol in &facts.sass_symbols {
705 match symbol.role {
706 "declaration" => {
707 sass_symbol_declaration_names.insert(symbol.name.clone());
708 }
709 _ => {
710 sass_symbol_reference_names.insert(symbol.name.clone());
711 }
712 }
713 }
714
715 for edge in &facts.sass_module_edges {
716 match edge.kind {
717 ParsedSassModuleEdgeFactKind::Use => {
718 sass_module_use_sources.insert(edge.source.clone());
719 }
720 ParsedSassModuleEdgeFactKind::Forward => {
721 sass_module_forward_sources.insert(edge.source.clone());
722 }
723 ParsedSassModuleEdgeFactKind::Import => {
724 sass_module_import_sources.insert(edge.source.clone());
725 }
726 }
727 }
728
729 for animation in facts.animations {
730 match animation.kind {
731 ParsedAnimationFactKind::KeyframesDeclaration => keyframe_names.push(animation.name),
732 ParsedAnimationFactKind::AnimationNameReference => {
733 animation_reference_names.push(animation.name);
734 }
735 }
736 }
737
738 for value in facts.css_module_values {
739 match value.kind {
740 ParsedCssModuleValueFactKind::Definition => {
741 css_module_value_definition_names.insert(value.name);
742 }
743 ParsedCssModuleValueFactKind::Reference => {
744 css_module_value_reference_names.insert(value.name);
745 }
746 ParsedCssModuleValueFactKind::ImportSource => {
747 css_module_value_import_sources.insert(value.name);
748 }
749 }
750 }
751
752 for composes in facts.css_module_composes {
753 match composes.kind {
754 ParsedCssModuleComposesFactKind::Target => {
755 css_module_composes_target_names.insert(composes.name);
756 }
757 ParsedCssModuleComposesFactKind::ImportSource => {
758 css_module_composes_import_sources.insert(composes.name);
759 }
760 }
761 }
762
763 for icss in facts.icss {
764 match icss.kind {
765 ParsedIcssFactKind::ExportName => {
766 icss_export_names.insert(icss.name);
767 }
768 ParsedIcssFactKind::ImportLocalName => {
769 icss_import_local_names.insert(icss.name);
770 }
771 ParsedIcssFactKind::ImportRemoteName => {
772 icss_import_remote_names.insert(icss.name);
773 }
774 ParsedIcssFactKind::ImportSource => {
775 icss_import_sources.insert(icss.name);
776 }
777 }
778 }
779
780 OmenaParserStyleFactsSummaryV0 {
781 schema_version: "0",
782 product: "omena-parser.style-facts",
783 dialect: style_dialect_label(dialect),
784 class_selector_names,
785 id_selector_names,
786 placeholder_selector_names,
787 keyframe_names,
788 animation_reference_names,
789 css_module_value_definition_names: css_module_value_definition_names.into_iter().collect(),
790 css_module_value_reference_names: css_module_value_reference_names.into_iter().collect(),
791 css_module_value_import_sources: css_module_value_import_sources.into_iter().collect(),
792 css_module_value_import_edges: facts
793 .css_module_value_import_edges
794 .into_iter()
795 .map(|edge| OmenaParserCssModuleValueImportEdgeFactV0 {
796 remote_name: edge.remote_name,
797 local_name: edge.local_name,
798 import_source: edge.import_source,
799 })
800 .collect(),
801 css_module_value_definition_edges: facts
802 .css_module_value_definition_edges
803 .into_iter()
804 .map(|edge| OmenaParserCssModuleValueDefinitionEdgeFactV0 {
805 definition_name: edge.definition_name,
806 reference_names: edge.reference_names,
807 })
808 .collect(),
809 css_module_composes_target_names: css_module_composes_target_names.into_iter().collect(),
810 css_module_composes_import_sources: css_module_composes_import_sources
811 .into_iter()
812 .collect(),
813 css_module_composes_edges: facts
814 .css_module_composes_edges
815 .into_iter()
816 .map(|edge| OmenaParserCssModuleComposesEdgeFactV0 {
817 kind: css_module_composes_edge_kind_label(edge.kind),
818 owner_selector_names: edge.owner_selector_names,
819 target_names: edge.target_names,
820 import_source: edge.import_source,
821 })
822 .collect(),
823 icss_export_names: icss_export_names.into_iter().collect(),
824 icss_import_local_names: icss_import_local_names.into_iter().collect(),
825 icss_import_remote_names: icss_import_remote_names.into_iter().collect(),
826 icss_import_sources: icss_import_sources.into_iter().collect(),
827 icss_import_edges: facts
828 .icss_import_edges
829 .into_iter()
830 .map(|edge| OmenaParserIcssImportEdgeFactV0 {
831 local_name: edge.local_name,
832 remote_name: edge.remote_name,
833 import_source: edge.import_source,
834 })
835 .collect(),
836 icss_export_edges: facts
837 .icss_export_edges
838 .into_iter()
839 .map(|edge| OmenaParserIcssExportEdgeFactV0 {
840 export_name: edge.export_name,
841 reference_names: edge.reference_names,
842 })
843 .collect(),
844 variable_names: variable_names.into_iter().collect(),
845 sass_symbol_declaration_names: sass_symbol_declaration_names.into_iter().collect(),
846 sass_symbol_reference_names: sass_symbol_reference_names.into_iter().collect(),
847 sass_symbol_facts: facts
848 .sass_symbols
849 .into_iter()
850 .map(|symbol| OmenaParserSassSymbolFactV0 {
851 kind: sass_symbol_fact_kind_label(symbol.kind),
852 symbol_kind: symbol.symbol_kind,
853 name: symbol.name,
854 role: symbol.role,
855 namespace: symbol.namespace,
856 })
857 .collect(),
858 sass_symbol_resolution,
859 sass_module_use_sources: sass_module_use_sources.into_iter().collect(),
860 sass_module_forward_sources: sass_module_forward_sources.into_iter().collect(),
861 sass_module_import_sources: sass_module_import_sources.into_iter().collect(),
862 sass_module_edges: facts
863 .sass_module_edges
864 .into_iter()
865 .map(|edge| OmenaParserSassModuleEdgeFactV0 {
866 kind: sass_module_edge_fact_kind_label(edge.kind),
867 source: edge.source,
868 namespace_kind: edge.namespace_kind,
869 namespace: edge.namespace,
870 visibility_filter_kind: edge.visibility_filter_kind,
871 visibility_filter_names: edge.visibility_filter_names,
872 })
873 .collect(),
874 custom_property_names: custom_property_names.into_values().collect(),
875 custom_property_decl_names: custom_property_decl_names.into_values().collect(),
876 custom_property_ref_names: custom_property_ref_names.into_values().collect(),
877 at_rule_names: facts
878 .at_rules
879 .into_iter()
880 .map(|at_rule| at_rule.name)
881 .collect(),
882 parser_error_count: facts.error_count,
883 }
884}
885
886pub fn summarize_omena_parser_lex(source: &str, dialect: StyleDialect) -> OmenaParserLexSummaryV0 {
887 let result = lex(source, dialect);
888 OmenaParserLexSummaryV0 {
889 schema_version: "0",
890 product: "omena-parser.lex-result",
891 dialect: style_dialect_label(result.dialect()),
892 tokens: result
893 .tokens()
894 .iter()
895 .map(|token| OmenaParserLexTokenV0 {
896 kind: format!("{:?}", token.kind),
897 text: token.text.clone(),
898 start: token.range.start().into(),
899 end: token.range.end().into(),
900 })
901 .collect(),
902 parser_error_count: result.errors().len(),
903 }
904}
905
906pub fn summarize_omena_parser_parity_lite(
907 source: &str,
908 dialect: StyleDialect,
909) -> OmenaParserParityLiteSummaryV0 {
910 let facts = collect_style_facts(source, dialect);
911 let result = parse(source, dialect);
912 let (tokens, _) = tokenize(source, &BuiltinDialectExtension::new(dialect));
913 let mut structural = ParserStructuralSummary::default();
914 summarize_parser_structural_range(&tokens, 0, tokens.len(), 0, &mut structural);
915 let mut selector_names = collect_parity_lite_selector_names_from_tokens(&tokens);
916 selector_names.sort();
917
918 OmenaParserParityLiteSummaryV0 {
919 schema_version: "0",
920 language: style_dialect_label(dialect),
921 selector_names,
922 keyframes_names: sorted_unique(
923 facts
924 .animations
925 .iter()
926 .filter(|animation| animation.kind == ParsedAnimationFactKind::KeyframesDeclaration)
927 .map(|animation| animation.name.clone()),
928 ),
929 value_decl_names: sorted_unique(
930 facts
931 .css_module_values
932 .iter()
933 .filter(|value| value.kind == ParsedCssModuleValueFactKind::Definition)
934 .map(|value| value.name.clone()),
935 ),
936 diagnostic_count: result.errors().len(),
937 rule_count: structural.rule_count,
938 declaration_count: structural.declaration_count,
939 grouped_selector_count: structural.grouped_selector_count,
940 max_nesting_depth: structural.max_nesting_depth,
941 at_rule_kind_counts: structural.at_rule_kind_counts,
942 declaration_kind_counts: structural.declaration_kind_counts,
943 }
944}
945
946fn style_dialect_label(dialect: StyleDialect) -> &'static str {
947 match dialect {
948 StyleDialect::Css => "css",
949 StyleDialect::Scss => "scss",
950 StyleDialect::Sass => "sass",
951 StyleDialect::Less => "less",
952 }
953}
954
955#[derive(Default)]
956struct ParserStructuralSummary {
957 rule_count: usize,
958 declaration_count: usize,
959 grouped_selector_count: usize,
960 max_nesting_depth: usize,
961 at_rule_kind_counts: OmenaParserAtRuleKindCountsV0,
962 declaration_kind_counts: OmenaParserDeclarationKindCountsV0,
963}
964
965fn summarize_parser_structural_range(
966 tokens: &[Token<'_>],
967 start: usize,
968 end: usize,
969 depth: usize,
970 summary: &mut ParserStructuralSummary,
971) {
972 let mut index = start;
973 while index < end {
974 index = skip_trivia_tokens(tokens, index, end);
975 if index >= end {
976 break;
977 }
978
979 if tokens[index].kind == SyntaxKind::AtKeyword {
980 increment_omena_parser_at_rule_kind_count(
981 &mut summary.at_rule_kind_counts,
982 classify_omena_parser_at_rule_kind(tokens[index].text),
983 );
984 let next_depth = depth + 1;
985 summary.max_nesting_depth = summary.max_nesting_depth.max(next_depth);
986 if let Some((open, close)) = find_block_after_header(tokens, index, end) {
987 summarize_parser_structural_range(tokens, open + 1, close, next_depth, summary);
988 index = close + 1;
989 } else {
990 index = skip_statement_or_unmatched_boundary(tokens, index, end);
991 }
992 continue;
993 }
994
995 let statement_end = css_module_value_statement_end(tokens, index);
996 if is_root_less_variable_statement(tokens, index, statement_end.min(end), depth) {
997 increment_omena_parser_at_rule_kind_count(
998 &mut summary.at_rule_kind_counts,
999 keyof_omena_parser_at_rule_kind_counts::Kind::Generic,
1000 );
1001 if statement_end >= end || tokens[statement_end].kind == SyntaxKind::RightBrace {
1002 break;
1003 }
1004 index = statement_end + 1;
1005 continue;
1006 }
1007
1008 if statement_end < end && tokens[statement_end].kind == SyntaxKind::LeftBrace {
1009 summary.rule_count += 1;
1010 let next_depth = depth + 1;
1011 summary.max_nesting_depth = summary.max_nesting_depth.max(next_depth);
1012 let group_count = count_omena_parser_selector_groups(tokens, index, statement_end);
1013 if group_count > 1 {
1014 summary.grouped_selector_count += group_count;
1015 }
1016 if let Some(close) = matching_right_brace(tokens, statement_end, end) {
1017 summarize_parser_structural_range(
1018 tokens,
1019 statement_end + 1,
1020 close,
1021 next_depth,
1022 summary,
1023 );
1024 index = close + 1;
1025 } else {
1026 index = statement_end + 1;
1027 }
1028 continue;
1029 }
1030
1031 if let Some(colon_index) = declaration_colon_index(tokens, index, statement_end.min(end)) {
1032 summary.declaration_count += 1;
1033 let property = previous_non_trivia_token_index(tokens, colon_index, index)
1034 .map(|property| tokens[property].text)
1035 .unwrap_or_default();
1036 increment_omena_parser_declaration_kind_count(
1037 &mut summary.declaration_kind_counts,
1038 classify_omena_parser_declaration_kind(property),
1039 );
1040 }
1041
1042 if statement_end >= end || tokens[statement_end].kind == SyntaxKind::RightBrace {
1043 break;
1044 }
1045 index = statement_end + 1;
1046 }
1047}
1048
1049fn is_root_less_variable_statement(
1050 tokens: &[Token<'_>],
1051 start: usize,
1052 end: usize,
1053 depth: usize,
1054) -> bool {
1055 if depth != 0 {
1056 return false;
1057 }
1058 let Some(first) = next_non_trivia_token_index_until(tokens, start, end) else {
1059 return false;
1060 };
1061 tokens[first].kind == SyntaxKind::LessVariable
1062 && declaration_colon_index(tokens, first, end).is_some()
1063}
1064
1065fn count_omena_parser_selector_groups(tokens: &[Token<'_>], start: usize, end: usize) -> usize {
1066 split_selector_groups(tokens, start, end)
1067 .into_iter()
1068 .filter(|(group_start, group_end)| {
1069 *group_start < *group_end
1070 && next_non_trivia_token_index_until(tokens, *group_start, *group_end).is_some()
1071 })
1072 .count()
1073}
1074
1075fn collect_parity_lite_selector_names_from_tokens(tokens: &[Token<'_>]) -> Vec<String> {
1076 let mut names = Vec::new();
1077 collect_parity_lite_selector_names_in_range(tokens, 0, tokens.len(), &[], None, &mut names);
1078 names
1079}
1080
1081fn collect_parity_lite_selector_names_in_range(
1082 tokens: &[Token<'_>],
1083 start: usize,
1084 end: usize,
1085 parent_branches: &[SelectorBranch],
1086 css_module_scope: Option<&'static str>,
1087 names: &mut Vec<String>,
1088) {
1089 let mut index = start;
1090 while index < end {
1091 index = skip_trivia_tokens(tokens, index, end);
1092 if index >= end {
1093 break;
1094 }
1095
1096 if tokens[index].kind == SyntaxKind::AtKeyword {
1097 let block = find_selector_block_after_header(tokens, index, end);
1098 if let Some((open, close)) = block {
1099 if tokens[index].text == "@nest" {
1100 if css_module_scope == Some("global") {
1101 collect_parity_lite_selector_names_in_range(
1102 tokens,
1103 open + 1,
1104 close,
1105 &[],
1106 css_module_scope,
1107 names,
1108 );
1109 } else {
1110 let branches =
1111 resolve_selector_header(tokens, index + 1, open, parent_branches);
1112 names.extend(branches.iter().map(|branch| branch.name.clone()));
1113 collect_grouped_ampersand_compound_selector_duplicates(
1114 tokens,
1115 index + 1,
1116 open,
1117 parent_branches.len(),
1118 names,
1119 );
1120 collect_parity_lite_selector_names_in_range(
1121 tokens,
1122 open + 1,
1123 close,
1124 &branches,
1125 css_module_scope,
1126 names,
1127 );
1128 }
1129 } else if style_wrapper_at_rule(tokens[index].text) {
1130 collect_parity_lite_selector_names_in_range(
1131 tokens,
1132 open + 1,
1133 close,
1134 parent_branches,
1135 css_module_scope,
1136 names,
1137 );
1138 }
1139 index = close + 1;
1140 } else {
1141 index = skip_statement_or_unmatched_boundary(tokens, index, end);
1142 }
1143 continue;
1144 }
1145
1146 let Some((open, close)) = find_selector_block_after_header(tokens, index, end) else {
1147 index = skip_statement_or_unmatched_boundary(tokens, index, end);
1148 continue;
1149 };
1150
1151 let effective_scope = css_module_scope
1152 .or_else(|| css_module_block_scope_marker_in_header(tokens, index, open));
1153 if effective_scope == Some("global") {
1154 collect_parity_lite_selector_names_in_range(
1155 tokens,
1156 open + 1,
1157 close,
1158 &[],
1159 effective_scope,
1160 names,
1161 );
1162 } else {
1163 let branches = resolve_selector_header(tokens, index, open, parent_branches);
1164 names.extend(branches.iter().map(|branch| branch.name.clone()));
1165 collect_grouped_ampersand_compound_selector_duplicates(
1166 tokens,
1167 index,
1168 open,
1169 parent_branches.len(),
1170 names,
1171 );
1172 collect_parity_lite_selector_names_in_range(
1173 tokens,
1174 open + 1,
1175 close,
1176 &branches,
1177 effective_scope,
1178 names,
1179 );
1180 }
1181 index = close + 1;
1182 }
1183}
1184
1185fn collect_grouped_ampersand_compound_selector_duplicates(
1186 tokens: &[Token<'_>],
1187 start: usize,
1188 end: usize,
1189 parent_branch_count: usize,
1190 names: &mut Vec<String>,
1191) {
1192 if parent_branch_count <= 1 || !header_contains_ampersand(tokens, start, end) {
1193 return;
1194 }
1195 for (name, _) in collect_class_selector_names_from_header(tokens, start, end) {
1196 names.extend(std::iter::repeat_n(name, parent_branch_count - 1));
1197 }
1198}
1199
1200fn header_contains_ampersand(tokens: &[Token<'_>], start: usize, end: usize) -> bool {
1201 tokens[start..end]
1202 .iter()
1203 .any(|token| token.kind == SyntaxKind::Ampersand)
1204}
1205
1206fn classify_omena_parser_at_rule_kind(text: &str) -> keyof_omena_parser_at_rule_kind_counts::Kind {
1207 let name = text.trim_start_matches('@');
1208 if matches_ignore_ascii_case(name, &["media"]) {
1209 keyof_omena_parser_at_rule_kind_counts::Kind::Media
1210 } else if matches_ignore_ascii_case(name, &["supports"]) {
1211 keyof_omena_parser_at_rule_kind_counts::Kind::Supports
1212 } else if matches_ignore_ascii_case(name, &["layer"]) {
1213 keyof_omena_parser_at_rule_kind_counts::Kind::Layer
1214 } else if matches_ignore_ascii_case(name, &["keyframes", "-webkit-keyframes"]) {
1215 keyof_omena_parser_at_rule_kind_counts::Kind::Keyframes
1216 } else if matches_ignore_ascii_case(name, &["value"]) {
1217 keyof_omena_parser_at_rule_kind_counts::Kind::Value
1218 } else if matches_ignore_ascii_case(name, &["at-root"]) {
1219 keyof_omena_parser_at_rule_kind_counts::Kind::AtRoot
1220 } else {
1221 keyof_omena_parser_at_rule_kind_counts::Kind::Generic
1222 }
1223}
1224
1225fn increment_omena_parser_at_rule_kind_count(
1226 counts: &mut OmenaParserAtRuleKindCountsV0,
1227 kind: keyof_omena_parser_at_rule_kind_counts::Kind,
1228) {
1229 match kind {
1230 keyof_omena_parser_at_rule_kind_counts::Kind::Media => counts.media += 1,
1231 keyof_omena_parser_at_rule_kind_counts::Kind::Supports => counts.supports += 1,
1232 keyof_omena_parser_at_rule_kind_counts::Kind::Layer => counts.layer += 1,
1233 keyof_omena_parser_at_rule_kind_counts::Kind::Keyframes => counts.keyframes += 1,
1234 keyof_omena_parser_at_rule_kind_counts::Kind::Value => counts.value += 1,
1235 keyof_omena_parser_at_rule_kind_counts::Kind::AtRoot => counts.at_root += 1,
1236 keyof_omena_parser_at_rule_kind_counts::Kind::Generic => counts.generic += 1,
1237 }
1238}
1239
1240fn classify_omena_parser_declaration_kind(
1241 property: &str,
1242) -> keyof_omena_parser_declaration_kind_counts::Kind {
1243 let property = property.trim();
1244 if matches_ignore_ascii_case(property, &["composes"]) {
1245 keyof_omena_parser_declaration_kind_counts::Kind::Composes
1246 } else if matches_ignore_ascii_case(property, &["animation"]) {
1247 keyof_omena_parser_declaration_kind_counts::Kind::Animation
1248 } else if matches_ignore_ascii_case(property, &["animation-name"]) {
1249 keyof_omena_parser_declaration_kind_counts::Kind::AnimationName
1250 } else {
1251 keyof_omena_parser_declaration_kind_counts::Kind::Generic
1252 }
1253}
1254
1255fn increment_omena_parser_declaration_kind_count(
1256 counts: &mut OmenaParserDeclarationKindCountsV0,
1257 kind: keyof_omena_parser_declaration_kind_counts::Kind,
1258) {
1259 match kind {
1260 keyof_omena_parser_declaration_kind_counts::Kind::Composes => counts.composes += 1,
1261 keyof_omena_parser_declaration_kind_counts::Kind::Animation => counts.animation += 1,
1262 keyof_omena_parser_declaration_kind_counts::Kind::AnimationName => {
1263 counts.animation_name += 1
1264 }
1265 keyof_omena_parser_declaration_kind_counts::Kind::Generic => counts.generic += 1,
1266 }
1267}
1268
1269mod keyof_omena_parser_at_rule_kind_counts {
1270 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
1271 pub enum Kind {
1272 Media,
1273 Supports,
1274 Layer,
1275 Keyframes,
1276 Value,
1277 AtRoot,
1278 Generic,
1279 }
1280}
1281
1282mod keyof_omena_parser_declaration_kind_counts {
1283 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
1284 pub enum Kind {
1285 Composes,
1286 Animation,
1287 AnimationName,
1288 Generic,
1289 }
1290}
1291
1292fn sorted_unique(values: impl IntoIterator<Item = String>) -> Vec<String> {
1293 values
1294 .into_iter()
1295 .collect::<BTreeSet<_>>()
1296 .into_iter()
1297 .collect()
1298}
1299
1300fn css_module_composes_edge_kind_label(kind: ParsedCssModuleComposesEdgeKind) -> &'static str {
1301 match kind {
1302 ParsedCssModuleComposesEdgeKind::Local => "local",
1303 ParsedCssModuleComposesEdgeKind::Global => "global",
1304 ParsedCssModuleComposesEdgeKind::External => "external",
1305 }
1306}
1307
1308fn sass_symbol_fact_kind_label(kind: ParsedSassSymbolFactKind) -> &'static str {
1309 match kind {
1310 ParsedSassSymbolFactKind::VariableDeclaration => "sassVariableDeclaration",
1311 ParsedSassSymbolFactKind::VariableReference => "sassVariableReference",
1312 ParsedSassSymbolFactKind::MixinDeclaration => "sassMixinDeclaration",
1313 ParsedSassSymbolFactKind::MixinInclude => "sassMixinInclude",
1314 ParsedSassSymbolFactKind::FunctionDeclaration => "sassFunctionDeclaration",
1315 ParsedSassSymbolFactKind::FunctionCall => "sassFunctionCall",
1316 }
1317}
1318
1319fn sass_module_edge_fact_kind_label(kind: ParsedSassModuleEdgeFactKind) -> &'static str {
1320 match kind {
1321 ParsedSassModuleEdgeFactKind::Use => "sassUse",
1322 ParsedSassModuleEdgeFactKind::Forward => "sassForward",
1323 ParsedSassModuleEdgeFactKind::Import => "sassImport",
1324 }
1325}
1326
1327fn summarize_omena_parser_sass_symbol_resolution(
1328 symbols: &[ParsedSassSymbolFact],
1329) -> OmenaParserSassSymbolResolutionV0 {
1330 let mut declaration_by_symbol: BTreeMap<
1331 (&'static str, Option<String>, String),
1332 (usize, &'static str),
1333 > = BTreeMap::new();
1334 let mut declaration_count = 0usize;
1335 let mut reference_count = 0usize;
1336 let mut edges = Vec::new();
1337
1338 for (source_order, symbol) in symbols.iter().enumerate() {
1339 let kind = sass_symbol_fact_kind_label(symbol.kind);
1340 if sass_symbol_fact_kind_is_declaration(symbol.kind) {
1341 declaration_count += 1;
1342 declaration_by_symbol.insert(
1343 (
1344 symbol.symbol_kind,
1345 symbol.namespace.clone(),
1346 symbol.name.clone(),
1347 ),
1348 (source_order, kind),
1349 );
1350 continue;
1351 }
1352 if !sass_symbol_fact_kind_is_reference(symbol.kind) {
1353 continue;
1354 }
1355
1356 reference_count += 1;
1357 let declaration = declaration_by_symbol.get(&(
1358 symbol.symbol_kind,
1359 symbol.namespace.clone(),
1360 symbol.name.clone(),
1361 ));
1362 edges.push(OmenaParserSassSymbolResolutionEdgeV0 {
1363 symbol_kind: symbol.symbol_kind,
1364 name: symbol.name.clone(),
1365 namespace: symbol.namespace.clone(),
1366 reference_kind: kind,
1367 reference_role: symbol.role,
1368 reference_source_order: source_order,
1369 declaration_kind: declaration.map(|(_, declaration_kind)| *declaration_kind),
1370 declaration_source_order: declaration.map(|(declaration_order, _)| *declaration_order),
1371 status: if declaration.is_some() {
1372 "resolved"
1373 } else {
1374 "unresolved"
1375 },
1376 });
1377 }
1378
1379 let resolved_reference_count = edges
1380 .iter()
1381 .filter(|edge| edge.status == "resolved")
1382 .count();
1383
1384 OmenaParserSassSymbolResolutionV0 {
1385 schema_version: "0",
1386 product: "omena-parser.sass-symbol-same-file-resolution",
1387 resolution_scope: "same-file",
1388 declaration_count,
1389 reference_count,
1390 resolved_reference_count,
1391 unresolved_reference_count: reference_count.saturating_sub(resolved_reference_count),
1392 edges,
1393 capabilities: OmenaParserSassSymbolResolutionCapabilitiesV0 {
1394 same_file_lexical_resolution_ready: true,
1395 declaration_before_reference_ready: true,
1396 unresolved_reference_reporting_ready: true,
1397 cross_file_module_resolution_ready: false,
1398 },
1399 }
1400}
1401
1402fn sass_symbol_fact_kind_is_declaration(kind: ParsedSassSymbolFactKind) -> bool {
1403 matches!(
1404 kind,
1405 ParsedSassSymbolFactKind::VariableDeclaration
1406 | ParsedSassSymbolFactKind::MixinDeclaration
1407 | ParsedSassSymbolFactKind::FunctionDeclaration
1408 )
1409}
1410
1411fn sass_symbol_fact_kind_is_reference(kind: ParsedSassSymbolFactKind) -> bool {
1412 matches!(
1413 kind,
1414 ParsedSassSymbolFactKind::VariableReference
1415 | ParsedSassSymbolFactKind::MixinInclude
1416 | ParsedSassSymbolFactKind::FunctionCall
1417 )
1418}
1419
1420pub fn summarize_parser_cst_equivalence(
1421 text: &str,
1422 dialect: StyleDialect,
1423) -> ParserCstEquivalenceSummaryV0 {
1424 let result = parse(text, dialect);
1425 let syntax = result.syntax();
1426 let cst = result.cst();
1427
1428 let mut node_count = 0;
1429 let mut token_count = 0;
1430 let mut syntax_kind_round_trip_ready = true;
1431 let mut zero_unknown_kind_ready = true;
1432
1433 for node in syntax.descendants() {
1434 node_count += 1;
1435 let kind = node.kind();
1436 syntax_kind_round_trip_ready &= SyntaxKind::from_raw(kind.into_raw()) == kind;
1437 zero_unknown_kind_ready &= SyntaxKind::ALL.contains(&kind);
1438 }
1439
1440 for token in syntax
1441 .descendants_with_tokens()
1442 .filter_map(|element| element.into_token())
1443 {
1444 token_count += 1;
1445 let kind = token.kind();
1446 syntax_kind_round_trip_ready &= SyntaxKind::from_raw(kind.into_raw()) == kind;
1447 zero_unknown_kind_ready &= SyntaxKind::ALL.contains(&kind);
1448 }
1449
1450 let typed_wrapper_count = usize::from(cst.stylesheet().is_some())
1451 + cst.rules().len()
1452 + cst.selectors().len()
1453 + cst.declarations().len()
1454 + cst.declaration_lists().len()
1455 + cst.values().len()
1456 + cst.component_values().len()
1457 + cst.simple_blocks().len()
1458 + cst.component_value_lists().len()
1459 + cst.comma_separated_component_value_lists().len()
1460 + cst.custom_property_values().len()
1461 + cst.at_rules().len()
1462 + cst.bogus_nodes().len();
1463
1464 ParserCstEquivalenceSummaryV0 {
1465 product: "omena-parser.cst-equivalence",
1466 dialect,
1467 root_kind: syntax.kind(),
1468 parser_node_count: node_count,
1469 parser_token_count: token_count,
1470 typed_wrapper_count,
1471 source_text_round_trip_ready: result.source_text().as_deref() == Some(text),
1472 syntax_kind_round_trip_ready,
1473 zero_unknown_kind_ready,
1474 typed_cst_wrapper_ready: cst.stylesheet().is_some() && typed_wrapper_count > 1,
1475 ready_surfaces: vec![
1476 "parserCstEquivalence",
1477 "parserUsesOmenaSyntaxKind",
1478 "parserCstSourceTextRoundTrip",
1479 "typedCstWrapperEquivalence",
1480 ],
1481 }
1482}
1483
1484pub fn summarize_parser_semantic_name_consumption(
1485 text: &str,
1486 dialect: StyleDialect,
1487 db: &dyn salsa::Database,
1488) -> ParserSemanticNameConsumptionSummaryV0 {
1489 let facts = collect_style_facts(text, dialect);
1490 let candidates = parser_semantic_name_candidates(&facts);
1491 let interned_name_count = candidates
1492 .iter()
1493 .filter(|candidate| intern_parser_semantic_name(db, candidate.kind, &candidate.text))
1494 .count();
1495 let invalid_name_count = candidates.len().saturating_sub(interned_name_count);
1496
1497 ParserSemanticNameConsumptionSummaryV0 {
1498 product: "omena-parser.semantic-name-consumption",
1499 dialect,
1500 semantic_name_count: candidates.len(),
1501 interned_name_count,
1502 invalid_name_count,
1503 class_name_count: count_parser_semantic_name_kind(&candidates, NameKind::ClassName),
1504 css_ident_count: count_parser_semantic_name_kind(&candidates, NameKind::CssIdent),
1505 property_name_count: count_parser_semantic_name_kind(&candidates, NameKind::PropertyName),
1506 selector_key_count: count_parser_semantic_name_kind(&candidates, NameKind::SelectorKey),
1507 custom_property_name_count: count_parser_semantic_name_kind(
1508 &candidates,
1509 NameKind::CustomPropertyName,
1510 ),
1511 keyframes_name_count: count_parser_semantic_name_kind(&candidates, NameKind::KeyframesName),
1512 mixin_name_count: count_parser_semantic_name_kind(&candidates, NameKind::MixinName),
1513 file_path_count: count_parser_semantic_name_kind(&candidates, NameKind::FilePath),
1514 ready_surfaces: vec![
1515 "parserSemanticNameConsumption",
1516 "typedInternerValidation",
1517 "styleFactNameKindProjection",
1518 ],
1519 }
1520}
1521
1522fn parser_semantic_name_candidates(facts: &ParsedStyleFacts) -> Vec<ParserSemanticNameCandidateV0> {
1523 let mut candidates = Vec::new();
1524
1525 for selector in &facts.selectors {
1526 let kind = match selector.kind {
1527 ParsedSelectorFactKind::Class => NameKind::ClassName,
1528 ParsedSelectorFactKind::Id | ParsedSelectorFactKind::Placeholder => {
1529 NameKind::SelectorKey
1530 }
1531 };
1532 push_parser_semantic_name_candidate(&mut candidates, kind, &selector.name);
1533 }
1534
1535 for variable in &facts.variables {
1536 let candidate = match variable.kind {
1537 ParsedVariableFactKind::CustomPropertyDeclaration
1538 | ParsedVariableFactKind::CustomPropertyReference => variable
1539 .property_key
1540 .as_ref()
1541 .map(|property_key| (NameKind::CustomPropertyName, property_key.as_str())),
1542 ParsedVariableFactKind::ScssDeclaration
1543 | ParsedVariableFactKind::ScssReference
1544 | ParsedVariableFactKind::LessDeclaration
1545 | ParsedVariableFactKind::LessReference => variable
1546 .name
1547 .as_non_property()
1548 .map(|name| (NameKind::CssIdent, name)),
1549 };
1550 let Some((kind, text)) = candidate else {
1551 continue;
1552 };
1553 push_parser_semantic_name_candidate(&mut candidates, kind, text);
1554 }
1555
1556 for symbol in &facts.sass_symbols {
1557 let kind = match symbol.kind {
1558 ParsedSassSymbolFactKind::MixinDeclaration | ParsedSassSymbolFactKind::MixinInclude => {
1559 NameKind::MixinName
1560 }
1561 ParsedSassSymbolFactKind::VariableDeclaration
1562 | ParsedSassSymbolFactKind::VariableReference
1563 | ParsedSassSymbolFactKind::FunctionDeclaration
1564 | ParsedSassSymbolFactKind::FunctionCall => NameKind::CssIdent,
1565 };
1566 push_parser_semantic_name_candidate(&mut candidates, kind, &symbol.name);
1567 if let Some(namespace) = &symbol.namespace {
1568 push_parser_semantic_name_candidate(&mut candidates, NameKind::CssIdent, namespace);
1569 }
1570 }
1571
1572 for include in &facts.sass_includes {
1573 push_parser_semantic_name_candidate(&mut candidates, NameKind::MixinName, &include.name);
1574 if let Some(namespace) = &include.namespace {
1575 push_parser_semantic_name_candidate(&mut candidates, NameKind::CssIdent, namespace);
1576 }
1577 }
1578
1579 for edge in &facts.sass_module_edges {
1580 push_parser_semantic_name_candidate(&mut candidates, NameKind::FilePath, &edge.source);
1581 if let Some(namespace) = &edge.namespace {
1582 push_parser_semantic_name_candidate(&mut candidates, NameKind::CssIdent, namespace);
1583 }
1584 }
1585
1586 for animation in &facts.animations {
1587 push_parser_semantic_name_candidate(
1588 &mut candidates,
1589 NameKind::KeyframesName,
1590 &animation.name,
1591 );
1592 }
1593
1594 for value in &facts.css_module_values {
1595 let kind = match value.kind {
1596 ParsedCssModuleValueFactKind::Definition | ParsedCssModuleValueFactKind::Reference => {
1597 NameKind::CssIdent
1598 }
1599 ParsedCssModuleValueFactKind::ImportSource => NameKind::FilePath,
1600 };
1601 push_parser_semantic_name_candidate(&mut candidates, kind, &value.name);
1602 }
1603
1604 for edge in &facts.css_module_value_import_edges {
1605 push_parser_semantic_name_candidate(&mut candidates, NameKind::CssIdent, &edge.local_name);
1606 push_parser_semantic_name_candidate(&mut candidates, NameKind::CssIdent, &edge.remote_name);
1607 push_parser_semantic_name_candidate(
1608 &mut candidates,
1609 NameKind::FilePath,
1610 &edge.import_source,
1611 );
1612 }
1613
1614 for edge in &facts.css_module_value_definition_edges {
1615 push_parser_semantic_name_candidate(
1616 &mut candidates,
1617 NameKind::CssIdent,
1618 &edge.definition_name,
1619 );
1620 for reference_name in &edge.reference_names {
1621 push_parser_semantic_name_candidate(
1622 &mut candidates,
1623 NameKind::CssIdent,
1624 reference_name,
1625 );
1626 }
1627 }
1628
1629 for composes in &facts.css_module_composes {
1630 let kind = match composes.kind {
1631 ParsedCssModuleComposesFactKind::Target => NameKind::ClassName,
1632 ParsedCssModuleComposesFactKind::ImportSource => NameKind::FilePath,
1633 };
1634 push_parser_semantic_name_candidate(&mut candidates, kind, &composes.name);
1635 }
1636
1637 for edge in &facts.css_module_composes_edges {
1638 for owner_selector_name in &edge.owner_selector_names {
1639 push_parser_semantic_name_candidate(
1640 &mut candidates,
1641 NameKind::ClassName,
1642 owner_selector_name,
1643 );
1644 }
1645 for target_name in &edge.target_names {
1646 push_parser_semantic_name_candidate(&mut candidates, NameKind::ClassName, target_name);
1647 }
1648 if let Some(import_source) = &edge.import_source {
1649 push_parser_semantic_name_candidate(&mut candidates, NameKind::FilePath, import_source);
1650 }
1651 }
1652
1653 for icss in &facts.icss {
1654 let kind = match icss.kind {
1655 ParsedIcssFactKind::ImportSource => NameKind::FilePath,
1656 ParsedIcssFactKind::ExportName
1657 | ParsedIcssFactKind::ImportLocalName
1658 | ParsedIcssFactKind::ImportRemoteName => NameKind::CssIdent,
1659 };
1660 push_parser_semantic_name_candidate(&mut candidates, kind, &icss.name);
1661 }
1662
1663 for edge in &facts.icss_import_edges {
1664 push_parser_semantic_name_candidate(&mut candidates, NameKind::CssIdent, &edge.local_name);
1665 push_parser_semantic_name_candidate(&mut candidates, NameKind::CssIdent, &edge.remote_name);
1666 push_parser_semantic_name_candidate(
1667 &mut candidates,
1668 NameKind::FilePath,
1669 &edge.import_source,
1670 );
1671 }
1672
1673 for edge in &facts.icss_export_edges {
1674 push_parser_semantic_name_candidate(&mut candidates, NameKind::CssIdent, &edge.export_name);
1675 for reference_name in &edge.reference_names {
1676 push_parser_semantic_name_candidate(
1677 &mut candidates,
1678 NameKind::CssIdent,
1679 reference_name,
1680 );
1681 }
1682 }
1683
1684 for at_rule in &facts.at_rules {
1685 push_parser_semantic_name_candidate(&mut candidates, NameKind::CssIdent, &at_rule.name);
1686 }
1687
1688 candidates
1689}
1690
1691fn push_parser_semantic_name_candidate(
1692 candidates: &mut Vec<ParserSemanticNameCandidateV0>,
1693 kind: NameKind,
1694 text: &str,
1695) {
1696 candidates.push(ParserSemanticNameCandidateV0 {
1697 kind,
1698 text: text.to_string(),
1699 });
1700}
1701
1702fn count_parser_semantic_name_kind(
1703 candidates: &[ParserSemanticNameCandidateV0],
1704 kind: NameKind,
1705) -> usize {
1706 candidates
1707 .iter()
1708 .filter(|candidate| candidate.kind == kind)
1709 .count()
1710}
1711
1712fn intern_parser_semantic_name(db: &dyn salsa::Database, kind: NameKind, text: &str) -> bool {
1713 match kind {
1714 NameKind::ClassName => intern_class_name(db, text).is_ok(),
1715 NameKind::CssIdent => intern_css_ident(db, text).is_ok(),
1716 NameKind::PropertyName => intern_property_name(db, text).is_ok(),
1717 NameKind::SelectorKey => intern_selector_key(db, text).is_ok(),
1718 NameKind::CustomPropertyName => intern_custom_property_name(db, text).is_ok(),
1719 NameKind::KeyframesName => intern_keyframes_name(db, text).is_ok(),
1720 NameKind::MixinName => intern_mixin_name(db, text).is_ok(),
1721 NameKind::FilePath => intern_file_path(db, text).is_ok(),
1722 }
1723}