Skip to main content

i_slint_compiler/passes/resolving/
remove_noop.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4use crate::{diagnostics::BuildDiagnostics, expression_tree::Expression, parser::SyntaxNode};
5
6/// Remove all expressions that are proven to have no effect from the given Expressions.
7///
8/// This function assumes that the given Expressions will form the an [Expression::CodeBlock], and
9/// will therefore not modify the last Expression in the Vec, as that forms the result value of the
10/// CodeBlock itself.
11pub fn remove_from_codeblock(
12    code_block: &mut Vec<(SyntaxNode, Expression)>,
13    diagnostics: &mut BuildDiagnostics,
14) {
15    if code_block.len() > 1 {
16        // In a code block, only the last expression returns a value.
17        // Therefore all other expressions inside the block are only useful if they have side
18        // effects.
19        //
20        // Remove all expressions without side effects (except for the last one) and emit a
21        // warning.
22        //
23        // Note: Iterate over the indices in reverse, so that all to-be-iterated indices remain
24        // valid when removing items from the vector.
25        for index in (0..(code_block.len() - 1)).rev() {
26            let (node, expression) = &code_block[index];
27            if without_side_effects(expression) {
28                diagnostics.push_warning("Expression has no effect!".to_owned(), node);
29                code_block.remove(index);
30            }
31        }
32    }
33}
34
35/// Returns whether the expression is certain to be without side effects.
36/// This function is conservative and may still return `false`, even if a given expression
37/// is without side effects.
38/// It is only guaranteed that if this function returns `true`, the expression definitely does not
39/// contain side effects.
40fn without_side_effects(expression: &Expression) -> bool {
41    match expression {
42        Expression::Condition { condition, true_expr, false_expr, .. } => {
43            without_side_effects(condition)
44                && without_side_effects(true_expr)
45                && without_side_effects(false_expr)
46        }
47        Expression::NumberLiteral(_, _) => true,
48        Expression::StringLiteral(_) => true,
49        Expression::BoolLiteral(_) => true,
50        Expression::Keys(_) => true,
51        Expression::CodeBlock(expressions) => expressions.iter().all(without_side_effects),
52        Expression::FunctionParameterReference { .. } => true,
53        // Invalid and uncompiled expressions are unknown at this point, so default to
54        // `false`, because they may have side-effects.
55        Expression::Invalid => false,
56        Expression::Uncompiled(_) => false,
57        // A property reference may cause re-evaluation of a property, which may result in
58        // side effects
59        Expression::PropertyReference(_) => false,
60        Expression::ElementReference(_) => false,
61        Expression::RepeaterIndexReference { .. } => true,
62        Expression::RepeaterModelReference { .. } => true,
63        Expression::StoreLocalVariable { .. } => false,
64        Expression::ReadLocalVariable { .. } => true,
65        Expression::StructFieldAccess { base, name: _ } => without_side_effects(base),
66        Expression::ArrayIndex { array, index } => {
67            without_side_effects(array) && without_side_effects(index)
68        }
69        // Note: This assumes that the cast itself does not have any side effects, which may not be
70        // the case if custom casting rules are implemented.
71        Expression::Cast { from, to: _ } => without_side_effects(from),
72        // Note: Calling a *pure* function is without side effects, however
73        // just from the expression, the purity of the function is not known.
74        // We would need to resolve the function to determine its purity.
75        Expression::FunctionCall { .. } => false,
76        Expression::SelfAssignment { .. } => false,
77        Expression::BinaryExpression { lhs, rhs, .. } => {
78            without_side_effects(lhs) && without_side_effects(rhs)
79        }
80        Expression::UnaryOp { sub, op: _ } => without_side_effects(sub),
81        Expression::ImageReference { .. } => true,
82        Expression::Array { element_ty: _, values } => values.iter().all(without_side_effects),
83        Expression::Struct { ty: _, values } => values.values().all(without_side_effects),
84        Expression::PathData(_) => true,
85        Expression::EasingCurve(_) => true,
86        Expression::MouseCursor(_) => true,
87        Expression::LinearGradient { angle, stops } => {
88            without_side_effects(angle)
89                && stops
90                    .iter()
91                    .all(|(start, end)| without_side_effects(start) && without_side_effects(end))
92        }
93        Expression::RadialGradient { center, radius, stops } => {
94            center
95                .as_ref()
96                .is_none_or(|(cx, cy)| without_side_effects(cx) && without_side_effects(cy))
97                && radius.as_ref().is_none_or(|r| without_side_effects(r))
98                && stops
99                    .iter()
100                    .all(|(start, end)| without_side_effects(start) && without_side_effects(end))
101        }
102        Expression::ConicGradient { from_angle, center, stops } => {
103            without_side_effects(from_angle)
104                && center
105                    .as_ref()
106                    .is_none_or(|(cx, cy)| without_side_effects(cx) && without_side_effects(cy))
107                && stops
108                    .iter()
109                    .all(|(start, end)| without_side_effects(start) && without_side_effects(end))
110        }
111        Expression::EnumerationValue(_) => true,
112        // A return statement is never without side effects, as an important "side effect" is that
113        // the current function stops at this point.
114        Expression::ReturnStatement(_) => false,
115        Expression::LayoutCacheAccess { .. } => false,
116        Expression::GridRepeaterCacheAccess { .. } => false,
117        Expression::ComputeBoxLayoutInfo { .. } => false,
118        Expression::ComputeGridLayoutInfo { .. } => false,
119        Expression::OrganizeGridLayout(_) => false,
120        Expression::SolveBoxLayout(_, _) => false,
121        Expression::SolveGridLayout { .. } => false,
122        Expression::SolveFlexboxLayout(..) => false,
123        Expression::ComputeFlexboxLayoutInfo { .. } => false,
124        Expression::MinMax { ty: _, op: _, lhs, rhs } => {
125            without_side_effects(lhs) && without_side_effects(rhs)
126        }
127        Expression::DebugHook { .. } => false,
128        Expression::EmptyComponentFactory => false,
129        Expression::EmptyDataTransfer => false,
130        // A closure that is not called has no side effect, regardless of what its body does.
131        Expression::Closure { .. } => true,
132    }
133}