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 {