From db8e8d88af53d035e0b8dd116c51ddaa69dde765 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 19 Aug 2026 17:04:40 +0200 Subject: [PATCH] slint-sc: don't suggest qualified names that are outside the subset The "Did you mean 'Colors.red'?" hint added for unresolved identifiers suggested named colors and builtin enum values in Slint SC mode, where the qualified forms don't resolve either. This also broke the slint-sc syntax tests, which match the full diagnostic message. Build the suggestions from the same lookup objects that gate the subset, instead of checking named_colors() and the type register directly: enum_or_color_suggestions now takes the LookupCtx and asks BuiltinNamespaceLookup and the enum lookup, so a name is only suggested when its qualified form resolves. User-declared enums are in the subset, so their values are still suggested. --- internal/compiler/lookup.rs | 16 ++++++++------- internal/compiler/passes/resolving.rs | 9 ++++----- .../tests/syntax/slint-sc/enums.slint | 3 +++ tools/lsp/language.rs | 20 ++++++++----------- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/internal/compiler/lookup.rs b/internal/compiler/lookup.rs index 67b709928e1..52dbaf2afd7 100644 --- a/internal/compiler/lookup.rs +++ b/internal/compiler/lookup.rs @@ -721,17 +721,19 @@ impl ColorSpecific { /// `["LayoutAlignment.center", "TextHorizontalAlignment.center"]`. This is the reverse of the /// `ColorSpecific` / enum lookups above, used to build "did you mean" suggestions. The result is /// sorted and deduplicated so it is deterministic. -pub fn enum_or_color_suggestions(type_register: &TypeRegister, name: &str) -> Vec { +pub fn enum_or_color_suggestions(ctx: &LookupCtx, name: &str) -> Vec { let name = crate::parser::normalize_identifier(name); let mut result = Vec::new(); - if named_colors().contains_key(name.as_str()) { + if named_colors().contains_key(name.as_str()) + && BuiltinNamespaceLookup.lookup(ctx, &SmolStr::new_static("Colors")).is_some() + { result.push(smol_str::format_smolstr!("{}.{name}", BuiltinNamespace::Colors)); } - for ty in type_register.all_types().values() { - if let Type::Enumeration(e) = ty { - if e.values.contains(&name) { - result.push(smol_str::format_smolstr!("{}.{name}", e.name)); - } + for ty in ctx.type_register.all_types().values() { + if let Type::Enumeration(e) = ty + && e.lookup(ctx, &name).is_some() + { + result.push(smol_str::format_smolstr!("{}.{name}", e.name)); } } result.sort(); diff --git a/internal/compiler/passes/resolving.rs b/internal/compiler/passes/resolving.rs index c724070c5cc..421b26464b2 100644 --- a/internal/compiler/passes/resolving.rs +++ b/internal/compiler/passes/resolving.rs @@ -2380,11 +2380,10 @@ fn lookup_qualified_name_node( if it.next().is_some() { ctx.diag.push_error(format!("Cannot access id '{}'", first.text()), &node); } else { - let mut parts = - crate::lookup::enum_or_color_suggestions(ctx.type_register, &first_str) - .iter() - .map(|s| format!("'{s}'")) - .collect::>(); + let mut parts = crate::lookup::enum_or_color_suggestions(ctx, &first_str) + .iter() + .map(|s| format!("'{s}'")) + .collect::>(); let hint = match parts.pop() { None => String::new(), Some(last) if parts.is_empty() => format!(". Did you mean {last}?"), diff --git a/internal/compiler/tests/syntax/slint-sc/enums.slint b/internal/compiler/tests/syntax/slint-sc/enums.slint index 7b502d98b15..e72e5b464f5 100644 --- a/internal/compiler/tests/syntax/slint-sc/enums.slint +++ b/internal/compiler/tests/syntax/slint-sc/enums.slint @@ -32,4 +32,7 @@ export component Test inherits Window { in-out property direction; //#sls.enum.value out property fixed: Direction.down; + + out property bare: up; +// > `Colors.red`). Like the import action above, re-derive the lookup error rather // than reading diagnostics, so nothing is offered when the identifier does resolve. use i_slint_compiler::lookup::LookupObject; - let is_lookup_error = util::with_lookup_ctx(document_cache, node.clone(), None, |ctx| { + let suggestions = util::with_lookup_ctx(document_cache, node.clone(), None, |ctx| { let name = i_slint_compiler::parser::normalize_identifier(token.text()); - i_slint_compiler::lookup::global_lookup().lookup(ctx, &name).is_none() + if i_slint_compiler::lookup::global_lookup().lookup(ctx, &name).is_none() { + i_slint_compiler::lookup::enum_or_color_suggestions(ctx, token.text()) + } else { + Vec::new() + } }) - .unwrap_or(true); - if is_lookup_error { - let suggestions = { - let global_tr = document_cache.global_type_registry(); - let tr = document_cache - .get_document_for_source_file(&token.source_file) - .map(|doc| &doc.local_registry) - .unwrap_or(&global_tr); - i_slint_compiler::lookup::enum_or_color_suggestions(tr, token.text()) - }; + .unwrap_or_default(); + if !suggestions.is_empty() { let range = util::text_range_to_lsp_range( &token.source_file, token.text_range(),