use omena_abstract_value::AbstractCssValueV0;
#[cfg(feature = "scanner-oracle")]
use omena_parser::lex;
use omena_parser::{StyleDialect, collect_style_facts, parse};
use omena_transform_cst::STABLE_NODE_KEY_TYPE_LABEL_V0;
use super::{
SCSS_CALL_RETURN_RECURSION_LIMIT,
blocks::control_flow_blocks_from_cst,
call_resolution::max_call_stack_depth_observed,
call_return_nodes::{
call_return_node_from_candidate, call_return_node_is_call, call_return_node_is_declaration,
stamp_containing_declarations_from_cst,
},
call_return_resolution::{
build_call_return_edges, stamp_call_resolved_return_values, stamp_contextual_return_values,
},
dialect_label,
lexical::collect_scss_global_variable_declarations,
model::{
OmenaScssEvalCallReturnIrSummaryV0, OmenaScssEvalCallReturnNodeV0,
OmenaScssEvalControlFlowBlockV0, OmenaScssEvalControlFlowIrSummaryV0,
},
return_candidates::collect_scss_return_candidates_from_cst,
symbol_candidates::call_return_candidate_from_sass_symbol_cst,
};
#[cfg(feature = "scanner-oracle")]
use super::{
blocks_scanner::control_flow_block_from_token_scanner_oracle,
call_return_nodes_scanner::stamp_containing_declarations_scanner_oracle,
return_candidates_scanner::collect_scss_return_candidates_scanner_oracle,
symbol_candidates_scanner::call_return_candidate_from_sass_symbol_scanner_oracle,
};
pub fn summarize_scss_control_flow_ir(
source: &str,
dialect: StyleDialect,
) -> Option<OmenaScssEvalControlFlowIrSummaryV0> {
if !matches!(
dialect,
StyleDialect::Css | StyleDialect::Scss | StyleDialect::Sass
) {
return None;
}
let parsed = parse(source, dialect);
let syntax = parsed.syntax();
let blocks = control_flow_blocks_from_cst(source, &syntax, dialect);
summarize_scss_control_flow_ir_from_blocks(dialect, blocks)
}
#[doc(hidden)]
#[cfg(feature = "scanner-oracle")]
pub fn summarize_scss_control_flow_ir_scanner_oracle(
source: &str,
dialect: StyleDialect,
) -> Option<OmenaScssEvalControlFlowIrSummaryV0> {
if !matches!(
dialect,
StyleDialect::Css | StyleDialect::Scss | StyleDialect::Sass
) {
return None;
}
let lexed = lex(source, dialect);
let tokens = lexed.tokens();
let blocks = tokens
.iter()
.enumerate()
.filter_map(|(index, token)| {
control_flow_block_from_token_scanner_oracle(source, tokens, index, token, dialect)
})
.collect::<Vec<_>>();
summarize_scss_control_flow_ir_from_blocks(dialect, blocks)
}
fn summarize_scss_control_flow_ir_from_blocks(
dialect: StyleDialect,
blocks: Vec<OmenaScssEvalControlFlowBlockV0>,
) -> Option<OmenaScssEvalControlFlowIrSummaryV0> {
if dialect == StyleDialect::Css && blocks.is_empty() {
return None;
}
let branch_block_count = blocks
.iter()
.filter(|block| block.kind.starts_with("branch"))
.count();
let loop_block_count = blocks.iter().filter(|block| block.kind == "loop").count();
let back_edge_count = blocks.iter().filter(|block| block.has_back_edge).count();
let edge_count = blocks.iter().map(|block| block.successor_count).sum();
Some(OmenaScssEvalControlFlowIrSummaryV0 {
schema_version: "0",
product: "omena-scss-eval.control-flow-ir",
mode: "oracleOnly",
dialect: dialect_label(dialect),
node_key_type: STABLE_NODE_KEY_TYPE_LABEL_V0,
flat_css_cfg_built: true,
merged_cross_file_graph: false,
block_count: blocks.len(),
branch_block_count,
loop_block_count,
back_edge_count,
edge_count,
blocks,
})
}
pub fn summarize_scss_call_return_ir(
source: &str,
dialect: StyleDialect,
) -> Option<OmenaScssEvalCallReturnIrSummaryV0> {
if !matches!(dialect, StyleDialect::Scss | StyleDialect::Sass) {
return None;
}
let facts = collect_style_facts(source, dialect);
let parsed = parse(source, dialect);
let syntax = parsed.syntax();
let mut candidates = facts
.sass_symbols
.iter()
.filter_map(|symbol| call_return_candidate_from_sass_symbol_cst(source, symbol, &syntax))
.chain(collect_scss_return_candidates_from_cst(
source, &syntax, dialect,
))
.collect::<Vec<_>>();
candidates.sort_by(|left, right| {
left.source_span_start
.cmp(&right.source_span_start)
.then(left.source_span_end.cmp(&right.source_span_end))
.then(left.kind.cmp(right.kind))
.then(left.name.cmp(&right.name))
});
let mut nodes = candidates
.into_iter()
.map(call_return_node_from_candidate)
.collect::<Vec<_>>();
stamp_containing_declarations_from_cst(&mut nodes, &syntax);
summarize_scss_call_return_ir_from_nodes(source, dialect, nodes)
}
#[doc(hidden)]
#[cfg(feature = "scanner-oracle")]
pub fn summarize_scss_call_return_ir_scanner_oracle(
source: &str,
dialect: StyleDialect,
) -> Option<OmenaScssEvalCallReturnIrSummaryV0> {
if !matches!(dialect, StyleDialect::Scss | StyleDialect::Sass) {
return None;
}
let facts = collect_style_facts(source, dialect);
let lexed = lex(source, dialect);
let tokens = lexed.tokens();
let mut candidates = facts
.sass_symbols
.iter()
.filter_map(|symbol| {
call_return_candidate_from_sass_symbol_scanner_oracle(source, tokens, symbol)
})
.chain(collect_scss_return_candidates_scanner_oracle(
source, tokens,
))
.collect::<Vec<_>>();
candidates.sort_by(|left, right| {
left.source_span_start
.cmp(&right.source_span_start)
.then(left.source_span_end.cmp(&right.source_span_end))
.then(left.kind.cmp(right.kind))
.then(left.name.cmp(&right.name))
});
let mut nodes = candidates
.into_iter()
.map(call_return_node_from_candidate)
.collect::<Vec<_>>();
stamp_containing_declarations_scanner_oracle(&mut nodes, tokens);
summarize_scss_call_return_ir_from_nodes(source, dialect, nodes)
}
fn summarize_scss_call_return_ir_from_nodes(
source: &str,
dialect: StyleDialect,
mut nodes: Vec<OmenaScssEvalCallReturnNodeV0>,
) -> Option<OmenaScssEvalCallReturnIrSummaryV0> {
let global_variable_declarations = collect_scss_global_variable_declarations(source, dialect);
stamp_contextual_return_values(&mut nodes, &global_variable_declarations);
let edges = build_call_return_edges(&nodes);
stamp_call_resolved_return_values(&mut nodes, &edges, &global_variable_declarations);
let declaration_node_count = nodes
.iter()
.filter(|node| call_return_node_is_declaration(node))
.count();
let call_node_count = nodes
.iter()
.filter(|node| call_return_node_is_call(node))
.count();
let return_node_count = nodes
.iter()
.filter(|node| node.kind == "functionReturn")
.count();
let return_value_count = nodes
.iter()
.filter(|node| node.return_value.is_some())
.count();
let exact_return_value_count = nodes
.iter()
.filter(|node| matches!(node.return_value, Some(AbstractCssValueV0::Exact { .. })))
.count();
let finite_set_return_value_count = nodes
.iter()
.filter(|node| {
matches!(
node.return_value,
Some(AbstractCssValueV0::FiniteSet { .. })
)
})
.count();
let raw_return_value_count = nodes
.iter()
.filter(|node| matches!(node.return_value, Some(AbstractCssValueV0::Raw { .. })))
.count();
let top_return_value_count = nodes
.iter()
.filter(|node| matches!(node.return_value, Some(AbstractCssValueV0::Top)))
.count();
let bottom_return_value_count = nodes
.iter()
.filter(|node| matches!(node.return_value, Some(AbstractCssValueV0::Bottom)))
.count();
let call_resolved_return_values = nodes
.iter()
.filter(|node| node.kind == "functionCall")
.filter_map(|node| node.call_resolved_return_value.as_ref())
.collect::<Vec<_>>();
let call_resolved_return_value_count = call_resolved_return_values.len();
let exact_call_resolved_return_value_count = call_resolved_return_values
.iter()
.filter(|value| matches!(value, AbstractCssValueV0::Exact { .. }))
.count();
let finite_set_call_resolved_return_value_count = call_resolved_return_values
.iter()
.filter(|value| matches!(value, AbstractCssValueV0::FiniteSet { .. }))
.count();
let raw_call_resolved_return_value_count = call_resolved_return_values
.iter()
.filter(|value| matches!(value, AbstractCssValueV0::Raw { .. }))
.count();
let top_call_resolved_return_value_count = call_resolved_return_values
.iter()
.filter(|value| matches!(value, AbstractCssValueV0::Top))
.count();
let bottom_call_resolved_return_value_count = call_resolved_return_values
.iter()
.filter(|value| matches!(value, AbstractCssValueV0::Bottom))
.count();
let call_argument_values = nodes
.iter()
.flat_map(|node| node.argument_values.iter())
.collect::<Vec<_>>();
let call_argument_value_count = call_argument_values.len();
let exact_call_argument_value_count = call_argument_values
.iter()
.filter(|argument| matches!(&argument.value, AbstractCssValueV0::Exact { .. }))
.count();
let finite_set_call_argument_value_count = call_argument_values
.iter()
.filter(|argument| matches!(&argument.value, AbstractCssValueV0::FiniteSet { .. }))
.count();
let raw_call_argument_value_count = call_argument_values
.iter()
.filter(|argument| matches!(&argument.value, AbstractCssValueV0::Raw { .. }))
.count();
let top_call_argument_value_count = call_argument_values
.iter()
.filter(|argument| matches!(&argument.value, AbstractCssValueV0::Top))
.count();
let bottom_call_argument_value_count = call_argument_values
.iter()
.filter(|argument| matches!(&argument.value, AbstractCssValueV0::Bottom))
.count();
let recursive_edge_count = edges.iter().filter(|edge| edge.recursive).count();
let capped_recursive_call_count = edges
.iter()
.filter(|edge| edge.capped_by_recursion_cap)
.count();
let max_stack_depth_observed = max_call_stack_depth_observed(&nodes, &edges);
Some(OmenaScssEvalCallReturnIrSummaryV0 {
schema_version: "0",
product: "omena-scss-eval.call-return-ir",
mode: "oracleOnly",
dialect: dialect_label(dialect),
node_key_type: STABLE_NODE_KEY_TYPE_LABEL_V0,
recursion_cap: SCSS_CALL_RETURN_RECURSION_LIMIT,
flat_css_cfg_built: false,
merged_cross_file_graph: false,
node_count: nodes.len(),
declaration_node_count,
call_node_count,
return_node_count,
return_value_count,
exact_return_value_count,
finite_set_return_value_count,
raw_return_value_count,
top_return_value_count,
bottom_return_value_count,
call_resolved_return_value_count,
exact_call_resolved_return_value_count,
finite_set_call_resolved_return_value_count,
raw_call_resolved_return_value_count,
top_call_resolved_return_value_count,
bottom_call_resolved_return_value_count,
call_argument_value_count,
exact_call_argument_value_count,
finite_set_call_argument_value_count,
raw_call_argument_value_count,
top_call_argument_value_count,
bottom_call_argument_value_count,
edge_count: edges.len(),
recursive_edge_count,
capped_recursive_call_count,
max_stack_depth_observed,
nodes,
edges,
})
}