#[cfg(feature = "test-support")]
use std::cell::Cell;
use std::collections::{BTreeMap, BTreeSet};
pub use engine_input_producers::{
ClassExpressionInputV2, EngineInputV2, ExpressionDomainCallSiteFlowAnalysisV0,
ExpressionDomainControlFlowAnalysisV0, ExpressionDomainFlowAnalysisV0,
ExpressionDomainProvenanceExplanationsV0, ExpressionDomainReducedProductIterationV0,
ExpressionSemanticsCanonicalProducerSignalV0, ExpressionSemanticsQueryFragmentsV0, PositionV2,
RangeV2, SelectorUsageCanonicalProducerSignalV0, SelectorUsageQueryFragmentsV0,
SourceAnalysisInputV2, SourceDocumentV2, SourceResolutionCanonicalProducerSignalV0,
SourceResolutionQueryFragmentsV0, StringTypeFactsV2, StyleAnalysisInputV2, StyleDocumentV2,
StyleSelectorV2, TypeFactEntryV2,
};
use engine_input_producers::{
collect_expression_domain_flow_graphs,
summarize_expression_domain_call_site_flow_analysis_input,
summarize_expression_domain_control_flow_analysis_input,
summarize_expression_domain_flow_analysis_input,
summarize_expression_domain_provenance_explanations_input,
summarize_expression_domain_reduced_product_iteration_input,
summarize_expression_semantics_canonical_producer_signal_input,
summarize_expression_semantics_query_fragments_input,
summarize_selector_usage_canonical_producer_signal_input,
summarize_selector_usage_query_fragments_input,
};
use omena_abstract_value::{
AbstractClassValueProvenanceV0, ClassValueFlowSealedIncrementalAnalysisV0,
SealedClassValueFlowAnalysisArtifactV0, analyze_class_value_flow_incremental,
analyze_class_value_flow_incremental_with_artifact, class_value_flow_incremental_input,
project_abstract_value_selectors, summarize_omena_abstract_value_domain,
summarize_reduced_class_value_product,
};
pub use omena_abstract_value::{
AbstractClassValueV0, AbstractPropertyValueCandidateV0, AbstractPropertyValueNarrowingV0,
AbstractPropertyValueV0, AbstractValueDomainSummaryV0, CascadeContextV0,
CascadeValueFamilyMemberV0, ClassBoundaryEffectV0, ClassValueFlowAnalysisV0,
ClassValueFlowIncrementalAnalysisV0, CssValueValidationClassV0, ExternalStringTypeFactsV0,
FactPrecision, FirstWitnessErrorV0, GuardAtomV0, GuardedTokenInputV0, GuardedTokenLanguageV0,
GuardedTokenMapInputV0, GuardedTokenMapV0, GuardedTokenObserverV0, Lin01ProvenanceSemiringV0,
LinearProvenancePathV0, LinearProvenanceV0, NaturalCountProvenanceSemiringV0,
OmenaAbstractValueCoverageDirectionV0, OmenaAbstractValuePrecisionBasisV0,
OmenaAbstractValuePrecisionWitnessV0, PolynomialProvenanceProjectionV0,
PolynomialProvenanceTermV0, PolynomialProvenanceV0, PolynomialProvenanceVariableV0,
ProvenanceSemiringLawReportV0, ReducedClassValueProductIterationV0, ReducedClassValueProductV0,
SelectorProjectionCertaintyV0, SpecStandardPropertyValueValidatorV0, TokenObserverProjectionV0,
abstract_class_value_from_facts, abstract_class_value_kind,
derive_context_indexed_cascade_restriction_maps_v0, fact_precision_from_class_value,
fact_precision_from_class_value_with_witness, iterate_reduced_class_value_product_constraints,
join_abstract_class_values, narrow_abstract_property_value_for_authored_cascade_branch,
narrow_abstract_property_value_for_cascade_branch,
narrow_abstract_property_value_for_pseudo_state, prefix_suffix_class_value,
summarize_context_indexed_cascade_value_family_v0,
summarize_polynomial_provenance_from_linear_v0, top_class_value,
validate_registered_property_value_v0, validate_standard_property_value_v0,
verify_provenance_semiring_laws_on_fixtures,
};
#[allow(deprecated)]
#[deprecated(
since = "0.4.0",
note = "use the context-indexed cascade value family adapters; removal is not before 1.0 and requires downstream migration plus zero audited non-compatibility uses"
)]
pub use omena_abstract_value::{
derive_cascade_restriction_maps_v0, summarize_cascade_value_family_v0,
};
pub use omena_incremental::{
IncrementalEditDistancePriorityInputV0, IncrementalGraphInputV0,
IncrementalInvalidationPriorityPlanV0, IncrementalNodeInputV0, IncrementalRevisionV0,
OmenaIncrementalDatabaseV0, OmenaSalsaDatabaseV0, OmenaWorkspaceSnapshotIdV0,
snapshot_from_graph_input,
};
pub use omena_refinement::{
CascadeDimensionalRefinementBridgeV0, RefinementPropertyPredicateV0,
summarize_cascade_dimensional_refinement_bridge_v0,
};
pub use omena_resolver::OmenaResolverSourceResolutionRuntimeIndexV0;
use omena_resolver::{
summarize_omena_resolver_canonical_producer_signal, summarize_omena_resolver_query_fragments,
summarize_omena_resolver_source_resolution_runtime,
};
pub use omena_value_lattice::{
canonicalize_css_value, split_top_level_value_arguments,
split_top_level_whitespace_value_components,
};
use serde::{Deserialize, Serialize};
pub const OMENA_QUERY_CURRENT_SCHEMA_VERSION: &str = "0";
pub const OMENA_QUERY_CURRENT_SCHEMA_VERSION_LABEL: &str = "V0";
#[cfg(feature = "test-support")]
thread_local! {
static SELECTOR_PROJECTION_EVALUATION_COUNT: Cell<usize> = const { Cell::new(0) };
}
#[cfg(feature = "test-support")]
pub fn reset_selector_projection_evaluation_count_for_test() {
SELECTOR_PROJECTION_EVALUATION_COUNT.with(|counter| counter.set(0));
}
#[cfg(feature = "test-support")]
pub fn selector_projection_evaluation_count_for_test() -> usize {
SELECTOR_PROJECTION_EVALUATION_COUNT.with(Cell::get)
}
#[cfg(feature = "test-support")]
fn record_selector_projection_evaluation_for_test() {
SELECTOR_PROJECTION_EVALUATION_COUNT.with(|counter| counter.set(counter.get() + 1));
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OmenaQueryAnalysisPrecisionV0 {
pub product: String,
pub value_domain: String,
pub flow_sensitivity: String,
pub context_sensitivity: String,
pub revision_axis: String,
}
const OMENA_QUERY_ANALYSIS_FACT_PRECISION_BY_VALUE_DOMAIN: &[(&str, FactPrecision)] = &[
("cascadeAtPosition", FactPrecision::Exact),
("styleModuleResolution", FactPrecision::Exact),
("classValueResolution", FactPrecision::Conservative),
("classValueUniverse", FactPrecision::Conservative),
("classValueFlow", FactPrecision::Heuristic),
("unknown", FactPrecision::Unknown),
];
pub fn fact_precision_from_analysis_precision(
precision: &OmenaQueryAnalysisPrecisionV0,
) -> FactPrecision {
OMENA_QUERY_ANALYSIS_FACT_PRECISION_BY_VALUE_DOMAIN
.iter()
.find_map(|(value_domain, mapped)| {
(*value_domain == precision.value_domain).then_some(*mapped)
})
.unwrap_or(FactPrecision::Unknown)
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OmenaQueryAnalysisResultV0<TValue> {
pub schema_version: String,
pub product: String,
pub value: TValue,
pub precision: OmenaQueryAnalysisPrecisionV0,
pub provenance: Vec<String>,
pub revision: u64,
}
impl<TValue> OmenaQueryAnalysisResultV0<TValue> {
pub fn new(
value: TValue,
precision: OmenaQueryAnalysisPrecisionV0,
provenance: Vec<String>,
revision: u64,
) -> Self {
Self {
schema_version: OMENA_QUERY_CURRENT_SCHEMA_VERSION.to_string(),
product: "omena-query.analysis-result".to_string(),
value,
precision,
provenance,
revision,
}
}
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct OmenaQueryFragmentBundleV0 {
pub schema_version: &'static str,
pub product: &'static str,
pub input_version: String,
pub expression_semantics: ExpressionSemanticsQueryFragmentsV0,
pub source_resolution: SourceResolutionQueryFragmentsV0,
pub selector_usage: SelectorUsageQueryFragmentsV0,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct OmenaQueryExpressionDomainIncrementalFlowAnalysisV0 {
pub schema_version: &'static str,
pub product: &'static str,
pub input_version: String,
pub revision: u64,
pub graph_count: usize,
pub dirty_graph_count: usize,
pub reused_graph_count: usize,
pub analyses: Vec<OmenaQueryExpressionDomainIncrementalFlowAnalysisEntryV0>,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct OmenaQueryExpressionDomainIncrementalFlowAnalysisEntryV0 {
pub graph_id: String,
pub file_path: String,
pub analysis: ClassValueFlowIncrementalAnalysisV0,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct OmenaQueryExpressionDomainSelectorProjectionV0 {
pub schema_version: &'static str,
pub product: &'static str,
pub input_version: String,
pub projection_count: usize,
pub projections: Vec<OmenaQueryExpressionDomainSelectorProjectionEntryV0>,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct OmenaQueryExpressionDomainSelectorProjectionEntryV0 {
pub graph_id: String,
pub file_path: String,
pub node_id: String,
pub target_style_paths: Vec<String>,
pub value_kind: &'static str,
#[serde(skip_serializing_if = "Option::is_none")]
pub reduced_product: Option<ReducedClassValueProductV0>,
pub selector_names: Vec<String>,
pub certainty: SelectorProjectionCertaintyV0,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct OmenaQueryExpressionDomainSelectorPrecisionV0 {
pub graph_id: String,
pub node_id: String,
pub precision: FactPrecision,
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct ExpressionDomainSelectorCertaintyFlowHedgeV0 {
graph_converged: bool,
contains_flow_iteration_limit: bool,
}
#[derive(Default)]
pub struct OmenaQueryExpressionDomainFlowRuntimeV0 {
revision: u64,
artifacts_by_graph_id: BTreeMap<String, SealedClassValueFlowAnalysisArtifactV0>,
read_set_digest_check_count: usize,
analysis_rebuild_count: usize,
artifact_refusal_count: usize,
}
impl OmenaQueryExpressionDomainFlowRuntimeV0 {
pub fn revision(&self) -> u64 {
self.revision
}
pub fn graph_count(&self) -> usize {
self.artifacts_by_graph_id.len()
}
pub fn read_set_digest_check_count(&self) -> usize {
self.read_set_digest_check_count
}
pub fn analysis_rebuild_count(&self) -> usize {
self.analysis_rebuild_count
}
pub fn artifact_refusal_count(&self) -> usize {
self.artifact_refusal_count
}
pub fn analyze_input(
&mut self,
input: &EngineInputV2,
) -> OmenaQueryExpressionDomainIncrementalFlowAnalysisV0 {
self.analyze_input_with_artifact_analyzer(
input,
analyze_class_value_flow_incremental_with_artifact,
)
}
fn analyze_input_with_artifact_analyzer<F>(
&mut self,
input: &EngineInputV2,
mut analyze_artifact: F,
) -> OmenaQueryExpressionDomainIncrementalFlowAnalysisV0
where
F: FnMut(
&omena_abstract_value::ClassValueFlowGraphV0,
Option<&SealedClassValueFlowAnalysisArtifactV0>,
u64,
) -> Result<
ClassValueFlowSealedIncrementalAnalysisV0,
omena_abstract_value::ClassValueFlowArtifactRefusalV0,
>,
{
self.revision += 1;
let revision = self.revision;
let flow_graphs = collect_expression_domain_flow_graphs(input);
let live_graph_ids = flow_graphs
.iter()
.map(|entry| entry.graph_id.clone())
.collect::<BTreeSet<_>>();
self.artifacts_by_graph_id
.retain(|graph_id, _| live_graph_ids.contains(graph_id));
let analyses = flow_graphs
.into_iter()
.map(|entry| {
let previous_artifact = self.artifacts_by_graph_id.get(&entry.graph_id);
let sealed_analysis = analyze_artifact(&entry.graph, previous_artifact, revision);
let (analysis, next_artifact) = match sealed_analysis {
Ok(sealed_analysis) => {
self.read_set_digest_check_count = self
.read_set_digest_check_count
.saturating_add(sealed_analysis.read_set_digest_check_count);
self.analysis_rebuild_count = self
.analysis_rebuild_count
.saturating_add(sealed_analysis.analysis_rebuild_count);
let (analysis, next_artifact) = legacy_flow_analysis_from_sealed(
&entry.graph,
revision,
previous_artifact,
sealed_analysis,
);
(analysis, Some(next_artifact))
}
Err(_) => {
self.read_set_digest_check_count = self
.read_set_digest_check_count
.saturating_add(usize::from(previous_artifact.is_some()));
self.analysis_rebuild_count = self.analysis_rebuild_count.saturating_add(1);
self.artifact_refusal_count = self.artifact_refusal_count.saturating_add(1);
(
analyze_class_value_flow_incremental(&entry.graph, None, revision),
None,
)
}
};
if let Some(next_artifact) = next_artifact {
self.artifacts_by_graph_id
.insert(entry.graph_id.clone(), next_artifact);
} else {
self.artifacts_by_graph_id.remove(&entry.graph_id);
}
OmenaQueryExpressionDomainIncrementalFlowAnalysisEntryV0 {
graph_id: entry.graph_id,
file_path: entry.file_path,
analysis,
}
})
.collect::<Vec<_>>();
let dirty_graph_count = analyses
.iter()
.filter(|entry| entry.analysis.incremental_plan.dirty_node_count > 0)
.count();
let reused_graph_count = analyses
.iter()
.filter(|entry| entry.analysis.reused_previous_analysis)
.count();
OmenaQueryExpressionDomainIncrementalFlowAnalysisV0 {
schema_version: OMENA_QUERY_CURRENT_SCHEMA_VERSION,
product: "omena-query.expression-domain-incremental-flow-analysis",
input_version: input.version.clone(),
revision,
graph_count: analyses.len(),
dirty_graph_count,
reused_graph_count,
analyses,
}
}
}
fn legacy_flow_analysis_from_sealed(
graph: &omena_abstract_value::ClassValueFlowGraphV0,
revision: u64,
previous_artifact: Option<&SealedClassValueFlowAnalysisArtifactV0>,
sealed: ClassValueFlowSealedIncrementalAnalysisV0,
) -> (
ClassValueFlowIncrementalAnalysisV0,
SealedClassValueFlowAnalysisArtifactV0,
) {
let ClassValueFlowSealedIncrementalAnalysisV0 {
reused_previous_analysis,
incremental_plan,
analysis,
next_artifact,
..
} = sealed;
let (incremental_plan, next_snapshot) = match incremental_plan {
Some(incremental_plan) => (incremental_plan, next_artifact.snapshot().clone()),
None => {
let mut database = OmenaIncrementalDatabaseV0::default();
if let Some(previous_artifact) = previous_artifact {
database.restore_snapshot(previous_artifact.snapshot());
}
let update = database
.plan_and_upsert_graph_input(&class_value_flow_incremental_input(graph, revision));
(update.incremental_plan, update.next_snapshot)
}
};
(
ClassValueFlowIncrementalAnalysisV0 {
schema_version: "0",
product: "omena-abstract-value.incremental-flow-analysis",
reused_previous_analysis,
incremental_plan,
next_snapshot,
analysis,
},
next_artifact,
)
}
pub fn summarize_omena_query_core_abstract_value_domain() -> AbstractValueDomainSummaryV0 {
summarize_omena_abstract_value_domain()
}
pub fn summarize_omena_query_fragment_bundle(input: &EngineInputV2) -> OmenaQueryFragmentBundleV0 {
OmenaQueryFragmentBundleV0 {
schema_version: OMENA_QUERY_CURRENT_SCHEMA_VERSION,
product: "omena-query.fragment-bundle",
input_version: input.version.clone(),
expression_semantics: summarize_omena_query_expression_semantics_query_fragments(input),
source_resolution: summarize_omena_query_source_resolution_query_fragments(input),
selector_usage: summarize_omena_query_selector_usage_query_fragments(input),
}
}
pub fn summarize_omena_query_expression_semantics_query_fragments(
input: &EngineInputV2,
) -> ExpressionSemanticsQueryFragmentsV0 {
summarize_expression_semantics_query_fragments_input(input)
}
pub fn summarize_omena_query_expression_domain_flow_analysis(
input: &EngineInputV2,
) -> ExpressionDomainFlowAnalysisV0 {
summarize_expression_domain_flow_analysis_input(input)
}
pub fn summarize_omena_query_expression_domain_control_flow_analysis(
input: &EngineInputV2,
) -> ExpressionDomainControlFlowAnalysisV0 {
summarize_expression_domain_control_flow_analysis_input(input)
}
pub fn summarize_omena_query_expression_domain_call_site_flow_analysis(
input: &EngineInputV2,
) -> ExpressionDomainCallSiteFlowAnalysisV0 {
summarize_expression_domain_call_site_flow_analysis_input(input)
}
pub fn summarize_omena_query_expression_domain_provenance_explanations(
input: &EngineInputV2,
) -> ExpressionDomainProvenanceExplanationsV0 {
summarize_expression_domain_provenance_explanations_input(input)
}
pub fn summarize_omena_query_expression_domain_reduced_product_iteration(
input: &EngineInputV2,
) -> ExpressionDomainReducedProductIterationV0 {
summarize_expression_domain_reduced_product_iteration_input(input)
}
pub fn summarize_omena_query_expression_domain_incremental_flow_analysis(
input: &EngineInputV2,
runtime: &mut OmenaQueryExpressionDomainFlowRuntimeV0,
) -> OmenaQueryExpressionDomainIncrementalFlowAnalysisV0 {
runtime.analyze_input(input)
}
pub fn summarize_omena_query_expression_domain_incremental_flow_analysis_result(
input: &EngineInputV2,
runtime: &mut OmenaQueryExpressionDomainFlowRuntimeV0,
) -> OmenaQueryAnalysisResultV0<OmenaQueryExpressionDomainIncrementalFlowAnalysisV0> {
let value = runtime.analyze_input(input);
let revision = value.revision;
OmenaQueryAnalysisResultV0::new(
value,
OmenaQueryAnalysisPrecisionV0 {
product: "omena-query.analysis-precision".to_string(),
value_domain: "classValueFlow".to_string(),
flow_sensitivity: "incrementalDataflow".to_string(),
context_sensitivity: "perExpressionGraph".to_string(),
revision_axis: "OmenaQueryExpressionDomainFlowRuntimeV0.revision".to_string(),
},
vec![
"omena-query-core.expression-domain-runtime".to_string(),
"omena-abstract-value.incremental-class-value-flow".to_string(),
],
revision,
)
}
pub fn summarize_omena_query_expression_domain_selector_projection(
input: &EngineInputV2,
) -> OmenaQueryExpressionDomainSelectorProjectionV0 {
summarize_omena_query_expression_domain_selector_projection_with_precision(input).0
}
pub fn summarize_omena_query_expression_domain_selector_projection_with_precision(
input: &EngineInputV2,
) -> (
OmenaQueryExpressionDomainSelectorProjectionV0,
Vec<OmenaQueryExpressionDomainSelectorPrecisionV0>,
) {
summarize_omena_query_expression_domain_selector_projection_with_precision_and_style_path_resolver(
input,
|target, _known| Some(target.to_string()),
)
}
pub fn summarize_omena_query_expression_domain_selector_projection_with_precision_and_style_path_resolver<
F,
>(
input: &EngineInputV2,
resolve_style_path: F,
) -> (
OmenaQueryExpressionDomainSelectorProjectionV0,
Vec<OmenaQueryExpressionDomainSelectorPrecisionV0>,
)
where
F: Fn(&str, &[String]) -> Option<String>,
{
#[cfg(feature = "test-support")]
record_selector_projection_evaluation_for_test();
let selector_certainty_flow_hedges = expression_domain_selector_certainty_flow_hedges(input);
summarize_omena_query_expression_domain_selector_projection_with_flow_hedges(
input,
resolve_style_path,
&selector_certainty_flow_hedges,
)
}
fn summarize_omena_query_expression_domain_selector_projection_with_flow_hedges<F>(
input: &EngineInputV2,
resolve_style_path: F,
selector_certainty_flow_hedges: &BTreeMap<
(String, String),
ExpressionDomainSelectorCertaintyFlowHedgeV0,
>,
) -> (
OmenaQueryExpressionDomainSelectorProjectionV0,
Vec<OmenaQueryExpressionDomainSelectorPrecisionV0>,
)
where
F: Fn(&str, &[String]) -> Option<String>,
{
let style_selectors_by_path = style_selector_universe_by_path(input);
let known_style_paths = style_selectors_by_path.keys().cloned().collect::<Vec<_>>();
let expression_targets = expression_target_style_paths(input);
let flow_analysis = summarize_omena_query_expression_domain_flow_analysis(input);
let mut projections = Vec::new();
let mut precisions = Vec::new();
for graph in flow_analysis.analyses {
for node in graph.analysis.nodes {
let target_style_paths = target_style_paths_for_flow_node(
node.id.as_str(),
node.predecessor_ids.as_slice(),
&expression_targets,
);
let resolved_target_style_paths = target_style_paths
.iter()
.map(|path| resolve_style_path(path, known_style_paths.as_slice()))
.collect::<Option<Vec<_>>>();
let selector_universe = selector_universe_for_targets(
resolved_target_style_paths.as_deref(),
&style_selectors_by_path,
);
let projection = project_abstract_value_selectors(&node.value, &selector_universe);
let certainty = hedge_selector_projection_certainty(
projection.certainty,
selector_certainty_flow_hedges.get(&(graph.file_path.clone(), node.id.clone())),
);
precisions.push(OmenaQueryExpressionDomainSelectorPrecisionV0 {
graph_id: graph.graph_id.clone(),
node_id: node.id.clone(),
precision: fact_precision_from_class_value(&node.value),
});
projections.push(OmenaQueryExpressionDomainSelectorProjectionEntryV0 {
graph_id: graph.graph_id.clone(),
file_path: graph.file_path.clone(),
node_id: node.id,
target_style_paths,
value_kind: node.value_kind,
reduced_product: summarize_reduced_class_value_product(&node.value),
selector_names: projection.selector_names,
certainty,
});
}
}
(
OmenaQueryExpressionDomainSelectorProjectionV0 {
schema_version: OMENA_QUERY_CURRENT_SCHEMA_VERSION,
product: "omena-query.expression-domain-selector-projection",
input_version: input.version.clone(),
projection_count: projections.len(),
projections,
},
precisions,
)
}
fn expression_domain_selector_certainty_flow_hedges(
input: &EngineInputV2,
) -> BTreeMap<(String, String), ExpressionDomainSelectorCertaintyFlowHedgeV0> {
bind_expression_domain_selector_certainty_flow_hedges(
input,
summarize_omena_query_expression_domain_control_flow_analysis(input),
)
}
fn bind_expression_domain_selector_certainty_flow_hedges(
input: &EngineInputV2,
control_flow: ExpressionDomainControlFlowAnalysisV0,
) -> BTreeMap<(String, String), ExpressionDomainSelectorCertaintyFlowHedgeV0> {
let control_flow_facts = input
.type_facts
.iter()
.filter(|entry| entry.control_flow_graph.is_some())
.collect::<Vec<_>>();
assert_eq!(
control_flow_facts.len(),
control_flow.analyses.len(),
"selector-certainty flow hedge requires one control analysis per control-flow type fact"
);
let mut hedges = BTreeMap::new();
for (entry, analyzed) in control_flow_facts.into_iter().zip(control_flow.analyses) {
let diagnostic_graph_id = format!(
"{}:{}:expression-domain-control-flow",
entry.file_path, entry.expression_id
);
assert_eq!(
analyzed.file_path, entry.file_path,
"selector-certainty flow hedge control analysis order/file mismatch"
);
assert_eq!(
analyzed.graph_id, diagnostic_graph_id,
"selector-certainty flow hedge control analysis order/graph mismatch"
);
let contains_flow_iteration_limit = analyzed
.analysis
.flow_analysis
.nodes
.iter()
.any(|node| abstract_value_contains_flow_iteration_limit(&node.value));
let key = (entry.file_path.clone(), entry.expression_id.clone());
let previous = hedges.insert(
key.clone(),
ExpressionDomainSelectorCertaintyFlowHedgeV0 {
graph_converged: analyzed.analysis.flow_analysis.converged,
contains_flow_iteration_limit,
},
);
assert!(
previous.is_none(),
"selector-certainty flow hedge duplicate type-fact key: file_path={:?} expression_id={:?}",
key.0,
key.1
);
}
hedges
}
fn hedge_selector_projection_certainty(
base: SelectorProjectionCertaintyV0,
flow_hedge: Option<&ExpressionDomainSelectorCertaintyFlowHedgeV0>,
) -> SelectorProjectionCertaintyV0 {
flow_hedge.map_or(base, |hedge| {
hedge_selector_certainty_for_flow(
base,
hedge.graph_converged,
hedge.contains_flow_iteration_limit,
)
})
}
fn hedge_selector_certainty_for_flow(
base: SelectorProjectionCertaintyV0,
graph_converged: bool,
contains_flow_iteration_limit: bool,
) -> SelectorProjectionCertaintyV0 {
if graph_converged && !contains_flow_iteration_limit {
base
} else {
SelectorProjectionCertaintyV0::Possible
}
}
fn abstract_value_contains_flow_iteration_limit(value: &AbstractClassValueV0) -> bool {
let provenance = match value {
AbstractClassValueV0::Automaton { provenance, .. }
| AbstractClassValueV0::Prefix { provenance, .. }
| AbstractClassValueV0::Suffix { provenance, .. }
| AbstractClassValueV0::PrefixSuffix { provenance, .. }
| AbstractClassValueV0::CharInclusion { provenance, .. }
| AbstractClassValueV0::Composite { provenance, .. }
| AbstractClassValueV0::Top { provenance } => *provenance,
AbstractClassValueV0::Bottom
| AbstractClassValueV0::Exact { .. }
| AbstractClassValueV0::FiniteSet { .. } => None,
};
provenance == Some(AbstractClassValueProvenanceV0::FlowIterationLimit)
}
fn expression_target_style_paths(input: &EngineInputV2) -> BTreeMap<String, String> {
input
.sources
.iter()
.flat_map(|source| source.document.class_expressions.iter())
.map(|expression| (expression.id.clone(), expression.scss_module_path.clone()))
.collect()
}
fn style_selector_universe_by_path(input: &EngineInputV2) -> BTreeMap<String, Vec<String>> {
input
.styles
.iter()
.map(|style| {
let selector_names = style
.document
.selectors
.iter()
.map(|selector| {
selector
.canonical_name
.clone()
.unwrap_or_else(|| selector.name.clone())
})
.collect::<BTreeSet<_>>()
.into_iter()
.collect::<Vec<_>>();
(style.file_path.clone(), selector_names)
})
.collect()
}
fn target_style_paths_for_flow_node(
node_id: &str,
predecessor_ids: &[String],
expression_targets: &BTreeMap<String, String>,
) -> Vec<String> {
let mut targets = BTreeSet::new();
if let Some(target) = expression_targets.get(node_id) {
targets.insert(target.clone());
}
for predecessor_id in predecessor_ids {
if let Some(target) = expression_targets.get(predecessor_id) {
targets.insert(target.clone());
}
}
targets.into_iter().collect()
}
fn selector_universe_for_targets(
target_style_paths: Option<&[String]>,
style_selectors_by_path: &BTreeMap<String, Vec<String>>,
) -> Vec<String> {
let mut selectors = BTreeSet::new();
if target_style_paths.is_none_or(<[String]>::is_empty) {
for selector_names in style_selectors_by_path.values() {
selectors.extend(selector_names.iter().cloned());
}
} else if let Some(target_style_paths) = target_style_paths {
for target_style_path in target_style_paths {
if let Some(selector_names) = style_selectors_by_path.get(target_style_path) {
selectors.extend(selector_names.iter().cloned());
}
}
}
selectors.into_iter().collect()
}
pub fn summarize_omena_query_source_resolution_query_fragments(
input: &EngineInputV2,
) -> SourceResolutionQueryFragmentsV0 {
summarize_omena_resolver_query_fragments(input)
}
pub fn summarize_omena_query_selector_usage_query_fragments(
input: &EngineInputV2,
) -> SelectorUsageQueryFragmentsV0 {
summarize_selector_usage_query_fragments_input(input)
}
pub fn summarize_omena_query_source_resolution_canonical_producer_signal(
input: &EngineInputV2,
) -> SourceResolutionCanonicalProducerSignalV0 {
summarize_omena_resolver_canonical_producer_signal(input)
}
pub fn summarize_omena_query_source_resolution_runtime(
input: &EngineInputV2,
) -> OmenaResolverSourceResolutionRuntimeIndexV0 {
summarize_omena_resolver_source_resolution_runtime(input)
}
pub fn summarize_omena_query_expression_semantics_canonical_producer_signal(
input: &EngineInputV2,
) -> ExpressionSemanticsCanonicalProducerSignalV0 {
summarize_expression_semantics_canonical_producer_signal_input(input)
}
pub fn summarize_omena_query_selector_usage_canonical_producer_signal(
input: &EngineInputV2,
) -> SelectorUsageCanonicalProducerSignalV0 {
summarize_selector_usage_canonical_producer_signal_input(input)
}
#[cfg(test)]
mod tests {
use super::*;
fn serialized_json_bytes<T: serde::Serialize>(
value: &T,
label: &str,
) -> Result<Vec<u8>, String> {
serde_json::to_vec(value).map_err(|error| format!("{label} serialization failed: {error}"))
}
fn selector_certainty_product_input() -> EngineInputV2 {
let range = RangeV2 {
start: PositionV2 {
line: 0,
character: 0,
},
end: PositionV2 {
line: 0,
character: 11,
},
};
EngineInputV2 {
version: "selector-certainty-product".to_string(),
sources: vec![SourceAnalysisInputV2 {
document: SourceDocumentV2 {
class_expressions: vec![ClassExpressionInputV2 {
id: "expr-certainty".to_string(),
kind: "styleAccess".to_string(),
scss_module_path: "/tmp/App.module.scss".to_string(),
range: range.clone(),
class_name: Some("x".to_string()),
root_binding_decl_id: None,
access_path: Some(vec!["styles".to_string(), "x".to_string()]),
}],
},
}],
styles: vec![StyleAnalysisInputV2 {
file_path: "/tmp/App.module.scss".to_string(),
source: None,
document: StyleDocumentV2 {
selectors: vec![StyleSelectorV2 {
name: "x".to_string(),
view_kind: "canonical".to_string(),
canonical_name: Some("x".to_string()),
range,
nested_safety: Some("safe".to_string()),
composes: None,
bem_suffix: None,
}],
},
}],
type_facts: vec![TypeFactEntryV2 {
file_path: "/tmp/App.tsx".to_string(),
expression_id: "expr-certainty".to_string(),
facts: StringTypeFactsV2 {
kind: "exact".to_string(),
constraint_kind: None,
values: Some(vec!["x".to_string()]),
prefix: None,
suffix: None,
min_len: None,
max_len: None,
char_must: None,
char_may: None,
may_include_other_chars: None,
provenance: None,
},
control_flow_graph: Some(engine_input_producers::TypeFactControlFlowGraphV2 {
entry_block_id: "seed".to_string(),
blocks: vec![
engine_input_producers::TypeFactControlFlowBlockV2 {
id: "seed".to_string(),
kind: "assignment".to_string(),
transfer_kind: "assignFacts".to_string(),
successor_block_ids: vec!["loop".to_string()],
symbol_ordinal: None,
variable_name: None,
expression_kind: None,
boundary_effect: "unknownBoundary".to_string(),
facts: Some(StringTypeFactsV2 {
kind: "finiteSet".to_string(),
constraint_kind: None,
values: Some(vec!["a".to_string(), "b".to_string()]),
prefix: None,
suffix: None,
min_len: None,
max_len: None,
char_must: None,
char_may: None,
may_include_other_chars: None,
provenance: None,
}),
},
engine_input_producers::TypeFactControlFlowBlockV2 {
id: "loop".to_string(),
kind: "loop".to_string(),
transfer_kind: "concatFacts".to_string(),
successor_block_ids: vec!["loop".to_string()],
symbol_ordinal: None,
variable_name: None,
expression_kind: None,
boundary_effect: "unknownBoundary".to_string(),
facts: None,
},
],
}),
}],
}
}
fn selector_certainty_colon_collision_input() -> EngineInputV2 {
let mut input = selector_certainty_product_input();
input.sources[0].document.class_expressions[0].id = "b:c".to_string();
input.sources[0].document.class_expressions[0].class_name = Some("x".to_string());
input.sources[0].document.class_expressions[0].access_path =
Some(vec!["styles".to_string(), "x".to_string()]);
let second_expression = ClassExpressionInputV2 {
id: "c".to_string(),
kind: "styleAccess".to_string(),
scss_module_path: "/tmp/App.module.scss".to_string(),
range: input.sources[0].document.class_expressions[0].range.clone(),
class_name: Some("y".to_string()),
root_binding_decl_id: None,
access_path: Some(vec!["styles".to_string(), "y".to_string()]),
};
input.sources[0]
.document
.class_expressions
.push(second_expression);
let second_selector = StyleSelectorV2 {
name: "y".to_string(),
view_kind: "canonical".to_string(),
canonical_name: Some("y".to_string()),
range: input.styles[0].document.selectors[0].range.clone(),
nested_safety: Some("safe".to_string()),
composes: None,
bem_suffix: None,
};
input.styles[0].document.selectors.push(second_selector);
let mut first_fact = input.type_facts[0].clone();
first_fact.file_path = "/tmp/A".to_string();
first_fact.expression_id = "b:c".to_string();
let mut second_fact = first_fact.clone();
second_fact.file_path = "/tmp/A:b".to_string();
second_fact.expression_id = "c".to_string();
second_fact.facts.values = Some(vec!["y".to_string()]);
if let Some(second_graph) = second_fact.control_flow_graph.as_mut() {
second_graph.blocks.truncate(1);
second_graph.blocks[0].successor_block_ids.clear();
}
input.type_facts = vec![first_fact, second_fact];
input
}
#[test]
fn expression_domain_runtime_reuses_graph_databases_across_revisions() {
let input = EngineInputV2 {
version: "core-runtime".to_string(),
sources: Vec::new(),
styles: Vec::new(),
type_facts: Vec::new(),
};
let mut runtime = OmenaQueryExpressionDomainFlowRuntimeV0::default();
let first =
summarize_omena_query_expression_domain_incremental_flow_analysis(&input, &mut runtime);
let second =
summarize_omena_query_expression_domain_incremental_flow_analysis(&input, &mut runtime);
assert_eq!(first.revision, 1);
assert_eq!(second.revision, 2);
assert_eq!(runtime.revision(), 2);
}
#[test]
fn expression_domain_runtime_checks_sealed_artifacts_before_product_reuse() {
let input = selector_certainty_product_input();
let mut runtime = OmenaQueryExpressionDomainFlowRuntimeV0::default();
let first =
summarize_omena_query_expression_domain_incremental_flow_analysis(&input, &mut runtime);
assert!(
first.graph_count > 0,
"fixture must exercise product flow graphs"
);
assert_eq!(runtime.read_set_digest_check_count(), 0);
assert_eq!(runtime.analysis_rebuild_count(), first.graph_count);
let second =
summarize_omena_query_expression_domain_incremental_flow_analysis(&input, &mut runtime);
eprintln!(
"productFlowArtifactCounters graphs={} digestChecks={} rebuilds={} refusals={}",
second.graph_count,
runtime.read_set_digest_check_count(),
runtime.analysis_rebuild_count(),
runtime.artifact_refusal_count(),
);
assert_eq!(second.reused_graph_count, second.graph_count);
assert_eq!(runtime.read_set_digest_check_count(), second.graph_count);
assert_eq!(
runtime.analysis_rebuild_count(),
first.graph_count,
"verified reuse must not rebuild the flow analysis"
);
assert_eq!(runtime.artifact_refusal_count(), 0);
}
#[test]
fn expression_domain_runtime_preserves_legacy_incremental_plan_bytes_across_reuse_revisions()
-> Result<(), String> {
let input = selector_certainty_product_input();
let flow_graphs = collect_expression_domain_flow_graphs(&input);
assert_eq!(
flow_graphs.len(),
1,
"fixture must isolate one product graph"
);
let graph = &flow_graphs[0].graph;
let mut runtime = OmenaQueryExpressionDomainFlowRuntimeV0::default();
let mut legacy_snapshot = None;
let mut legacy_analysis = None;
for revision in 1..=5 {
let product = summarize_omena_query_expression_domain_incremental_flow_analysis(
&input,
&mut runtime,
);
let legacy = omena_abstract_value::analyze_class_value_flow_incremental_with_reuse(
graph,
legacy_snapshot.as_ref(),
legacy_analysis.as_ref(),
revision,
);
let product_analysis = &product.analyses[0].analysis;
let product_plan_bytes = serialized_json_bytes(
&product_analysis.incremental_plan,
"product incremental plan",
)?;
let legacy_plan_bytes =
serialized_json_bytes(&legacy.incremental_plan, "legacy incremental plan")?;
let product_changed_at = product_analysis
.incremental_plan
.nodes
.iter()
.map(|node| node.changed_at.value)
.collect::<Vec<_>>();
let legacy_changed_at = legacy
.incremental_plan
.nodes
.iter()
.map(|node| node.changed_at.value)
.collect::<Vec<_>>();
assert_eq!(
product_changed_at, legacy_changed_at,
"unchanged product nodes must preserve the legacy changedAt stamps at revision {revision}"
);
assert_eq!(
product_plan_bytes, legacy_plan_bytes,
"incrementalPlan bytes diverged from the independent legacy oracle at revision {revision}"
);
assert_eq!(
serialized_json_bytes(product_analysis, "product incremental analysis")?,
serialized_json_bytes(&legacy, "legacy incremental analysis")?,
"public incremental analysis bytes diverged at revision {revision}"
);
if revision > 1 {
assert_eq!(product.reused_graph_count, 1);
assert!(product_analysis.reused_previous_analysis);
assert!(legacy.reused_previous_analysis);
}
legacy_snapshot = Some(legacy.next_snapshot);
legacy_analysis = Some(legacy.analysis);
}
let mut changed_input = selector_certainty_product_input();
changed_input.type_facts[0].facts.values = Some(vec!["changed".to_string()]);
let changed_graphs = collect_expression_domain_flow_graphs(&changed_input);
let changed_product = summarize_omena_query_expression_domain_incremental_flow_analysis(
&changed_input,
&mut runtime,
);
let changed_legacy = omena_abstract_value::analyze_class_value_flow_incremental_with_reuse(
&changed_graphs[0].graph,
legacy_snapshot.as_ref(),
legacy_analysis.as_ref(),
6,
);
assert_eq!(changed_product.reused_graph_count, 0);
assert!(
!changed_product.analyses[0]
.analysis
.reused_previous_analysis
);
assert!(!changed_legacy.reused_previous_analysis);
assert_eq!(
serialized_json_bytes(
&changed_product.analyses[0].analysis,
"changed product analysis",
)?,
serialized_json_bytes(&changed_legacy, "changed legacy analysis")?,
"a member change after consecutive reuse revisions must match the independent legacy oracle"
);
eprintln!(
"productFlowArtifactByteIdentity graphs=1 reuseRevisions=4 memberChanges=1 digestChecks={} rebuilds={} refusals={}",
runtime.read_set_digest_check_count(),
runtime.analysis_rebuild_count(),
runtime.artifact_refusal_count(),
);
assert_eq!(runtime.read_set_digest_check_count(), 5);
assert_eq!(runtime.analysis_rebuild_count(), 2);
assert_eq!(runtime.artifact_refusal_count(), 0);
Ok(())
}
#[test]
fn expression_domain_runtime_refusal_fallback_rebuilds_and_recovers() -> Result<(), String> {
let input = selector_certainty_product_input();
let graph = collect_expression_domain_flow_graphs(&input)
.pop()
.ok_or_else(|| "fixture must produce one graph".to_string())?
.graph;
let mut runtime = OmenaQueryExpressionDomainFlowRuntimeV0::default();
let first = runtime.analyze_input(&input);
let second = runtime.analyze_input(&input);
assert_eq!(first.graph_count, 1);
assert_eq!(second.reused_graph_count, 1);
let mut injected = false;
let third = runtime.analyze_input_with_artifact_analyzer(
&input,
|graph, previous_artifact, revision| {
if revision == 3 && previous_artifact.is_some() && !injected {
injected = true;
return Err(omena_abstract_value::ClassValueFlowArtifactRefusalV0 {
schema_version: "0",
product: "omena-abstract-value.flow-analysis-artifact-refusal",
cause: omena_abstract_value::ClassValueFlowArtifactRefusalCauseV0::ArtifactDigestMismatch,
});
}
analyze_class_value_flow_incremental_with_artifact(
graph,
previous_artifact,
revision,
)
},
);
assert!(injected, "the product refusal seam must be exercised");
assert_eq!(third.reused_graph_count, 0);
assert_eq!(runtime.artifact_refusal_count(), 1);
assert_eq!(runtime.analysis_rebuild_count(), 2);
assert_eq!(
serialized_json_bytes(&third.analyses[0].analysis, "fallback analysis")?,
serialized_json_bytes(
&analyze_class_value_flow_incremental(&graph, None, 3),
"fresh fallback oracle",
)?,
"a refused artifact must serve the fresh fallback value"
);
let fourth = runtime.analyze_input(&input);
assert_eq!(fourth.reused_graph_count, 0);
assert_eq!(runtime.analysis_rebuild_count(), 3);
assert_eq!(runtime.artifact_refusal_count(), 1);
let fifth = runtime.analyze_input(&input);
assert_eq!(fifth.reused_graph_count, 1);
assert_eq!(runtime.analysis_rebuild_count(), 3);
assert_eq!(runtime.artifact_refusal_count(), 1);
assert_eq!(
fifth.analyses[0].analysis.analysis,
fourth.analyses[0].analysis.analysis
);
eprintln!(
"productFlowArtifactRefusalRecovery graphs=1 refusalRevision=3 fallbackRebuilds=1 reseedRebuilds=1 reuseResumedRevision=5 totalRebuilds={} refusals={}",
runtime.analysis_rebuild_count(),
runtime.artifact_refusal_count(),
);
Ok(())
}
#[test]
fn nonconverged_flow_hedge_demotes_typed_query_projection() {
let input = selector_certainty_product_input();
let graph_id = "/tmp/App.tsx:expr-certainty:expression-domain-control-flow";
let control_flow = summarize_omena_query_expression_domain_control_flow_analysis(&input);
let projection = summarize_omena_query_expression_domain_selector_projection(&input);
let control_entry = &control_flow.analyses[0];
let entry = &projection.projections[0];
println!(
"certainty-hedge-census graphId={graph_id} hedged=1 base=exact certainty=possible"
);
assert_eq!(control_entry.graph_id, graph_id);
assert!(!control_entry.analysis.flow_analysis.converged);
assert!(
control_entry
.analysis
.flow_analysis
.nodes
.iter()
.all(|node| {
matches!(
&node.value,
AbstractClassValueV0::Top {
provenance: Some(AbstractClassValueProvenanceV0::FlowIterationLimit)
}
)
})
);
assert_eq!(entry.value_kind, "exact");
assert_eq!(entry.selector_names, vec!["x".to_string()]);
assert_eq!(entry.certainty, SelectorProjectionCertaintyV0::Possible);
}
#[test]
fn colon_colliding_graph_ids_bind_selector_certainty_by_type_fact_tuple() {
let input = selector_certainty_colon_collision_input();
let control_flow = summarize_omena_query_expression_domain_control_flow_analysis(&input);
let projection = summarize_omena_query_expression_domain_selector_projection(&input);
assert_eq!(control_flow.analyses.len(), 2);
assert_eq!(
control_flow.analyses[0].graph_id, control_flow.analyses[1].graph_id,
"fixture must retain the diagnostic graph-id collision"
);
let nonconverged = projection
.projections
.iter()
.filter(|entry| entry.file_path == "/tmp/A" && entry.node_id == "b:c")
.collect::<Vec<_>>();
let converged = projection
.projections
.iter()
.filter(|entry| entry.file_path == "/tmp/A:b" && entry.node_id == "c")
.collect::<Vec<_>>();
assert_eq!(nonconverged.len(), 1);
assert_eq!(converged.len(), 1);
assert_eq!(
nonconverged[0].certainty,
SelectorProjectionCertaintyV0::Possible
);
assert_eq!(converged[0].certainty, SelectorProjectionCertaintyV0::Exact);
}
#[test]
#[should_panic(expected = "selector-certainty flow hedge duplicate type-fact key")]
fn duplicate_selector_certainty_type_fact_key_fails_closed() {
let mut input = selector_certainty_product_input();
input.type_facts.push(input.type_facts[0].clone());
let _ = summarize_omena_query_expression_domain_selector_projection(&input);
}
#[test]
#[should_panic(
expected = "selector-certainty flow hedge requires one control analysis per control-flow type fact"
)]
fn missing_selector_certainty_control_analysis_fails_closed() {
let input = selector_certainty_product_input();
let mut control_flow =
summarize_omena_query_expression_domain_control_flow_analysis(&input);
control_flow.analyses.clear();
let _ = bind_expression_domain_selector_certainty_flow_hedges(&input, control_flow);
}
#[test]
fn analysis_precision_view_maps_known_producers_and_fails_closed() {
let precision = |value_domain: &str| OmenaQueryAnalysisPrecisionV0 {
product: "omena-query.analysis-precision".to_string(),
value_domain: value_domain.to_string(),
flow_sensitivity: "fixture".to_string(),
context_sensitivity: "fixture".to_string(),
revision_axis: "fixture".to_string(),
};
assert_eq!(
fact_precision_from_analysis_precision(&precision("cascadeAtPosition")),
FactPrecision::Exact
);
assert_eq!(
fact_precision_from_analysis_precision(&precision("styleModuleResolution")),
FactPrecision::Exact
);
assert_eq!(
fact_precision_from_analysis_precision(&precision("classValueResolution")),
FactPrecision::Conservative
);
assert_eq!(
fact_precision_from_analysis_precision(&precision("classValueUniverse")),
FactPrecision::Conservative
);
assert_eq!(
fact_precision_from_analysis_precision(&precision("classValueFlow")),
FactPrecision::Heuristic
);
assert_eq!(
fact_precision_from_analysis_precision(&precision("unknown")),
FactPrecision::Unknown
);
assert_eq!(
fact_precision_from_analysis_precision(&precision("unregistered")),
FactPrecision::Unknown
);
}
}