Skip to main content

luaur_analysis/functions/
autocomplete_expression_autocomplete_core.rs

1use crate::enums::autocomplete_context::AutocompleteContext;
2use crate::enums::autocomplete_entry_kind::AutocompleteEntryKind;
3use crate::enums::prop_index_type::PropIndexType;
4use crate::enums::type_correct_kind::TypeCorrectKind;
5use crate::functions::autocomplete_if_else_expression::autocomplete_if_else_expression;
6use crate::functions::autocomplete_props_autocomplete_core_alt_b::autocomplete_props as autocomplete_props_seed;
7use crate::functions::autocomplete_string_singleton::autocomplete_string_singleton;
8use crate::functions::check_type_correct_kind::check_type_correct_kind;
9use crate::functions::find_expected_type_at::find_expected_type_at;
10use crate::functions::function_is_expected_at::function_is_expected_at;
11use crate::functions::get_paren_recommendation::get_paren_recommendation;
12use crate::functions::is_being_defined::is_being_defined;
13use crate::functions::is_binding_legal_at_current_position::is_binding_legal_at_current_position;
14use crate::functions::to_string_symbol::to_string_symbol;
15use crate::records::autocomplete_entry::AutocompleteEntry;
16use crate::records::builtin_types::BuiltinTypes;
17use crate::records::internal_error_reporter::InternalErrorReporter;
18use crate::records::module::Module;
19use crate::records::type_arena::TypeArena;
20use crate::type_aliases::autocomplete_entry_map::AutocompleteEntryMap;
21use crate::type_aliases::scope_ptr_type::ScopePtr;
22use luaur_ast::records::ast_expr::AstExpr;
23use luaur_ast::records::ast_expr_function::AstExprFunction;
24use luaur_ast::records::ast_expr_global::AstExprGlobal;
25use luaur_ast::records::ast_expr_index_name::AstExprIndexName;
26use luaur_ast::records::ast_expr_local::AstExprLocal;
27use luaur_ast::records::ast_node::AstNode;
28use luaur_ast::records::position::Position;
29use luaur_ast::rtti::{ast_node_as, ast_node_is};
30use luaur_common::FFlag;
31
32/// C++ `static AutocompleteContext autocompleteExpression(...)`
33/// (AutocompleteCore.cpp:1478-1566).
34pub fn autocomplete_expression(
35    module: &Module,
36    builtin_types: &BuiltinTypes,
37    type_arena: *mut TypeArena,
38    ancestry: &alloc::vec::Vec<*mut AstNode>,
39    scope_at_position: &ScopePtr,
40    position: Position,
41    result: &mut AutocompleteEntryMap,
42) -> AutocompleteContext {
43    luaur_common::macros::luau_assert::LUAU_ASSERT!(!ancestry.is_empty());
44
45    let node: *mut AstNode = ancestry[ancestry.len() - 1];
46
47    if FFlag::DebugLuauMagicVariableNames.get() {
48        let ice = InternalErrorReporter {
49            on_internal_error: None,
50            module_name: alloc::string::String::new(),
51        };
52        let local = unsafe { ast_node_as::<AstExprLocal>(node) };
53        if !local.is_null()
54            && unsafe {
55                (*(*local).local)
56                    .name
57                    .operator_eq_c_char(c"_luau_autocomplete_ice".as_ptr())
58            }
59        {
60            ice.ice_string_location("_luau_autocomplete_ice encountered", unsafe {
61                &(*local).base.base.location
62            });
63        }
64        let global = unsafe { ast_node_as::<AstExprGlobal>(node) };
65        if !global.is_null()
66            && unsafe {
67                (*global)
68                    .name
69                    .operator_eq_c_char(c"_luau_autocomplete_ice".as_ptr())
70            }
71        {
72            ice.ice_string_location("_luau_autocomplete_ice encountered", unsafe {
73                &(*global).base.base.location
74            });
75        }
76    }
77
78    if unsafe { ast_node_is::<AstExprIndexName>(&*node) } {
79        let expr = unsafe { (*node).as_expr_const() as *const AstExpr };
80        if let Some(it) = module.ast_types.find(&expr) {
81            autocomplete_props_seed(
82                module,
83                type_arena,
84                builtin_types,
85                *it,
86                PropIndexType::Point,
87                ancestry,
88                result,
89            );
90        }
91    } else if autocomplete_if_else_expression(
92        node as *const AstNode,
93        &mut ancestry.clone(),
94        position,
95        result,
96    ) {
97        return AutocompleteContext::Keyword;
98    } else if unsafe { ast_node_is::<AstExprFunction>(&*node) } {
99        return AutocompleteContext::Unknown;
100    } else {
101        // This is inefficient. :(
102        let mut scope: Option<ScopePtr> = Some(scope_at_position.clone());
103
104        while let Some(scope_ref) = scope {
105            for (name, binding) in &scope_ref.bindings {
106                if !is_binding_legal_at_current_position(name, binding, position) {
107                    continue;
108                }
109
110                if is_being_defined(ancestry, name) {
111                    continue;
112                }
113
114                let n = unsafe { to_string_symbol(name) };
115                if !result.contains_key(&n) {
116                    let type_correct = check_type_correct_kind(
117                        module,
118                        type_arena,
119                        builtin_types,
120                        node,
121                        position,
122                        binding.type_id,
123                    );
124
125                    result.insert(
126                        n.clone(),
127                        AutocompleteEntry {
128                            kind: AutocompleteEntryKind::Binding,
129                            r#type: Some(binding.type_id),
130                            deprecated: binding.deprecated,
131                            wrong_index_type: false,
132                            type_correct,
133                            containing_extern_type: None,
134                            prop: None,
135                            documentation_symbol: binding.documentation_symbol.clone(),
136                            tags: Default::default(),
137                            parens: get_paren_recommendation(
138                                binding.type_id,
139                                ancestry,
140                                type_correct,
141                            ),
142                            insert_text: None,
143                            indexed_with_self: false,
144                        },
145                    );
146                }
147            }
148
149            scope = scope_ref.parent.clone();
150        }
151
152        let correct_for_nil = check_type_correct_kind(
153            module,
154            type_arena,
155            builtin_types,
156            node,
157            position,
158            builtin_types.nilType,
159        );
160        let correct_for_true = check_type_correct_kind(
161            module,
162            type_arena,
163            builtin_types,
164            node,
165            position,
166            builtin_types.trueType,
167        );
168        let correct_for_false = check_type_correct_kind(
169            module,
170            type_arena,
171            builtin_types,
172            node,
173            position,
174            builtin_types.falseType,
175        );
176        let correct_for_function =
177            if function_is_expected_at(module, node, position).unwrap_or(false) {
178                TypeCorrectKind::Correct
179            } else {
180                TypeCorrectKind::None
181            };
182
183        result.insert(
184            alloc::string::String::from("if"),
185            AutocompleteEntry {
186                kind: AutocompleteEntryKind::Keyword,
187                r#type: None,
188                deprecated: false,
189                wrong_index_type: false,
190                ..Default::default()
191            },
192        );
193        result.insert(
194            alloc::string::String::from("true"),
195            AutocompleteEntry {
196                kind: AutocompleteEntryKind::Keyword,
197                r#type: Some(builtin_types.booleanType),
198                deprecated: false,
199                wrong_index_type: false,
200                type_correct: correct_for_true,
201                ..Default::default()
202            },
203        );
204        result.insert(
205            alloc::string::String::from("false"),
206            AutocompleteEntry {
207                kind: AutocompleteEntryKind::Keyword,
208                r#type: Some(builtin_types.booleanType),
209                deprecated: false,
210                wrong_index_type: false,
211                type_correct: correct_for_false,
212                ..Default::default()
213            },
214        );
215        result.insert(
216            alloc::string::String::from("nil"),
217            AutocompleteEntry {
218                kind: AutocompleteEntryKind::Keyword,
219                r#type: Some(builtin_types.nilType),
220                deprecated: false,
221                wrong_index_type: false,
222                type_correct: correct_for_nil,
223                ..Default::default()
224            },
225        );
226        result.insert(
227            alloc::string::String::from("not"),
228            AutocompleteEntry {
229                kind: AutocompleteEntryKind::Keyword,
230                ..Default::default()
231            },
232        );
233        result.insert(
234            alloc::string::String::from("function"),
235            AutocompleteEntry {
236                kind: AutocompleteEntryKind::Keyword,
237                r#type: None,
238                deprecated: false,
239                wrong_index_type: false,
240                type_correct: correct_for_function,
241                ..Default::default()
242            },
243        );
244
245        if let Some(ty) = find_expected_type_at(module, node, position) {
246            autocomplete_string_singleton(ty, true, node, position, result);
247        }
248    }
249
250    AutocompleteContext::Expression
251}