diagnostic: try to detect use of range expression and recommand to use number

Several users have been asking if it is possible to use range
expression.
Detect this and have a meaningful error message
This commit is contained in:
Olivier Goffart 2025-04-19 22:47:45 +02:00 committed by GitHub
parent 36bc0d1922
commit b77368f1b6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 51 additions and 1 deletions

View file

@ -55,6 +55,7 @@ enum OperatorPrecedence {
fn parse_expression_helper(p: &mut impl Parser, precedence: OperatorPrecedence) -> bool {
let mut p = p.start_node(SyntaxKind::Expression);
let checkpoint = p.checkpoint();
let mut possible_range = false;
match p.nth(0).kind() {
SyntaxKind::Identifier => {
parse_qualified_name(&mut *p);
@ -66,7 +67,12 @@ fn parse_expression_helper(p: &mut impl Parser, precedence: OperatorPrecedence)
p.consume()
}
}
SyntaxKind::NumberLiteral => p.consume(),
SyntaxKind::NumberLiteral => {
if p.nth(0).as_str().ends_with('.') {
possible_range = true;
}
p.consume()
}
SyntaxKind::ColorLiteral => p.consume(),
SyntaxKind::LParent => {
p.consume();
@ -97,6 +103,12 @@ fn parse_expression_helper(p: &mut impl Parser, precedence: OperatorPrecedence)
}
let mut p = p.start_node_at(checkpoint.clone(), SyntaxKind::MemberAccess);
p.consume(); // '.'
if possible_range && p.peek().kind() == SyntaxKind::NumberLiteral {
let error = format!("Parse error. Range expressions are not supported in Slint. You can use an integer as a model to repeat something multiple time. Eg: `for i in {} : ...`", p.peek().as_str());
p.error(error);
p.consume();
return false;
}
if !p.expect(SyntaxKind::Identifier) {
return false;
}
@ -119,6 +131,7 @@ fn parse_expression_helper(p: &mut impl Parser, precedence: OperatorPrecedence)
}
_ => break,
}
possible_range = false;
}
if precedence >= OperatorPrecedence::Mul {

View file

@ -1583,6 +1583,17 @@ fn maybe_lookup_object(
LookupResult::Expression { expression, .. } => {
let ty_descr = match expression.ty() {
Type::Struct { .. } => String::new(),
Type::Float32
if ctx.property_type == Type::Model
&& matches!(
expression,
Expression::NumberLiteral(_, Unit::None),
) =>
{
// usually something like `0..foo`
format!(" of float. Range expressions are not supported in Slint, but you can use an integer as a model to repeat something multiple time. Eg: `for i in {}`", next.text())
}
ty => format!(" of {ty}"),
};
ctx.diag.push_error(

View file

@ -20,4 +20,8 @@ export SuperSimple := Rectangle {
Rectangle {}
}
for x in 0..32: Rectangle { }
// ^error{Parse error. Range expressions are not supported in Slint. You can use an integer as a model to repeat something multiple time. Eg: `for i in 32 : ...`}
}

View file

@ -0,0 +1,22 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
export component Foo {
in property <int> count;
for x in 0..count: Rectangle { }
// ^error{Cannot access the field 'count' of float. Range expressions are not supported in Slint, but you can use an integer as a model to repeat something multiple time. Eg: `for i in count`}
// In these case, we should not suggest to use a model with a count
property <float> invalid: 0..count;
// ^error{Cannot access the field 'count' of float$}
for x in invalid.count: Rectangle { }
// ^error{Cannot access the field 'count' of float$}
for x in 0.px.count: Rectangle { }
// ^error{Cannot access the field 'count' of length$}
}