From a1ce332f6f1f4a0fdadbd208d7cfcf4bc1b3371b Mon Sep 17 00:00:00 2001 From: Cam Swords Date: Tue, 28 May 2024 17:40:50 -0700 Subject: [PATCH] [move][move-ide] Revise how IDE information is recorded in the compiler (#17864) ## Description This PR moves how we record IDE information to communicate it to the Move Analyzer. It is now accrued on the `CompilationEnv` (modulo some care in typing to handle type elaboration) and can be added through methods there. The goal is to set up an extensible way to track IDE information in the future, tying it to location information. This change has also caused us to change how we compute spans for dotted expression terms: previously, we would make the expression with the call's span in order to report failures to autoborrow to point at the call site (instead of just the subject term). This caused recurring through location information in the analyzer to enter an infinite loop in the macro case, as the subject term shared the location of the overall macro call and recursion into the subject term on the macro information looped forever. This diff introduces a solution to this issue by more-carefully computing spans for subject terms so that they remain pointing at the actual subject term, and plumbs the call location through dotted expression handling to retain good error reporting for the method case. It also introduces a very small change to handle location reporting for `*`/`&`/`&mut ` usage for type errors. ## Test plan All tests still pass. --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: --- .../crates/move-analyzer/src/compiler_info.rs | 62 ++++ .../move/crates/move-analyzer/src/lib.rs | 1 + .../move/crates/move-analyzer/src/symbols.rs | 91 ++--- .../src/hlir/detect_dead_code.rs | 7 +- .../move-compiler/src/hlir/translate.rs | 33 +- .../crates/move-compiler/src/naming/ast.rs | 12 - .../src/naming/resolve_use_funs.rs | 1 - .../move-compiler/src/naming/translate.rs | 1 - .../crates/move-compiler/src/shared/ide.rs | 93 +++++ .../crates/move-compiler/src/shared/mod.rs | 22 +- .../crates/move-compiler/src/typing/ast.rs | 60 ---- .../crates/move-compiler/src/typing/core.rs | 28 +- .../src/typing/dependency_ordering.rs | 6 - .../crates/move-compiler/src/typing/expand.rs | 43 ++- .../src/typing/infinite_instantiations.rs | 8 +- .../move-compiler/src/typing/macro_expand.rs | 17 +- .../move-compiler/src/typing/translate.rs | 328 ++++++++---------- .../move-compiler/src/typing/visitor.rs | 6 - .../move_2024/ide_mode/dot_incomplete.exp | 18 - .../move_2024/ide_mode/index_autocomplete.exp | 18 - .../ide_mode/named_struct_autocomplete.exp | 12 - .../positional_struct_autocomplete.exp | 12 - .../ide_mode/struct_method_autocomplete.exp | 12 - .../ide_mode/struct_scoped_autocomplete.exp | 12 - .../typing/let_mut_borrow_mut_dot_call.exp | 6 +- .../move_check/ide_mode/dot_incomplete.exp | 32 -- .../ide_mode/struct_method_autocomplete.exp | 12 - .../typing/constant_unsupported_exps.exp | 6 + .../move/crates/move-ir-types/src/location.rs | 10 + 29 files changed, 450 insertions(+), 519 deletions(-) create mode 100644 external-crates/move/crates/move-analyzer/src/compiler_info.rs create mode 100644 external-crates/move/crates/move-compiler/src/shared/ide.rs diff --git a/external-crates/move/crates/move-analyzer/src/compiler_info.rs b/external-crates/move/crates/move-analyzer/src/compiler_info.rs new file mode 100644 index 0000000000000..5972c8ddd9f01 --- /dev/null +++ b/external-crates/move/crates/move-analyzer/src/compiler_info.rs @@ -0,0 +1,62 @@ +// Copyright (c) The Move Contributors +// SPDX-License-Identifier: Apache-2.0 + +use std::collections::{BTreeMap, BTreeSet}; + +use move_compiler::shared::ide as CI; +use move_ir_types::location::Loc; + +#[derive(Default, Debug, Clone)] +pub struct CompilerInfo { + pub macro_info: BTreeMap, + pub expanded_lambdas: BTreeSet, + pub autocomplete_info: BTreeMap, +} + +impl CompilerInfo { + pub fn new() -> CompilerInfo { + CompilerInfo::default() + } + + pub fn from(info: impl IntoIterator) -> Self { + let mut result = Self::new(); + result.add_info(info); + result + } + + pub fn add_info(&mut self, info: impl IntoIterator) { + for (loc, entry) in info { + match entry { + CI::IDEAnnotation::MacroCallInfo(info) => { + // TODO: should we check this is not also an expanded lambda? + // TODO: what if we find two macro calls? + if let Some(_old) = self.macro_info.insert(loc, *info) { + eprintln!("Repeated macro info"); + } + } + CI::IDEAnnotation::ExpandedLambda => { + self.expanded_lambdas.insert(loc); + } + CI::IDEAnnotation::AutocompleteInfo(info) => { + // TODO: what if we find two autocomplete info sets? Intersection may be better + // than union, as it's likely in a lambda body. + if let Some(_old) = self.autocomplete_info.insert(loc, *info) { + eprintln!("Repeated autocomplete info"); + } + } + } + } + } + + pub fn get_macro_info(&mut self, loc: &Loc) -> Option<&CI::MacroCallInfo> { + self.macro_info.get(loc) + } + + pub fn is_expanded_lambda(&mut self, loc: &Loc) -> bool { + self.expanded_lambdas.contains(loc) + } + + pub fn get_autocomplete_info(&mut self, loc: &Loc) -> Option<&CI::AutocompleteInfo> { + self.autocomplete_info.get(loc) + } +} diff --git a/external-crates/move/crates/move-analyzer/src/lib.rs b/external-crates/move/crates/move-analyzer/src/lib.rs index 15082886df6a0..10dfe88703dd1 100644 --- a/external-crates/move/crates/move-analyzer/src/lib.rs +++ b/external-crates/move/crates/move-analyzer/src/lib.rs @@ -6,6 +6,7 @@ extern crate move_ir_types; pub mod analyzer; +pub mod compiler_info; pub mod completion; pub mod context; pub mod diagnostics; diff --git a/external-crates/move/crates/move-analyzer/src/symbols.rs b/external-crates/move/crates/move-analyzer/src/symbols.rs index c76b2d5416129..e22a83895f2f2 100644 --- a/external-crates/move/crates/move-analyzer/src/symbols.rs +++ b/external-crates/move/crates/move-analyzer/src/symbols.rs @@ -55,6 +55,7 @@ #![allow(clippy::non_canonical_partial_ord_impl)] use crate::{ + compiler_info::CompilerInfo, context::Context, diagnostics::{lsp_diagnostics, lsp_empty_diagnostics}, utils::get_loc, @@ -98,10 +99,13 @@ use move_compiler::{ linters::LintLevel, naming::ast::{StructDefinition, StructFields, TParam, Type, TypeName_, Type_, UseFuns}, parser::ast::{self as P, DatatypeName, FunctionName}, - shared::{unique_map::UniqueMap, Identifier, Name, NamedAddressMap, NamedAddressMaps}, + shared::{ + ide::MacroCallInfo, unique_map::UniqueMap, Identifier, Name, NamedAddressMap, + NamedAddressMaps, + }, typing::ast::{ - BuiltinFunction_, Exp, ExpListItem, Function, FunctionBody_, IDEInfo, LValue, LValueList, - LValue_, MacroCallInfo, ModuleDefinition, SequenceItem, SequenceItem_, UnannotatedExp_, + BuiltinFunction_, Exp, ExpListItem, Function, FunctionBody_, LValue, LValueList, LValue_, + ModuleDefinition, SequenceItem, SequenceItem_, UnannotatedExp_, }, unit_test::filter_test_members::UNIT_TEST_POISON_FUN_NAME, PASS_CFGIR, PASS_PARSER, PASS_TYPING, @@ -390,6 +394,8 @@ pub struct TypingSymbolicator<'a> { /// In some cases (e.g., when processing bodies of macros) we want to keep traversing /// the AST but without recording the actual metadata (uses, definitions, types, etc.) traverse_only: bool, + /// IDE Annotation Information from the Compiler + compiler_info: CompilerInfo, } /// Maps a line number to a list of use-def-s on a given line (use-def set is sorted by col_start) @@ -717,7 +723,6 @@ fn ast_exp_to_ide_string(exp: &Exp) -> Option { .join(", "), ) } - UE::IDEAnnotation(_, exp) => ast_exp_to_ide_string(exp), UE::ExpList(list) => { let items = list .iter() @@ -1194,6 +1199,7 @@ pub fn get_symbols( BuildPlan::create(resolution_graph)?.set_compiler_vfs_root(overlay_fs_root.clone()); let mut parsed_ast = None; let mut typed_ast = None; + let mut compiler_info = None; let mut diagnostics = None; let mut dependencies = build_plan.compute_dependencies(); @@ -1285,13 +1291,16 @@ pub fn get_symbols( let failure = true; diagnostics = Some((diags, failure)); eprintln!("typed AST compilation failed"); + eprintln!("diagnostics: {:#?}", diagnostics); return Ok((files, vec![])); } }; eprintln!("compiled to typed AST"); let (mut compiler, typed_program) = compiler.into_ast(); typed_ast = Some(typed_program.clone()); - + compiler_info = Some(CompilerInfo::from( + compiler.compilation_env().ide_information.clone(), + )); edition = Some(compiler.compilation_env().edition(Some(root_pkg_name))); // compile to CFGIR for accurate diags @@ -1399,7 +1408,6 @@ pub fn get_symbols( &mut mod_to_alias_lengths, ); } - let mut typing_symbolicator = TypingSymbolicator { mod_outer_defs: &mod_outer_defs, files: &files, @@ -1410,6 +1418,7 @@ pub fn get_symbols( use_defs: UseDefMap::new(), alias_lengths: &BTreeMap::new(), traverse_only: false, + compiler_info: compiler_info.unwrap(), }; process_typed_modules( @@ -2764,6 +2773,38 @@ impl<'a> TypingSymbolicator<'a> { /// Get symbols for an expression fn exp_symbols(&mut self, exp: &Exp, scope: &mut OrdMap) { + let expanded_lambda = self.compiler_info.is_expanded_lambda(&exp.exp.loc); + if let Some(macro_call_info) = self.compiler_info.get_macro_info(&exp.exp.loc) { + debug_assert!(!expanded_lambda, "Compiler info issue"); + let MacroCallInfo { + module, + name, + method_name, + type_arguments, + by_value_args, + } = macro_call_info.clone(); + self.mod_call_symbols(&module, name, method_name, &type_arguments, None, scope); + by_value_args + .iter() + .for_each(|a| self.seq_item_symbols(scope, a)); + let old_traverse_mode = self.traverse_only; + // stop adding new use-defs etc. + self.traverse_only = true; + self.exp_symbols_inner(exp, scope); + self.traverse_only = old_traverse_mode; + } else if expanded_lambda { + let old_traverse_mode = self.traverse_only; + // start adding new use-defs etc. when processing a lambda argument + self.traverse_only = false; + self.exp_symbols_inner(exp, scope); + self.traverse_only = old_traverse_mode; + } else { + self.exp_symbols_inner(exp, scope); + } + } + + /// Get symbols for an expression + fn exp_symbols_inner(&mut self, exp: &Exp, scope: &mut OrdMap) { use UnannotatedExp_ as E; match &exp.exp.value { E::Move { from_user: _, var } => { @@ -2840,41 +2881,6 @@ impl<'a> TypingSymbolicator<'a> { self.traverse_only = old_traverse_mode; } } - E::IDEAnnotation(info, exp) => { - match info { - IDEInfo::MacroCallInfo(MacroCallInfo { - module, - name, - method_name, - type_arguments, - by_value_args, - }) => { - self.mod_call_symbols( - module, - *name, - *method_name, - type_arguments, - None, - scope, - ); - by_value_args - .iter() - .for_each(|a| self.seq_item_symbols(scope, a)); - let old_traverse_mode = self.traverse_only; - // stop adding new use-defs etc. - self.traverse_only = true; - self.exp_symbols(exp, scope); - self.traverse_only = old_traverse_mode; - } - IDEInfo::ExpandedLambda => { - let old_traverse_mode = self.traverse_only; - // start adding new use-defs etc. when processing a lambda argument - self.traverse_only = false; - self.exp_symbols(exp, scope); - self.traverse_only = old_traverse_mode; - } - } - } E::Assign(lvalues, opt_types, e) => { self.lvalue_list_symbols(false, lvalues, scope); for opt_t in opt_types { @@ -2932,9 +2938,6 @@ impl<'a> TypingSymbolicator<'a> { self.exp_symbols(exp, scope); self.add_type_id_use_def(t); } - E::AutocompleteDotAccess { base_exp, .. } => { - self.exp_symbols(base_exp, scope); - } E::Unit { .. } | E::Value(_) | E::Continue(_) diff --git a/external-crates/move/crates/move-compiler/src/hlir/detect_dead_code.rs b/external-crates/move/crates/move-compiler/src/hlir/detect_dead_code.rs index 45a2529ed7e0a..b2a74124ccc41 100644 --- a/external-crates/move/crates/move-compiler/src/hlir/detect_dead_code.rs +++ b/external-crates/move/crates/move-compiler/src/hlir/detect_dead_code.rs @@ -360,7 +360,6 @@ fn tail(context: &mut Context, e: &T::Exp) -> Option { } } E::Block((_, seq)) => tail_block(context, seq), - E::IDEAnnotation(_, e) => tail(context, e), // ----------------------------------------------------------------------------------------- // statements @@ -480,7 +479,6 @@ fn value(context: &mut Context, e: &T::Exp) -> Option { } } E::Block((_, seq)) => value_block(context, seq), - E::IDEAnnotation(_, e) => value(context, e), // ----------------------------------------------------------------------------------------- // calls and nested expressions @@ -528,8 +526,7 @@ fn value(context: &mut Context, e: &T::Exp) -> Option { | E::UnaryExp(_, base_exp) | E::Borrow(_, base_exp, _) | E::Cast(base_exp, _) - | E::TempBorrow(_, base_exp) - | E::AutocompleteDotAccess { base_exp, .. } => value_report!(base_exp), + | E::TempBorrow(_, base_exp) => value_report!(base_exp), E::BorrowLocal(_, _) => None, @@ -683,7 +680,6 @@ fn statement(context: &mut Context, e: &T::Exp) -> Option { E::Block((_, seq)) => statement_block( context, seq, /* stmt_pos */ true, /* skip_last */ false, ), - E::IDEAnnotation(_, e) => statement(context, e), E::Return(rhs) => { if let Some(rhs_control_flow) = value(context, rhs) { context.report_value_error(rhs_control_flow); @@ -742,7 +738,6 @@ fn statement(context: &mut Context, e: &T::Exp) -> Option { | E::ErrorConstant { .. } | E::Move { .. } | E::Copy { .. } - | E::AutocompleteDotAccess { .. } | E::UnresolvedError => value(context, e), E::Value(_) | E::Unit { .. } => None, diff --git a/external-crates/move/crates/move-compiler/src/hlir/translate.rs b/external-crates/move/crates/move-compiler/src/hlir/translate.rs index 0960f13cf32bf..25e5a3a864927 100644 --- a/external-crates/move/crates/move-compiler/src/hlir/translate.rs +++ b/external-crates/move/crates/move-compiler/src/hlir/translate.rs @@ -15,12 +15,7 @@ use crate::{ parser::ast::{ Ability_, BinOp, BinOp_, ConstantName, DatatypeName, Field, FunctionName, VariantName, }, - shared::{ - process_binops, - string_utils::{debug_print, format_oxford_list}, - unique_map::UniqueMap, - *, - }, + shared::{process_binops, string_utils::debug_print, unique_map::UniqueMap, *}, sui_mode::ID_FIELD_NAME, typing::ast as T, FullyCompiledProgram, @@ -1044,7 +1039,6 @@ fn tail( }) } E::Block((_, seq)) => tail_block(context, block, expected_type, seq), - E::IDEAnnotation(_, e) => tail(context, block, expected_type, *e), // ----------------------------------------------------------------------------------------- // statements that need to be hoisted out @@ -1353,7 +1347,6 @@ fn value( bound_exp } E::Block((_, seq)) => value_block(context, block, Some(&out_type), eloc, seq), - E::IDEAnnotation(_, e) => value(context, block, expected_type, *e), // ----------------------------------------------------------------------------------------- // calls @@ -1692,28 +1685,6 @@ fn value( assert!(context.env.has_errors()); make_exp(HE::UnresolvedError) } - E::AutocompleteDotAccess { - base_exp: _, - methods, - fields, - } => { - if !(context.env.ide_mode()) { - context - .env - .add_diag(ice!((eloc, "Found autocomplete outside of IDE mode"))); - }; - let names = methods - .into_iter() - .map(|(m, f)| format!("{m}::{f}")) - .chain(fields.into_iter().map(|n| format!("{n}"))) - .collect::>(); - let msg = format!( - "Autocompletes to: {}", - format_oxford_list!("or", "'{}'", names) - ); - context.env.add_diag(diag!(IDE::Autocomplete, (eloc, msg))); - make_exp(HE::UnresolvedError) - } }; maybe_freeze(context, block, expected_type.cloned(), preresult) } @@ -1977,7 +1948,6 @@ fn statement(context: &mut Context, block: &mut Block, e: T::Exp) { } } E::Block((_, seq)) => statement_block(context, block, seq), - E::IDEAnnotation(_, e) => statement(context, block, *e), E::Return(rhs) => { let expected_type = context.signature.as_ref().map(|s| s.return_type.clone()); let exp = value(context, block, expected_type.as_ref(), *rhs); @@ -2055,7 +2025,6 @@ fn statement(context: &mut Context, block: &mut Block, e: T::Exp) { | E::Move { .. } | E::Copy { .. } | E::UnresolvedError - | E::AutocompleteDotAccess { .. } | E::NamedBlock(_, _)) => value_statement(context, block, make_exp(e_)), E::Value(_) | E::Unit { .. } => (), diff --git a/external-crates/move/crates/move-compiler/src/naming/ast.rs b/external-crates/move/crates/move-compiler/src/naming/ast.rs index 62a93bba0684e..0c7aae235a23a 100644 --- a/external-crates/move/crates/move-compiler/src/naming/ast.rs +++ b/external-crates/move/crates/move-compiler/src/naming/ast.rs @@ -397,11 +397,6 @@ pub enum MacroArgument { Substituted(Loc), } -#[derive(Debug, PartialEq, Clone)] -pub enum IDEInfo { - ExpandedLambda, -} - #[derive(Debug, PartialEq, Clone)] #[allow(clippy::large_enum_variant)] pub enum Exp_ { @@ -432,7 +427,6 @@ pub enum Exp_ { While(BlockLabel, Box, Box), Loop(BlockLabel, Box), Block(Block), - IDEAnnotation(IDEInfo, Box), Lambda(Lambda), Assign(LValueList, Box), @@ -1737,12 +1731,6 @@ impl AstDebug for Exp_ { e.ast_debug(w); } E::Block(seq) => seq.ast_debug(w), - E::IDEAnnotation(info, e) => match info { - IDEInfo::ExpandedLambda => { - w.write("ExpandedLambda:"); - e.ast_debug(w); - } - }, E::Lambda(l) => l.ast_debug(w), E::ExpList(es) => { w.write("("); diff --git a/external-crates/move/crates/move-compiler/src/naming/resolve_use_funs.rs b/external-crates/move/crates/move-compiler/src/naming/resolve_use_funs.rs index 12c86be4abb0d..2248154e6fcdf 100644 --- a/external-crates/move/crates/move-compiler/src/naming/resolve_use_funs.rs +++ b/external-crates/move/crates/move-compiler/src/naming/resolve_use_funs.rs @@ -358,7 +358,6 @@ fn exp(context: &mut Context, sp!(_, e_): &mut N::Exp) { from_macro_argument: _, seq, }) => sequence(context, seq), - N::Exp_::IDEAnnotation(_, e) => exp(context, e), N::Exp_::FieldMutate(ed, e) => { exp_dotted(context, ed); exp(context, e) diff --git a/external-crates/move/crates/move-compiler/src/naming/translate.rs b/external-crates/move/crates/move-compiler/src/naming/translate.rs index d9dda7a6af655..1ce2fc283609c 100644 --- a/external-crates/move/crates/move-compiler/src/naming/translate.rs +++ b/external-crates/move/crates/move-compiler/src/naming/translate.rs @@ -3785,7 +3785,6 @@ fn remove_unused_bindings_exp( from_macro_argument: _, seq, }) => remove_unused_bindings_seq(context, used, seq), - N::Exp_::IDEAnnotation(_, e) => remove_unused_bindings_exp(context, used, e), N::Exp_::Lambda(N::Lambda { parameters: sp!(_, parameters), return_label: _, diff --git a/external-crates/move/crates/move-compiler/src/shared/ide.rs b/external-crates/move/crates/move-compiler/src/shared/ide.rs new file mode 100644 index 0000000000000..123801bbbc7a6 --- /dev/null +++ b/external-crates/move/crates/move-compiler/src/shared/ide.rs @@ -0,0 +1,93 @@ +// Copyright (c) The Move Contributors +// SPDX-License-Identifier: Apache-2.0 + +use std::collections::BTreeSet; + +use crate::{ + expansion::ast as E, naming::ast as N, parser::ast as P, shared::Name, typing::ast as T, +}; + +use move_ir_types::location::Loc; +use move_symbol_pool::Symbol; + +//************************************************************************************************* +// Types +//************************************************************************************************* + +#[derive(Debug, Clone, Default)] +pub struct IDEInfo { + annotations: Vec<(Loc, IDEAnnotation)>, +} + +#[derive(Debug, Clone)] +/// An individual IDE annotation. +pub enum IDEAnnotation { + /// A macro call site. + MacroCallInfo(Box), + /// An expanded lambda site. + ExpandedLambda, + /// Autocomplete information. + AutocompleteInfo(Box), +} + +#[derive(Debug, Clone)] +pub struct MacroCallInfo { + /// Module where the macro is defined + pub module: E::ModuleIdent, + /// Name of the macro function + pub name: P::FunctionName, + /// Optional method name if macro invoked as dot-call + pub method_name: Option, + /// Type params at macro's call site + pub type_arguments: Vec, + /// By-value args (at this point there should only be one, representing receiver arg) + pub by_value_args: Vec, +} + +#[derive(Debug, Clone)] +pub struct AutocompleteInfo { + /// Methods that are valid autocompletes + pub methods: BTreeSet<(E::ModuleIdent, P::FunctionName)>, + /// Fields that are valid autocompletes (e.g., for a struct) + /// TODO: possibly extend this with type information? + pub fields: BTreeSet, +} + +//************************************************************************************************* +// Impls +//************************************************************************************************* + +impl IDEInfo { + pub fn new() -> Self { + Self::default() + } + + pub fn add_ide_annotation(&mut self, loc: Loc, info: IDEAnnotation) { + self.annotations.push((loc, info)); + } + + pub fn extend(&mut self, mut other: Self) { + self.annotations.append(&mut other.annotations); + } + + pub fn is_empty(&self) -> bool { + self.annotations.is_empty() + } + + pub fn iter(&self) -> std::slice::Iter<'_, (Loc, IDEAnnotation)> { + self.annotations.iter() + } + + pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, (Loc, IDEAnnotation)> { + self.annotations.iter_mut() + } +} + +impl IntoIterator for IDEInfo { + type Item = (Loc, IDEAnnotation); + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.annotations.into_iter() + } +} diff --git a/external-crates/move/crates/move-compiler/src/shared/mod.rs b/external-crates/move/crates/move-compiler/src/shared/mod.rs index 02c183e75fa84..994d7da4ae705 100644 --- a/external-crates/move/crates/move-compiler/src/shared/mod.rs +++ b/external-crates/move/crates/move-compiler/src/shared/mod.rs @@ -20,6 +20,7 @@ use crate::{ hlir::ast as H, naming::ast as N, parser::ast as P, + shared::ide::{IDEAnnotation, IDEInfo}, sui_mode, typing::{ ast as T, @@ -45,6 +46,7 @@ use std::{ use vfs::{VfsError, VfsPath}; pub mod ast_debug; +pub mod ide; pub mod known_attributes; pub mod program_info; pub mod remembering_unique_map; @@ -245,6 +247,7 @@ pub struct CompilationEnv { // pub counter: u64, mapped_files: MappedFiles, save_hooks: Vec, + pub ide_information: IDEInfo, } macro_rules! known_code_filter { @@ -367,6 +370,7 @@ impl CompilationEnv { prim_definers: BTreeMap::new(), mapped_files: MappedFiles::empty(), save_hooks, + ide_information: IDEInfo::new(), } } @@ -608,10 +612,6 @@ impl CompilationEnv { self.prim_definers.get(&t) } - pub fn ide_mode(&self) -> bool { - self.flags.ide_mode() - } - pub fn save_parser_ast(&self, ast: &P::Program) { for hook in &self.save_hooks { hook.save_parser_ast(ast) @@ -653,6 +653,20 @@ impl CompilationEnv { hook.save_cfgir_ast(ast) } } + + // -- IDE Information -- + + pub fn ide_mode(&self) -> bool { + self.flags.ide_mode() + } + + pub fn extend_ide_info(&mut self, info: IDEInfo) { + self.ide_information.extend(info); + } + + pub fn add_ide_annotation(&mut self, loc: Loc, info: IDEAnnotation) { + self.ide_information.add_ide_annotation(loc, info); + } } pub fn format_allow_attr(attr_name: FilterPrefix, filter: FilterName) -> String { diff --git a/external-crates/move/crates/move-compiler/src/typing/ast.rs b/external-crates/move/crates/move-compiler/src/typing/ast.rs index a2a6010c91570..6849e52110792 100644 --- a/external-crates/move/crates/move-compiler/src/typing/ast.rs +++ b/external-crates/move/crates/move-compiler/src/typing/ast.rs @@ -169,26 +169,6 @@ pub enum BuiltinFunction_ { } pub type BuiltinFunction = Spanned; -#[derive(Debug, PartialEq, Clone)] -pub enum IDEInfo { - MacroCallInfo(MacroCallInfo), - ExpandedLambda, -} - -#[derive(Debug, PartialEq, Clone)] -pub struct MacroCallInfo { - /// Module where the macro is defined - pub module: ModuleIdent, - /// Name of the macro function - pub name: FunctionName, - /// Optional method name if macro invoked as dot-call - pub method_name: Option, - /// Type params at macro's call site - pub type_arguments: Vec, - /// By-value args (at this point there should only be one, representing receiver arg) - pub by_value_args: Vec, -} - #[derive(Debug, PartialEq, Clone)] pub enum UnannotatedExp_ { Unit { @@ -225,7 +205,6 @@ pub enum UnannotatedExp_ { }, NamedBlock(BlockLabel, Sequence), Block(Sequence), - IDEAnnotation(IDEInfo, Box), Assign(LValueList, Vec>, Box), Mutate(Box, Box), Return(Box), @@ -253,14 +232,6 @@ pub enum UnannotatedExp_ { Cast(Box, Box), Annotate(Box, Box), - - // unfinished dot access (e.g. `some_field.`) with autocomplete information on it. - AutocompleteDotAccess { - base_exp: Box, - methods: BTreeSet<(ModuleIdent, FunctionName)>, - fields: BTreeSet, - }, - ErrorConstant { line_number_loc: Loc, error_constant: Option, @@ -756,22 +727,6 @@ impl AstDebug for UnannotatedExp_ { seq.ast_debug(w) } E::Block(seq) => seq.ast_debug(w), - E::IDEAnnotation(info, e) => match info { - IDEInfo::MacroCallInfo(i) => { - w.write(format!("{}::{}", i.module, i.name)); - if !i.type_arguments.is_empty() { - w.write("<"); - w.comma(&i.type_arguments, |w, t| t.ast_debug(w)); - w.write(">"); - } - w.write("()"); - e.ast_debug(w); - } - IDEInfo::ExpandedLambda => { - w.write("ExpandedLambda:"); - e.ast_debug(w); - } - }, E::ExpList(es) => { w.write("("); w.comma(es, |w, e| e.ast_debug(w)); @@ -865,21 +820,6 @@ impl AstDebug for UnannotatedExp_ { ty.ast_debug(w); w.write(")"); } - E::AutocompleteDotAccess { - base_exp, - methods, - fields, - } => { - base_exp.ast_debug(w); - w.write(".{"); - let names = methods - .iter() - .map(|(m, f)| format!("{m}::{f}")) - .chain(fields.iter().map(|n| format!("{n}"))) - .collect::>(); - w.comma(names, |w, n| w.write(n)); - w.write("}"); - } E::UnresolvedError => w.write("_|_"), E::ErrorConstant { line_number_loc: _, diff --git a/external-crates/move/crates/move-compiler/src/typing/core.rs b/external-crates/move/crates/move-compiler/src/typing/core.rs index 3ee5756f441b1..504bd6b46b097 100644 --- a/external-crates/move/crates/move-compiler/src/typing/core.rs +++ b/external-crates/move/crates/move-compiler/src/typing/core.rs @@ -21,8 +21,12 @@ use crate::{ Ability_, ConstantName, DatatypeName, Field, FunctionName, VariantName, ENTRY_MODIFIER, }, shared::{ - known_attributes::TestingAttribute, program_info::*, string_utils::debug_print, - unique_map::UniqueMap, *, + ide::{IDEAnnotation, IDEInfo}, + known_attributes::TestingAttribute, + program_info::*, + string_utils::debug_print, + unique_map::UniqueMap, + *, }, typing::match_compilation, FullyCompiledProgram, @@ -84,6 +88,7 @@ pub(super) struct TypingDebugFlags { pub(super) match_constant_conversion: bool, pub(super) autocomplete_resolution: bool, pub(super) function_translation: bool, + pub(super) type_elaboration: bool, } pub struct Context<'env> { @@ -123,6 +128,9 @@ pub struct Context<'env> { /// This is to prevent accidentally thinking we are in a recursive call if a macro is used /// inside a lambda body pub lambda_expansion: Vec>, + /// IDE Info for the current module member. We hold onto this during typing so we can elaborate + /// it at the end. + pub ide_info: IDEInfo, } pub struct ResolvedFunctionType { @@ -185,6 +193,7 @@ impl<'env> Context<'env> { match_constant_conversion: false, autocomplete_resolution: false, function_translation: false, + type_elaboration: false, }; Context { use_funs: vec![global_use_funs], @@ -207,6 +216,7 @@ impl<'env> Context<'env> { used_module_members: BTreeMap::new(), macro_expansion: vec![], lambda_expansion: vec![], + ide_info: IDEInfo::new(), } } @@ -474,7 +484,7 @@ impl<'env> Context<'env> { self.use_funs.last().unwrap().color.unwrap() } - pub fn reset_for_module_item(&mut self) { + pub fn reset_for_module_item(&mut self, loc: Loc) { self.named_block_map = BTreeMap::new(); self.return_type = None; self.locals = UniqueMap::new(); @@ -485,6 +495,12 @@ impl<'env> Context<'env> { self.max_variable_color = RefCell::new(0); self.macro_expansion = vec![]; self.lambda_expansion = vec![]; + + if !self.ide_info.is_empty() { + self.env + .add_diag(ice!((loc, "IDE info should be cleared after each item"))); + self.ide_info = IDEInfo::new(); + } } pub fn error_type(&mut self, loc: Loc) -> Type { @@ -864,7 +880,7 @@ impl<'env> Context<'env> { } //******************************************** - // IDE Helpers + // IDE Information //******************************************** /// Find all valid methods in scope for a given `TypeName`. This is used for autocomplete. @@ -923,6 +939,10 @@ impl<'env> Context<'env> { debug_print!(self.debug.autocomplete_resolution, (lines "fields" => &fields; dbg)); fields } + + pub fn add_ide_info(&mut self, loc: Loc, info: IDEAnnotation) { + self.ide_info.add_ide_annotation(loc, info); + } } //************************************************************************************************** diff --git a/external-crates/move/crates/move-compiler/src/typing/dependency_ordering.rs b/external-crates/move/crates/move-compiler/src/typing/dependency_ordering.rs index af5fc7f769e43..fcb9d302ba8dc 100644 --- a/external-crates/move/crates/move-compiler/src/typing/dependency_ordering.rs +++ b/external-crates/move/crates/move-compiler/src/typing/dependency_ordering.rs @@ -419,7 +419,6 @@ fn exp(context: &mut Context, e: &T::Exp) { E::Loop { body, .. } => exp(context, body), E::NamedBlock(_, seq) => sequence(context, seq), E::Block(seq) => sequence(context, seq), - E::IDEAnnotation(_, e) => exp(context, e), E::Assign(sp!(_, lvs_), ty_opts, e) => { lvalues(context, lvs_); for ty_opt in ty_opts { @@ -472,11 +471,6 @@ fn exp(context: &mut Context, e: &T::Exp) { exp(context, e); type_(context, ty) } - E::AutocompleteDotAccess { - base_exp, - methods: _, - fields: _, - } => exp(context, base_exp), E::Unit { .. } | E::Value(_) | E::Move { .. } diff --git a/external-crates/move/crates/move-compiler/src/typing/expand.rs b/external-crates/move/crates/move-compiler/src/typing/expand.rs index 7507e55b7629c..1e1c63d198529 100644 --- a/external-crates/move/crates/move-compiler/src/typing/expand.rs +++ b/external-crates/move/crates/move-compiler/src/typing/expand.rs @@ -9,8 +9,9 @@ use crate::{ ice, naming::ast::{BuiltinTypeName_, FunctionSignature, Type, TypeName_, Type_}, parser::ast::Ability_, + shared::{ide::IDEAnnotation, string_utils::debug_print, AstDebug}, typing::{ - ast::{self as T, IDEInfo}, + ast::{self as T}, core::{self, Context}, }, }; @@ -58,8 +59,10 @@ pub fn type_(context: &mut Context, ty: &mut Type) { Anything | UnresolvedError | Param(_) | Unit => (), Ref(_, b) => type_(context, b), Var(tvar) => { + debug_print!(context.debug.type_elaboration, ("before" => Var(*tvar))); let ty_tvar = sp(ty.loc, Var(*tvar)); let replacement = core::unfold_type(&context.subst, ty_tvar); + debug_print!(context.debug.type_elaboration, ("resolved" => replacement)); let replacement = match replacement { sp!(loc, Var(_)) => { let diag = ice!(( @@ -85,6 +88,7 @@ pub fn type_(context: &mut Context, ty: &mut Type) { }; *ty = replacement; type_(context, ty); + debug_print!(context.debug.type_elaboration, ("after" => ty)); } Apply(Some(_), sp!(_, TypeName_::Builtin(_)), tys) => types(context, tys), aty @ Apply(Some(_), _, _) => { @@ -258,17 +262,6 @@ pub fn exp(context: &mut Context, e: &mut T::Exp) { E::Loop { body: eloop, .. } => exp(context, eloop), E::NamedBlock(_, seq) => sequence(context, seq), E::Block(seq) => sequence(context, seq), - E::IDEAnnotation(info, er) => { - exp(context, er); - match info { - IDEInfo::MacroCallInfo(i) => { - for t in i.type_arguments.iter_mut() { - type_(context, t); - } - } - IDEInfo::ExpandedLambda => (), - } - } E::Assign(assigns, tys, er) => { lvalues(context, assigns); expected_types(context, tys); @@ -281,12 +274,7 @@ pub fn exp(context: &mut Context, e: &mut T::Exp) { | E::Dereference(base_exp) | E::UnaryExp(_, base_exp) | E::Borrow(_, base_exp, _) - | E::TempBorrow(_, base_exp) - | E::AutocompleteDotAccess { - base_exp, - methods: _, - fields: _, - } => exp(context, base_exp), + | E::TempBorrow(_, base_exp) => exp(context, base_exp), E::Mutate(el, er) => { exp(context, el); exp(context, er) @@ -520,3 +508,22 @@ fn exp_list_item(context: &mut Context, item: &mut T::ExpListItem) { } } } + +//************************************************************************************************** +// IDE Information +//************************************************************************************************** + +pub fn ide_annotation(context: &mut Context, annotation: &mut IDEAnnotation) { + match annotation { + IDEAnnotation::MacroCallInfo(info) => { + for t in info.type_arguments.iter_mut() { + type_(context, t); + } + for t in info.by_value_args.iter_mut() { + sequence_item(context, t); + } + } + IDEAnnotation::ExpandedLambda => (), + IDEAnnotation::AutocompleteInfo(_) => (), + } +} diff --git a/external-crates/move/crates/move-compiler/src/typing/infinite_instantiations.rs b/external-crates/move/crates/move-compiler/src/typing/infinite_instantiations.rs index c01b9201fb799..03e98d4e657cb 100644 --- a/external-crates/move/crates/move-compiler/src/typing/infinite_instantiations.rs +++ b/external-crates/move/crates/move-compiler/src/typing/infinite_instantiations.rs @@ -266,7 +266,6 @@ fn exp(context: &mut Context, e: &T::Exp) { E::Loop { body: eloop, .. } => exp(context, eloop), E::NamedBlock(_, seq) => sequence(context, seq), E::Block(seq) => sequence(context, seq), - E::IDEAnnotation(_, er) => exp(context, er), E::Assign(_, _, er) => exp(context, er), E::Builtin(_, base_exp) @@ -277,12 +276,7 @@ fn exp(context: &mut Context, e: &T::Exp) { | E::Dereference(base_exp) | E::UnaryExp(_, base_exp) | E::Borrow(_, base_exp, _) - | E::TempBorrow(_, base_exp) - | E::AutocompleteDotAccess { - base_exp, - methods: _, - fields: _, - } => exp(context, base_exp), + | E::TempBorrow(_, base_exp) => exp(context, base_exp), E::Mutate(el, er) | E::BinopExp(el, _, _, er) => { exp(context, el); exp(context, er) diff --git a/external-crates/move/crates/move-compiler/src/typing/macro_expand.rs b/external-crates/move/crates/move-compiler/src/typing/macro_expand.rs index fab304da9ce11..4423b7de8399e 100644 --- a/external-crates/move/crates/move-compiler/src/typing/macro_expand.rs +++ b/external-crates/move/crates/move-compiler/src/typing/macro_expand.rs @@ -10,7 +10,7 @@ use crate::{ self as N, BlockLabel, Color, MatchArm_, TParamID, Type, Type_, UseFuns, Var, Var_, }, parser::ast::FunctionName, - shared::{program_info::FunctionInfo, unique_map::UniqueMap}, + shared::{ide::IDEAnnotation, program_info::FunctionInfo, unique_map::UniqueMap}, typing::{ ast as T, core::{self, TParamSubst}, @@ -44,6 +44,7 @@ pub struct ExpandedMacro { pub body: Box, } +#[derive(Debug)] pub enum EvalStrategy { ByValue(ByValue), ByName(ByName), @@ -613,7 +614,6 @@ fn recolor_exp(ctx: &mut Recolor, sp!(_, e_): &mut N::Exp) { } recolor_seq(ctx, s); } - N::Exp_::IDEAnnotation(_, e) => recolor_exp(ctx, e), N::Exp_::FieldMutate(ed, e) => { recolor_exp_dotted(ctx, ed); recolor_exp(ctx, e) @@ -898,7 +898,6 @@ fn exp(context: &mut Context, sp!(eloc, e_): &mut N::Exp) { from_macro_argument: _, seq: s, }) => seq(context, s), - N::Exp_::IDEAnnotation(_, e) => exp(context, e), N::Exp_::FieldMutate(ed, e) => { exp_dotted(context, ed); exp(context, e) @@ -1056,11 +1055,13 @@ fn exp(context: &mut Context, sp!(eloc, e_): &mut N::Exp) { from_macro_argument: None, seq: (N::UseFuns::new(context.macro_color), result), }); - *e_ = if context.core.env.ide_mode() { - N::Exp_::IDEAnnotation(N::IDEInfo::ExpandedLambda, Box::new(sp(*eloc, block))) - } else { - block - }; + if context.core.env.ide_mode() { + context + .core + .env + .add_ide_annotation(*eloc, IDEAnnotation::ExpandedLambda); + } + *e_ = block; } /////// diff --git a/external-crates/move/crates/move-compiler/src/typing/translate.rs b/external-crates/move/crates/move-compiler/src/typing/translate.rs index 72acf573b154f..b680d8c9a98ba 100644 --- a/external-crates/move/crates/move-compiler/src/typing/translate.rs +++ b/external-crates/move/crates/move-compiler/src/typing/translate.rs @@ -20,6 +20,7 @@ use crate::{ VariantName, }, shared::{ + ide::{AutocompleteInfo, IDEAnnotation, MacroCallInfo}, known_attributes::{SyntaxAttribute, TestingAttribute}, process_binops, program_info::{ConstantInfo, DatatypeKind, TypingProgramInfo}, @@ -29,7 +30,7 @@ use crate::{ }, sui_mode, typing::{ - ast::{self as T, IDEInfo, MacroCallInfo}, + ast::{self as T}, core::{ self, public_testing_visibility, Context, PublicForTesting, ResolvedFunctionType, Subst, }, @@ -207,7 +208,7 @@ fn module( context.add_use_funs_scope(use_funs); structs .iter_mut() - .for_each(|(_, _, s)| struct_def(context, s)); + .for_each(|(loc, _name, s)| struct_def(context, loc, s)); enums.iter_mut().for_each(|(_, _, e)| enum_def(context, e)); process_attributes(context, &attributes); let constants = nconstants.map(|name, c| constant(context, name, c)); @@ -238,6 +239,18 @@ fn module( (typed_module, new_friends) } +fn finalize_ide_info(context: &mut Context) { + if !context.env.ide_mode() { + assert!(context.ide_info.is_empty()); + return; + } + let mut info = std::mem::take(&mut context.ide_info); + for (_loc, ann) in info.iter_mut() { + expand::ide_annotation(context, ann); + } + context.env.extend_ide_info(info); +} + //************************************************************************************************** // Functions //************************************************************************************************** @@ -255,7 +268,7 @@ fn function(context: &mut Context, name: FunctionName, f: N::Function) -> T::Fun } = f; context.env.add_warning_filter_scope(warning_filter.clone()); assert!(context.constraints.is_empty()); - context.reset_for_module_item(); + context.reset_for_module_item(name.loc()); context.current_function = Some(name); context.in_macro_function = macro_.is_some(); process_attributes(context, &attributes); @@ -271,6 +284,7 @@ fn function(context: &mut Context, name: FunctionName, f: N::Function) -> T::Fun } else { function_body(context, n_body) }; + finalize_ide_info(context); context.current_function = None; context.in_macro_function = false; context.env.pop_warning_filter_scope(); @@ -344,9 +358,9 @@ fn function_body(context: &mut Context, sp!(loc, nb_): N::FunctionBody) -> T::Fu // Constants //************************************************************************************************** -fn constant(context: &mut Context, _name: ConstantName, nconstant: N::Constant) -> T::Constant { +fn constant(context: &mut Context, name: ConstantName, nconstant: N::Constant) -> T::Constant { assert!(context.constraints.is_empty()); - context.reset_for_module_item(); + context.reset_for_module_item(name.loc()); let N::Constant { warning_filter, @@ -385,6 +399,9 @@ fn constant(context: &mut Context, _name: ConstantName, nconstant: N::Constant) expand::exp(context, &mut value); check_valid_constant::exp(context, &value); + if context.env.ide_mode() { + finalize_ide_info(context); + } context.env.pop_warning_filter_scope(); T::Constant { @@ -490,10 +507,6 @@ mod check_valid_constant { sequence(context, seq); return; } - E::IDEAnnotation(_, er) => { - exp(context, er); - return; - } E::UnaryExp(_, er) => { exp(context, er); return; @@ -530,14 +543,6 @@ mod check_valid_constant { exp(context, &call.arguments); "Module calls are" } - E::AutocompleteDotAccess { - base_exp, - methods: _, - fields: _, - } => { - exp(context, base_exp); - "Partial dot access paths are" - } E::Builtin(b, args) => { exp(context, args); s = format!("'{}' is", b); @@ -670,9 +675,9 @@ mod check_valid_constant { // Data Types //************************************************************************************************** -fn struct_def(context: &mut Context, s: &mut N::StructDefinition) { +fn struct_def(context: &mut Context, sloc: Loc, s: &mut N::StructDefinition) { assert!(context.constraints.is_empty()); - context.reset_for_module_item(); + context.reset_for_module_item(sloc); context .env .add_warning_filter_scope(s.warning_filter.clone()); @@ -732,8 +737,9 @@ fn enum_def(context: &mut Context, enum_: &mut N::EnumDefinition) { let enum_type_params = &enum_.type_parameters; let mut field_types = vec![]; - for (_, _, variant) in enum_.variants.iter_mut() { - let mut varient_fields = variant_def(context, enum_abilities, enum_type_params, variant); + for (vloc, _, variant) in enum_.variants.iter_mut() { + let mut varient_fields = + variant_def(context, vloc, enum_abilities, enum_type_params, variant); field_types.append(&mut varient_fields); } @@ -743,11 +749,12 @@ fn enum_def(context: &mut Context, enum_: &mut N::EnumDefinition) { fn variant_def( context: &mut Context, + vloc: Loc, enum_abilities: &AbilitySet, enum_tparams: &[DatatypeTypeParameter], v: &mut N::VariantDefinition, ) -> Vec<(usize, Type)> { - context.reset_for_module_item(); + context.reset_for_module_item(vloc); let field_map = match &mut v.fields { N::VariantFields::Empty => return vec![], @@ -1633,13 +1640,6 @@ fn exp(context: &mut Context, ne: Box) -> Box { context.maybe_exit_macro_argument(eloc, from_macro_argument); res } - NE::IDEAnnotation(info, e) => { - let new_info = match info { - N::IDEInfo::ExpandedLambda => IDEInfo::ExpandedLambda, - }; - let new_exp = exp(context, e); - (new_exp.ty.clone(), TE::IDEAnnotation(new_info, new_exp)) - } NE::Lambda(_) => { if context .env @@ -1839,7 +1839,14 @@ fn exp(context: &mut Context, ne: Box) -> Box { } NE::ExpDotted(usage, edotted) => { - let out_exp = *exp_dotted_usage(context, usage, eloc, edotted); + let mut out_exp = *exp_dotted_usage(context, usage, eloc, edotted); + // If the type location would point to this expression (i.e., not to a struct field or + // similar), we watnt to respan it to blame the overall term for a type mismatch + // (because it likely has a `&`, `*`, or `&mut `). If the type is from elsewhere, + // however, we prefer that location. + if eloc.contains(&out_exp.ty.loc) { + out_exp.ty.loc = eloc; + } (out_exp.ty, out_exp.exp.value) } @@ -3070,8 +3077,9 @@ struct ExpDotted { // if a constant appears plainly in a `use`/`copy` position, and suppresses constant usage // warning if so. warn_on_constant: bool, - // This should only be used in IDE mode, and should only occur on the outer-most parse form. - for_autocomplete: bool, + // This should only be used in IDE mode, used to drive autocompletion reporting in a situation + // like `x.foo.{CURSOR}`. + autocomplete_last: Option, } impl ExpDotted { @@ -3129,7 +3137,7 @@ fn process_exp_dotted( base_type, accessors, warn_on_constant: true, - for_autocomplete: false, + autocomplete_last: None, } } @@ -3165,58 +3173,6 @@ fn process_exp_dotted( } } - /// Process a dotted expression with autocomplete enabled. Note that this bails after the - /// innermost autocomplete, and that is the one that will receive any suggestions. - #[growing_stack] - fn process_exp_dotted_autocomplete( - context: &mut Context, - constraint_verb: Option<&str>, - sp!(dloc, ndot_): N::ExpDotted, - ) -> ExpDotted { - match ndot_ { - N::ExpDotted_::Exp(e) => process_base_exp(context, constraint_verb, dloc, e), - N::ExpDotted_::Dot(ndot, field) => { - let mut inner = process_exp_dotted_autocomplete(context, Some("dot access"), *ndot); - if inner.for_autocomplete { - return inner; - } - let inner_ty = inner.last_type(); - let field_type = resolve_field(context, dloc, inner_ty, &field); - inner.loc = dloc; - debug_print!(context.debug.autocomplete_resolution, ("field type" => field_type)); - if matches!(&field_type.value, Type_::UnresolvedError) { - // If we failed to resolve a field, mark this as for_autocomplete to provide - // suggestions at the error location. - inner.for_autocomplete = true; - } else { - inner - .accessors - .push(ExpDottedAccess::Field(field, field_type)); - } - inner - } - N::ExpDotted_::Index(ndot, sp!(argloc, nargs_)) => { - let mut inner = process_exp_dotted_autocomplete(context, Some("dot access"), *ndot); - if inner.for_autocomplete { - return inner; - } - let inner_ty = inner.last_type(); - let index_access = process_index_access(context, dloc, inner_ty, argloc, nargs_); - inner.loc = dloc; - inner.accessors.push(index_access); - inner - } - N::ExpDotted_::DotAutocomplete(_loc, ndot) => { - let mut inner = process_exp_dotted_autocomplete(context, Some("dot access"), *ndot); - if inner.for_autocomplete { - return inner; - } - inner.for_autocomplete = true; - inner - } - } - } - #[growing_stack] fn process_exp_dotted_inner( context: &mut Context, @@ -3227,6 +3183,7 @@ fn process_exp_dotted( N::ExpDotted_::Exp(e) => process_base_exp(context, constraint_verb, dloc, e), N::ExpDotted_::Dot(ndot, field) => { let mut inner = process_exp_dotted_inner(context, Some("dot access"), *ndot); + assert!(inner.autocomplete_last.is_none()); let inner_ty = inner.last_type(); let field_type = resolve_field(context, dloc, inner_ty, &field); inner.loc = dloc; @@ -3237,12 +3194,19 @@ fn process_exp_dotted( } N::ExpDotted_::Index(ndot, sp!(argloc, nargs_)) => { let mut inner = process_exp_dotted_inner(context, Some("dot access"), *ndot); + assert!(inner.autocomplete_last.is_none()); let inner_ty = inner.last_type(); let index_access = process_index_access(context, dloc, inner_ty, argloc, nargs_); inner.loc = dloc; inner.accessors.push(index_access); inner } + N::ExpDotted_::DotAutocomplete(loc, ndot) if context.env.ide_mode() => { + let mut inner = process_exp_dotted_inner(context, Some("dot access"), *ndot); + assert!(inner.autocomplete_last.is_none()); + inner.autocomplete_last = Some(loc); + inner + } N::ExpDotted_::DotAutocomplete(_loc, ndot) => { context .env @@ -3253,17 +3217,13 @@ fn process_exp_dotted( } } - if context.env.ide_mode() { - process_exp_dotted_autocomplete(context, constraint_verb, ndotted) - } else { - process_exp_dotted_inner(context, constraint_verb, ndotted) - } + process_exp_dotted_inner(context, constraint_verb, ndotted) } fn exp_dotted_usage( context: &mut Context, usage: DottedUsage, - eloc: Loc, + exp_loc: Loc, ndotted: N::ExpDotted, ) -> Box { let constraint_verb = match &ndotted.value { @@ -3274,23 +3234,23 @@ fn exp_dotted_usage( }; let edotted = process_exp_dotted(context, constraint_verb, ndotted); if matches!(usage, DottedUsage::Borrow(_)) && edotted.accessors.is_empty() { - context.add_base_type_constraint(eloc, "Invalid borrow", edotted.base.ty.clone()); + context.add_base_type_constraint(exp_loc, "Invalid borrow", edotted.base.ty.clone()); } - resolve_exp_dotted(context, usage, eloc, edotted) + resolve_exp_dotted(context, usage, exp_loc, edotted) } fn exp_dotted_expression( context: &mut Context, usage: DottedUsage, constraint_verb: Option<&str>, - eloc: Loc, + error_loc: Loc, ndotted: N::ExpDotted, ) -> Box { let edotted = process_exp_dotted(context, constraint_verb, ndotted); if matches!(usage, DottedUsage::Borrow(_)) && edotted.accessors.is_empty() { - context.add_base_type_constraint(eloc, "Invalid borrow", edotted.base.ty.clone()); + context.add_base_type_constraint(error_loc, "Invalid borrow", edotted.base.ty.clone()); } - resolve_exp_dotted(context, usage, eloc, edotted) + resolve_exp_dotted(context, usage, error_loc, edotted) } // This comment servees to document the function below. Depending on the shape of the dotted @@ -3325,17 +3285,17 @@ fn exp_dotted_expression( fn resolve_exp_dotted( context: &mut Context, usage: DottedUsage, - eloc: Loc, - mut edotted: ExpDotted, + error_loc: Loc, + edotted: ExpDotted, ) -> Box { use T::UnannotatedExp_ as TE; + let eloc = edotted.loc; let make_exp = |ty, exp_| Box::new(T::exp(ty, sp(eloc, exp_))); - let make_error = |context: &mut Context| make_error_exp(context, eloc); + let make_error = |context: &mut Context| make_error_exp(context, error_loc); let edotted_ty = core::unfold_type(&context.subst, edotted.last_type()); - let for_autocomplete = edotted.for_autocomplete; - debug_print!(context.debug.autocomplete_resolution, ("autocompleting" => edotted; dbg)); + let autocomplete_last = edotted.autocomplete_last; let result = match usage { DottedUsage::Move(loc) => { @@ -3369,7 +3329,7 @@ fn resolve_exp_dotted( loc, ) { // call for effect - borrow_exp_dotted(context, false, edotted); + borrow_exp_dotted(context, error_loc, false, edotted); let msg = "Invalid 'move'. 'move' works only with \ variables, e.g. 'move x'. 'move' on a path access is not supported"; context @@ -3410,11 +3370,11 @@ fn resolve_exp_dotted( } } } else { - exp_dotted_to_owned(context, usage, edotted) + exp_dotted_to_owned(context, error_loc, usage, edotted) }; if !matches!(copy_exp.exp.value, TE::UnresolvedError) { context.add_ability_constraint( - eloc, + error_loc, Some(format!( "Invalid 'copy' of owned value without the '{}' ability", Ability_::Copy @@ -3429,32 +3389,17 @@ fn resolve_exp_dotted( if edotted.accessors.is_empty() { Box::new(edotted.base) } else { - exp_dotted_to_owned(context, DottedUsage::Use, edotted) + exp_dotted_to_owned(context, error_loc, DottedUsage::Use, edotted) } } - DottedUsage::Borrow(mut_) => { - // To maintain previous error reporting - edotted.loc = eloc; - borrow_exp_dotted(context, mut_, edotted) - } + DottedUsage::Borrow(mut_) => borrow_exp_dotted(context, error_loc, mut_, edotted), }; - // Even if for_autocomplete is set, we process the inner dot path to report any additional - // errors that may detect or report. - if for_autocomplete { - let Some(tn) = type_to_type_name(context, &edotted_ty, eloc, "autocompletion".to_string()) - else { - return make_error_exp(context, eloc); - }; - let methods = context.find_all_methods(&tn); - let fields = context.find_all_fields(&tn); - let e_ = TE::AutocompleteDotAccess { - base_exp: result, - methods, - fields, - }; - let ty = sp(eloc, Type_::UnresolvedError); - Box::new(T::exp(ty, sp(eloc, e_))) + if let Some(loc) = autocomplete_last { + assert!(context.env.ide_mode()); + debug_print!(context.debug.autocomplete_resolution, ("computing autocomplete" => result; dbg)); + ide_report_autocomplete(context, &loc, &edotted_ty); + result } else { result } @@ -3487,9 +3432,13 @@ fn resolve_exp_dotted( // mut |- E . (index, methods, args, t) ~> f(e, args ...) : Ref(mut, t) // -fn borrow_exp_dotted(context: &mut Context, mut_: bool, ed: ExpDotted) -> Box { +fn borrow_exp_dotted( + context: &mut Context, + error_loc: Loc, + mut_: bool, + ed: ExpDotted, +) -> Box { use T::UnannotatedExp_ as TE; - fn check_mut(context: &mut Context, loc: Loc, cur_type: Type, expected_mut: bool) { let sp!(tyloc, cur_exp_type) = core::unfold_type(&context.subst, cur_type); let cur_mut = match cur_exp_type { @@ -3516,7 +3465,7 @@ fn borrow_exp_dotted(context: &mut Context, mut_: bool, ed: ExpDotted) -> Box Box { + // report autocomplete information for the IDE + ide_report_autocomplete(context, &name.loc(), &ty); let e_ = TE::Borrow(mut_, exp, name); let ty = sp(loc, Type_::Ref(mut_, Box::new(ty))); exp = Box::new(T::exp(ty, sp(loc, e_))); @@ -3578,7 +3529,7 @@ fn borrow_exp_dotted(context: &mut Context, mut_: bool, ed: ExpDotted) -> Box Box Dereference(e) // -fn exp_dotted_to_owned(context: &mut Context, usage: DottedUsage, ed: ExpDotted) -> Box { +fn exp_dotted_to_owned( + context: &mut Context, + error_loc: Loc, + usage: DottedUsage, + ed: ExpDotted, +) -> Box { use T::UnannotatedExp_ as TE; let (access_msg, access_type) = if let Some(accessor) = ed.accessors.last() { match accessor { @@ -3655,12 +3611,12 @@ fn exp_dotted_to_owned(context: &mut Context, usage: DottedUsage, ed: ExpDotted) if edotted.accessors.is_empty() { edotted.warn_on_constant = false; } - let borrow_exp = borrow_exp_dotted(context, false, edotted); + let borrow_exp = borrow_exp_dotted(context, error_loc, false, edotted); // If we're in an autoborrow, bail. // if matches!(&borrow_exp.exp.value, TE::AutocompleteAccess(..)) { return borrow_exp; } let eloc = borrow_exp.exp.loc; context.add_ability_constraint( - eloc, + error_loc, Some(format!( "Invalid {} of {} without the '{}' ability", case, @@ -3735,6 +3691,20 @@ fn warn_on_constant_borrow(context: &mut Context, loc: Loc, e: &T::Exp) { } } +fn ide_report_autocomplete(context: &mut Context, at_loc: &Loc, in_ty: &Type) { + if !context.env.ide_mode() { + return; + } + let ty = core::unfold_type(&context.subst, in_ty.clone()); + let Some(tn) = type_to_type_name(context, &ty, *at_loc, "autocompletion".to_string()) else { + return; + }; + let methods = context.find_all_methods(&tn); + let fields = context.find_all_fields(&tn); + let info = AutocompleteInfo { methods, fields }; + context.add_ide_info(*at_loc, IDEAnnotation::AutocompleteInfo(Box::new(info))); +} + //************************************************************************************************** // Calls //************************************************************************************************** @@ -3752,7 +3722,7 @@ enum ResolvedMethodCall { fn method_call( context: &mut Context, - loc: Loc, + call_loc: Loc, edotted: ExpDotted, method: Name, ty_args_opt: Option>, @@ -3762,40 +3732,43 @@ fn method_call( use T::UnannotatedExp_ as TE; let mut edotted = edotted; let (m, f, fty, usage) = - match method_call_resolve(context, loc, &mut edotted, method, ty_args_opt) { + match method_call_resolve(context, call_loc, &edotted, method, ty_args_opt) { ResolvedMethodCall::Resolved(m, f, fty, usage) => (*m, f, fty, usage), ResolvedMethodCall::UnknownName if context.env.ide_mode() => { - // If the method name fails to resolve, we do autocomplete for the dotted expression. - edotted.for_autocomplete = true; - let err_ty = context.error_type(loc); + // Even if the method name fails to resolve, we want autocomplete information. + edotted.autocomplete_last = Some(method.loc); + let err_ty = context.error_type(call_loc); let dot_output = - resolve_exp_dotted(context, DottedUsage::Borrow(false), loc, edotted); + resolve_exp_dotted(context, DottedUsage::Borrow(false), call_loc, edotted); return Some((err_ty, dot_output.exp.value)); } ResolvedMethodCall::InvalidBaseType | ResolvedMethodCall::UnknownName => return None, }; - let first_arg = *resolve_exp_dotted(context, usage, loc, edotted); + // report autocomplete information for the IDE + if context.env.ide_mode() { + edotted.autocomplete_last = Some(method.loc); + } + let first_arg = *resolve_exp_dotted(context, usage, call_loc, edotted); args.insert(0, first_arg); - let (mut call, ret_ty) = module_call_impl(context, loc, m, f, fty, argloc, args); + let (mut call, ret_ty) = module_call_impl(context, call_loc, m, f, fty, argloc, args); call.method_name = Some(method); Some((ret_ty, TE::ModuleCall(Box::new(call)))) } fn method_call_resolve( context: &mut Context, - loc: Loc, - edotted: &mut ExpDotted, + call_loc: Loc, + edotted: &ExpDotted, method: Name, ty_args_opt: Option>, ) -> ResolvedMethodCall { - edotted.loc = loc; let edotted_ty = core::unfold_type(&context.subst, edotted.last_type()); - - let Some(tn) = type_to_type_name(context, &edotted_ty, loc, "method call".to_string()) else { + let Some(tn) = type_to_type_name(context, &edotted_ty, call_loc, "method call".to_string()) + else { return ResolvedMethodCall::InvalidBaseType; }; let Some((m, f, fty)) = - core::make_method_call_type(context, loc, &edotted_ty, &tn, method, ty_args_opt) + core::make_method_call_type(context, call_loc, &edotted_ty, &tn, method, ty_args_opt) else { return ResolvedMethodCall::UnknownName; }; @@ -4239,19 +4212,22 @@ fn macro_method_call( nargs: Vec, ) -> Option<(Type, T::UnannotatedExp_)> { let mut edotted = edotted; - let (m, f, fty, usage) = - match method_call_resolve(context, loc, &mut edotted, method, ty_args_opt) { - ResolvedMethodCall::Resolved(m, f, fty, usage) => (*m, f, fty, usage), - ResolvedMethodCall::UnknownName if context.env.ide_mode() => { - // If the method name fails to resolve, we do autocomplete for the dotted expression. - edotted.for_autocomplete = true; - let err_ty = context.error_type(loc); - let dot_output = - resolve_exp_dotted(context, DottedUsage::Borrow(false), loc, edotted); - return Some((err_ty, dot_output.exp.value)); - } - ResolvedMethodCall::InvalidBaseType | ResolvedMethodCall::UnknownName => return None, - }; + let (m, f, fty, usage) = match method_call_resolve(context, loc, &edotted, method, ty_args_opt) + { + ResolvedMethodCall::Resolved(m, f, fty, usage) => (*m, f, fty, usage), + ResolvedMethodCall::UnknownName if context.env.ide_mode() => { + // Even if the method name fails to resolve, we want autocomplete information. + edotted.autocomplete_last = Some(method.loc); + let err_ty = context.error_type(loc); + let dot_output = resolve_exp_dotted(context, DottedUsage::Borrow(false), loc, edotted); + return Some((err_ty, dot_output.exp.value)); + } + ResolvedMethodCall::InvalidBaseType | ResolvedMethodCall::UnknownName => return None, + }; + // report autocomplete information for the IDE + if context.env.ide_mode() { + edotted.autocomplete_last = Some(method.loc); + } let first_arg = *resolve_exp_dotted(context, usage, loc, edotted); let mut args = vec![macro_expand::EvalStrategy::ByValue(first_arg)]; args.extend( @@ -4470,24 +4446,18 @@ fn expand_macro( seq.push_back(sp(body.exp.loc, TS::Seq(body))); let use_funs = N::UseFuns::new(context.current_call_color()); let block = TE::Block((use_funs, seq)); - let e_ = if context.env.ide_mode() { - TE::IDEAnnotation( - T::IDEInfo::MacroCallInfo(MacroCallInfo { - module: m, - name: f, - method_name, - type_arguments: type_args.clone(), - by_value_args, - }), - Box::new(T::Exp { - ty: ty.clone(), - exp: sp(call_loc, block), - }), - ) - } else { - block - }; - (ty, e_) + if context.env.ide_mode() { + let macro_call_info = MacroCallInfo { + module: m, + name: f, + method_name, + type_arguments: type_args.clone(), + by_value_args, + }; + let info = IDEAnnotation::MacroCallInfo(Box::new(macro_call_info)); + context.add_ide_info(call_loc, info); + } + (ty, block) } }; if context.pop_macro_expansion(call_loc, &m, &f) { diff --git a/external-crates/move/crates/move-compiler/src/typing/visitor.rs b/external-crates/move/crates/move-compiler/src/typing/visitor.rs index e7ed4f28b2274..a10643a417bbc 100644 --- a/external-crates/move/crates/move-compiler/src/typing/visitor.rs +++ b/external-crates/move/crates/move-compiler/src/typing/visitor.rs @@ -227,7 +227,6 @@ pub trait TypingVisitorContext { E::Loop { body, .. } => self.visit_exp(body), E::NamedBlock(_, seq) => self.visit_seq(seq), E::Block(seq) => self.visit_seq(seq), - E::IDEAnnotation(_, e) => self.visit_exp(e), E::Assign(_, _, e) => self.visit_exp(e), E::Mutate(e1, e2) => { self.visit_exp(e1); @@ -260,11 +259,6 @@ pub trait TypingVisitorContext { E::TempBorrow(_, e) => self.visit_exp(e), E::Cast(e, _) => self.visit_exp(e), E::Annotate(e, _) => self.visit_exp(e), - E::AutocompleteDotAccess { - base_exp, - methods: _, - fields: _, - } => self.visit_exp(base_exp), E::Unit { .. } | E::Value(_) | E::Move { .. } diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/dot_incomplete.exp b/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/dot_incomplete.exp index c4a482eefd239..b109e75964879 100644 --- a/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/dot_incomplete.exp +++ b/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/dot_incomplete.exp @@ -1,9 +1,3 @@ -error[E15001]: IDE autocomplete - ┌─ tests/move_2024/ide_mode/dot_incomplete.move:13:21 - │ -13 │ let _tmp1 = _s.; // incomplete with `;` (next line should parse) - │ ^^^ Autocompletes to: 'a' - error[E01002]: unexpected token ┌─ tests/move_2024/ide_mode/dot_incomplete.move:13:24 │ @@ -13,12 +7,6 @@ error[E01002]: unexpected token │ Unexpected ';' │ Expected an identifier or a decimal number -error[E15001]: IDE autocomplete - ┌─ tests/move_2024/ide_mode/dot_incomplete.move:14:21 - │ -14 │ let _tmp2 = _s.a.; // incomplete with `;` (next line should parse) - │ ^^^^^ Autocompletes to: 'x' - error[E01002]: unexpected token ┌─ tests/move_2024/ide_mode/dot_incomplete.move:14:26 │ @@ -28,12 +16,6 @@ error[E01002]: unexpected token │ Unexpected ';' │ Expected an identifier or a decimal number -error[E15001]: IDE autocomplete - ┌─ tests/move_2024/ide_mode/dot_incomplete.move:15:21 - │ -15 │ let _tmp3 = _s.a. // incomplete without `;` (unexpected `let`) - │ ^^^^^ Autocompletes to: 'x' - error[E01002]: unexpected token ┌─ tests/move_2024/ide_mode/dot_incomplete.move:16:9 │ diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/index_autocomplete.exp b/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/index_autocomplete.exp index 285ae8c6e170a..65c84e0fdb4b1 100644 --- a/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/index_autocomplete.exp +++ b/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/index_autocomplete.exp @@ -1,9 +1,3 @@ -error[E15001]: IDE autocomplete - ┌─ tests/move_2024/ide_mode/index_autocomplete.move:17:17 - │ -17 │ let _ = &in.0.0[1]. ; - │ ^^^^^^^^^^^ Autocompletes to: '0' - error[E01002]: unexpected token ┌─ tests/move_2024/ide_mode/index_autocomplete.move:17:29 │ @@ -13,12 +7,6 @@ error[E01002]: unexpected token │ Unexpected ';' │ Expected an identifier or a decimal number -error[E15001]: IDE autocomplete - ┌─ tests/move_2024/ide_mode/index_autocomplete.move:21:17 - │ -21 │ let _ = &in.0.0[1].0[0]. ; - │ ^^^^^^^^^^^^^^^^ Autocompletes to: - error[E01002]: unexpected token ┌─ tests/move_2024/ide_mode/index_autocomplete.move:21:34 │ @@ -28,12 +16,6 @@ error[E01002]: unexpected token │ Unexpected ';' │ Expected an identifier or a decimal number -error[E15001]: IDE autocomplete - ┌─ tests/move_2024/ide_mode/index_autocomplete.move:25:17 - │ -25 │ let _ = &in.0.0[1].0[0]. ; - │ ^^^^^^^^^^^^^^^^ Autocompletes to: 'c' or 'd' - error[E01002]: unexpected token ┌─ tests/move_2024/ide_mode/index_autocomplete.move:25:34 │ diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/named_struct_autocomplete.exp b/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/named_struct_autocomplete.exp index 4da0d47191c91..9c31cb3b2c1db 100644 --- a/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/named_struct_autocomplete.exp +++ b/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/named_struct_autocomplete.exp @@ -1,9 +1,3 @@ -error[E15001]: IDE autocomplete - ┌─ tests/move_2024/ide_mode/named_struct_autocomplete.move:13:21 - │ -13 │ let _tmp1 = _s.; - │ ^^^ Autocompletes to: 'a' - error[E01002]: unexpected token ┌─ tests/move_2024/ide_mode/named_struct_autocomplete.move:13:24 │ @@ -13,12 +7,6 @@ error[E01002]: unexpected token │ Unexpected ';' │ Expected an identifier or a decimal number -error[E15001]: IDE autocomplete - ┌─ tests/move_2024/ide_mode/named_struct_autocomplete.move:14:21 - │ -14 │ let _tmp2 = _s.a.; - │ ^^^^^ Autocompletes to: 'x' - error[E01002]: unexpected token ┌─ tests/move_2024/ide_mode/named_struct_autocomplete.move:14:26 │ diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/positional_struct_autocomplete.exp b/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/positional_struct_autocomplete.exp index abe451a9defe2..e206d634ed1fa 100644 --- a/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/positional_struct_autocomplete.exp +++ b/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/positional_struct_autocomplete.exp @@ -1,9 +1,3 @@ -error[E15001]: IDE autocomplete - ┌─ tests/move_2024/ide_mode/positional_struct_autocomplete.move:9:21 - │ -9 │ let _tmp1 = _s.; - │ ^^^ Autocompletes to: '0' - error[E01002]: unexpected token ┌─ tests/move_2024/ide_mode/positional_struct_autocomplete.move:9:24 │ @@ -13,12 +7,6 @@ error[E01002]: unexpected token │ Unexpected ';' │ Expected an identifier or a decimal number -error[E15001]: IDE autocomplete - ┌─ tests/move_2024/ide_mode/positional_struct_autocomplete.move:10:21 - │ -10 │ let _tmp2 = _s.0.; - │ ^^^^^ Autocompletes to: '0' - error[E01002]: unexpected token ┌─ tests/move_2024/ide_mode/positional_struct_autocomplete.move:10:26 │ diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/struct_method_autocomplete.exp b/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/struct_method_autocomplete.exp index b7f1142c0dfcc..ff2accbd13835 100644 --- a/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/struct_method_autocomplete.exp +++ b/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/struct_method_autocomplete.exp @@ -1,9 +1,3 @@ -error[E15001]: IDE autocomplete - ┌─ tests/move_2024/ide_mode/struct_method_autocomplete.move:18:21 - │ -18 │ let _tmp1 = _a.; - │ ^^^ Autocompletes to: 'a::m::t0', 'a::m::t1', or 'a::m::t2' - error[E01002]: unexpected token ┌─ tests/move_2024/ide_mode/struct_method_autocomplete.move:18:24 │ @@ -13,12 +7,6 @@ error[E01002]: unexpected token │ Unexpected ';' │ Expected an identifier or a decimal number -error[E15001]: IDE autocomplete - ┌─ tests/move_2024/ide_mode/struct_method_autocomplete.move:19:21 - │ -19 │ let _tmp2 = _b.; - │ ^^^ Autocompletes to: 'a::m::t3', 'a::m::t4', or 'a::m::t5' - error[E01002]: unexpected token ┌─ tests/move_2024/ide_mode/struct_method_autocomplete.move:19:24 │ diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/struct_scoped_autocomplete.exp b/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/struct_scoped_autocomplete.exp index 7dea9086a8c69..8ec4654f627f8 100644 --- a/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/struct_scoped_autocomplete.exp +++ b/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/struct_scoped_autocomplete.exp @@ -1,9 +1,3 @@ -error[E15001]: IDE autocomplete - ┌─ tests/move_2024/ide_mode/struct_scoped_autocomplete.move:17:21 - │ -17 │ let _tmp1 = _a.; - │ ^^^ Autocompletes to: 'a::m::t0' - error[E01002]: unexpected token ┌─ tests/move_2024/ide_mode/struct_scoped_autocomplete.move:17:24 │ @@ -13,12 +7,6 @@ error[E01002]: unexpected token │ Unexpected ';' │ Expected an identifier or a decimal number -error[E15001]: IDE autocomplete - ┌─ tests/move_2024/ide_mode/struct_scoped_autocomplete.move:18:21 - │ -18 │ let _tmp2 = _b.; - │ ^^^ Autocompletes to: 'a::m::t1' - error[E01002]: unexpected token ┌─ tests/move_2024/ide_mode/struct_scoped_autocomplete.move:18:24 │ diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/let_mut_borrow_mut_dot_call.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/let_mut_borrow_mut_dot_call.exp index 3749156b36853..2495486805772 100644 --- a/external-crates/move/crates/move-compiler/tests/move_2024/typing/let_mut_borrow_mut_dot_call.exp +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/let_mut_borrow_mut_dot_call.exp @@ -5,7 +5,7 @@ error[E04024]: invalid usage of immutable variable │ - To use the variable mutably, it must be declared 'mut', e.g. 'mut x' · 8 │ x.foo(); - │ ^^^^^^^ Invalid mutable borrow of immutable variable 'x' + │ ^ Invalid mutable borrow of immutable variable 'x' error[E04024]: invalid usage of immutable variable ┌─ tests/move_2024/typing/let_mut_borrow_mut_dot_call.move:9:9 @@ -14,7 +14,7 @@ error[E04024]: invalid usage of immutable variable │ - To use the variable mutably, it must be declared 'mut', e.g. 'mut y' · 9 │ y.foo(); - │ ^^^^^^^ Invalid mutable borrow of immutable variable 'y' + │ ^ Invalid mutable borrow of immutable variable 'y' error[E04024]: invalid usage of immutable variable ┌─ tests/move_2024/typing/let_mut_borrow_mut_dot_call.move:10:9 @@ -23,5 +23,5 @@ error[E04024]: invalid usage of immutable variable │ - To use the variable mutably, it must be declared 'mut', e.g. 'mut s' · 10 │ s.foo(); - │ ^^^^^^^ Invalid mutable borrow of immutable variable 's' + │ ^ Invalid mutable borrow of immutable variable 's' diff --git a/external-crates/move/crates/move-compiler/tests/move_check/ide_mode/dot_incomplete.exp b/external-crates/move/crates/move-compiler/tests/move_check/ide_mode/dot_incomplete.exp index 770682ba5502a..46f9d796222c8 100644 --- a/external-crates/move/crates/move-compiler/tests/move_check/ide_mode/dot_incomplete.exp +++ b/external-crates/move/crates/move-compiler/tests/move_check/ide_mode/dot_incomplete.exp @@ -1,17 +1,3 @@ -warning[W09003]: unused assignment - ┌─ tests/move_check/ide_mode/dot_incomplete.move:12:13 - │ -12 │ let s = AnotherStruct { another_field: SomeStruct { some_field: 0 } }; - │ ^ Unused assignment for variable 's'. Consider removing, replacing with '_', or prefixing with '_' (e.g., '_s') - │ - = This warning can be suppressed with '#[allow(unused_assignment)]' applied to the 'module' or module member ('const', 'fun', or 'struct') - -error[E15001]: IDE autocomplete - ┌─ tests/move_check/ide_mode/dot_incomplete.move:13:21 - │ -13 │ let _tmp1 = s.; // incomplete with `;` (next line should parse) - │ ^^ Autocompletes to: 'another_field' - error[E01002]: unexpected token ┌─ tests/move_check/ide_mode/dot_incomplete.move:13:23 │ @@ -21,12 +7,6 @@ error[E01002]: unexpected token │ Unexpected ';' │ Expected an identifier or a decimal number -error[E15001]: IDE autocomplete - ┌─ tests/move_check/ide_mode/dot_incomplete.move:14:21 - │ -14 │ let _tmp2 = s.another_field.; // incomplete with `;` (next line should parse) - │ ^^^^^^^^^^^^^^^^ Autocompletes to: 'some_field' - error[E01002]: unexpected token ┌─ tests/move_check/ide_mode/dot_incomplete.move:14:37 │ @@ -36,12 +16,6 @@ error[E01002]: unexpected token │ Unexpected ';' │ Expected an identifier or a decimal number -error[E15001]: IDE autocomplete - ┌─ tests/move_check/ide_mode/dot_incomplete.move:15:21 - │ -15 │ let _tmp3 = s.another_field. // incomplete without `;` (unexpected `let`) - │ ^^^^^^^^^^^^^^^^ Autocompletes to: 'some_field' - error[E01002]: unexpected token ┌─ tests/move_check/ide_mode/dot_incomplete.move:16:9 │ @@ -51,12 +25,6 @@ error[E01002]: unexpected token │ Unexpected 'let' │ Expected an identifier or a decimal number -error[E15001]: IDE autocomplete - ┌─ tests/move_check/ide_mode/dot_incomplete.move:17:20 - │ -17 │ let _tmp = s. // incomplete without `;` (unexpected `}`) - │ ^^ Autocompletes to: 'another_field' - error[E01002]: unexpected token ┌─ tests/move_check/ide_mode/dot_incomplete.move:18:5 │ diff --git a/external-crates/move/crates/move-compiler/tests/move_check/ide_mode/struct_method_autocomplete.exp b/external-crates/move/crates/move-compiler/tests/move_check/ide_mode/struct_method_autocomplete.exp index e7fbf475622ed..1cef3d8aae539 100644 --- a/external-crates/move/crates/move-compiler/tests/move_check/ide_mode/struct_method_autocomplete.exp +++ b/external-crates/move/crates/move-compiler/tests/move_check/ide_mode/struct_method_autocomplete.exp @@ -22,12 +22,6 @@ error[E13001]: feature is not supported in specified edition │ = You can update the edition in the 'Move.toml', or via command line flag if invoking the compiler directly. -error[E15001]: IDE autocomplete - ┌─ tests/move_check/ide_mode/struct_method_autocomplete.move:18:21 - │ -18 │ let _tmp1 = _a.; - │ ^^^ Autocompletes to: - error[E01002]: unexpected token ┌─ tests/move_check/ide_mode/struct_method_autocomplete.move:18:24 │ @@ -37,12 +31,6 @@ error[E01002]: unexpected token │ Unexpected ';' │ Expected an identifier or a decimal number -error[E15001]: IDE autocomplete - ┌─ tests/move_check/ide_mode/struct_method_autocomplete.move:19:21 - │ -19 │ let _tmp2 = _b.; - │ ^^^ Autocompletes to: - error[E01002]: unexpected token ┌─ tests/move_check/ide_mode/struct_method_autocomplete.move:19:24 │ diff --git a/external-crates/move/crates/move-compiler/tests/move_check/typing/constant_unsupported_exps.exp b/external-crates/move/crates/move-compiler/tests/move_check/typing/constant_unsupported_exps.exp index 61c990c49a519..df33c4621c100 100644 --- a/external-crates/move/crates/move-compiler/tests/move_check/typing/constant_unsupported_exps.exp +++ b/external-crates/move/crates/move-compiler/tests/move_check/typing/constant_unsupported_exps.exp @@ -208,3 +208,9 @@ error[E04013]: invalid statement or expression in constant 39 │ *&b.f; │ ^^^^ References (and reference operations) are not supported in constants +error[E04013]: invalid statement or expression in constant + ┌─ tests/move_check/typing/constant_unsupported_exps.move:39:11 + │ +39 │ *&b.f; + │ ^^^ References (and reference operations) are not supported in constants + diff --git a/external-crates/move/crates/move-ir-types/src/location.rs b/external-crates/move/crates/move-ir-types/src/location.rs index a119b553817dc..fb6ee5ba54fb9 100644 --- a/external-crates/move/crates/move-ir-types/src/location.rs +++ b/external-crates/move/crates/move-ir-types/src/location.rs @@ -69,6 +69,16 @@ impl Loc { end: self.end as usize, } } + + /// Indicates this this location contains the provided location + pub fn contains(&self, other: &Loc) -> bool { + self.file_hash == other.file_hash && self.start <= other.start && other.end <= self.end + } + + /// Indicates this this location overlaps the provided location + pub fn overlaps(&self, other: &Loc) -> bool { + self.file_hash == other.file_hash && !(self.end < other.start || other.end < self.start) + } } impl PartialOrd for Loc {