diff --git a/external-crates/move/crates/move-compiler/src/expansion/path_expander.rs b/external-crates/move/crates/move-compiler/src/expansion/path_expander.rs index 31bd3a5af940a..d97869a4580c2 100644 --- a/external-crates/move/crates/move-compiler/src/expansion/path_expander.rs +++ b/external-crates/move/crates/move-compiler/src/expansion/path_expander.rs @@ -50,9 +50,8 @@ pub enum Access { Module, // Just used for errors } -// This trait describes the commands available to handle alias scopes and expanding name access -// chains. This is used to model both legacy and modern path expansion. - +/// This trait describes the commands available to handle alias scopes and expanding name access +/// chains. This is used to model both legacy and modern path expansion. pub trait PathExpander { // Push a new innermost alias scope fn push_alias_scope( diff --git a/external-crates/move/crates/move-compiler/src/expansion/translate.rs b/external-crates/move/crates/move-compiler/src/expansion/translate.rs index bb3ee58ac946d..7d4230b6cf5cc 100644 --- a/external-crates/move/crates/move-compiler/src/expansion/translate.rs +++ b/external-crates/move/crates/move-compiler/src/expansion/translate.rs @@ -221,6 +221,18 @@ impl<'env, 'map> Context<'env, 'map> { .name_access_chain_to_module_ident(inner_context, chain) } + fn error_ide_autocomplete_suggestion(&mut self, loc: Loc) { + let Context { + path_expander, + defn_context: inner_context, + .. + } = self; + path_expander + .as_mut() + .unwrap() + .ide_autocomplete_suggestion(inner_context, loc) + } + pub fn spec_deprecated(&mut self, loc: Loc, is_error: bool) { let diag = self.spec_deprecated_diag(loc, is_error); self.env().add_diag(diag); @@ -2328,7 +2340,11 @@ fn type_(context: &mut Context, sp!(loc, pt_): P::Type) -> E::Type { let result = type_(context, *result); ET::Fun(args, Box::new(result)) } - PT::UnresolvedError => ET::UnresolvedError, + PT::UnresolvedError => { + // Treat an unresolved error as a leading access + context.error_ide_autocomplete_suggestion(loc); + ET::UnresolvedError + } }; sp(loc, t_) } diff --git a/external-crates/move/crates/move-compiler/src/parser/syntax.rs b/external-crates/move/crates/move-compiler/src/parser/syntax.rs index aec02f56d8f13..2e1f9d21e48ab 100644 --- a/external-crates/move/crates/move-compiler/src/parser/syntax.rs +++ b/external-crates/move/crates/move-compiler/src/parser/syntax.rs @@ -2941,21 +2941,28 @@ fn parse_type_( )); } _ => { - let tn = if whitespace_sensitive_ty_args { - parse_name_access_chain( - context, - /* macros */ false, - /* tyargs */ true, - || "a type name", - )? + if context.at_stop_set() { + context + .env + .add_diag(*unexpected_token_error(context.tokens, "a type name")); + Type_::UnresolvedError } else { - parse_name_access_chain_with_tyarg_whitespace( - context, - /* macros */ false, - || "a type name", - )? - }; - Type_::Apply(Box::new(tn)) + let tn = if whitespace_sensitive_ty_args { + parse_name_access_chain( + context, + /* macros */ false, + /* tyargs */ true, + || "a type name", + )? + } else { + parse_name_access_chain_with_tyarg_whitespace( + context, + /* macros */ false, + || "a type name", + )? + }; + Type_::Apply(Box::new(tn)) + } } }; let end_loc = context.tokens.previous_end_loc(); diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/missing_type_suggestions.exp b/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/missing_type_suggestions.exp new file mode 100644 index 0000000000000..575a7d1222305 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/missing_type_suggestions.exp @@ -0,0 +1,68 @@ +warning[W09001]: unused alias + ┌─ tests/move_2024/ide_mode/missing_type_suggestions.move:7:16 + │ +7 │ use a::m::{Self, S, A}; + │ ^^^^ Unused 'use' of alias 'm'. Consider removing it + │ + = This warning can be suppressed with '#[allow(unused_use)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +warning[W09001]: unused alias + ┌─ tests/move_2024/ide_mode/missing_type_suggestions.move:7:22 + │ +7 │ use a::m::{Self, S, A}; + │ ^ Unused 'use' of alias 'S'. Consider removing it + │ + = This warning can be suppressed with '#[allow(unused_use)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +warning[W09001]: unused alias + ┌─ tests/move_2024/ide_mode/missing_type_suggestions.move:7:25 + │ +7 │ use a::m::{Self, S, A}; + │ ^ Unused 'use' of alias 'A'. Consider removing it + │ + = This warning can be suppressed with '#[allow(unused_use)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +warning[W09002]: unused variable + ┌─ tests/move_2024/ide_mode/missing_type_suggestions.move:9:18 + │ +9 │ public fun p(a: + │ ^ Unused parameter 'a'. Consider removing or prefixing with an underscore: '_a' + │ + = This warning can be suppressed with '#[allow(unused_variable)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +error[E01002]: unexpected token + ┌─ tests/move_2024/ide_mode/missing_type_suggestions.move:11:5 + │ + 9 │ public fun p(a: + │ - To match this '(' +10 │ +11 │ public fun q(): + │ ^ Expected ')' + +error[E01002]: unexpected token + ┌─ tests/move_2024/ide_mode/missing_type_suggestions.move:11:5 + │ +11 │ public fun q(): + │ ^^^^^^ + │ │ + │ Unexpected 'public' + │ Expected a type name + +error[E01002]: unexpected token + ┌─ tests/move_2024/ide_mode/missing_type_suggestions.move:12:1 + │ +12 │ } + │ ^ + │ │ + │ Unexpected '}' + │ Expected a type name + +error[E01002]: unexpected token + ┌─ tests/move_2024/ide_mode/missing_type_suggestions.move:14:1 + │ +14 │ + │ ^ + │ + │ Unexpected end-of-file + │ Expected '{' + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/missing_type_suggestions.ide b/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/missing_type_suggestions.ide new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/missing_type_suggestions.ide.exp b/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/missing_type_suggestions.ide.exp new file mode 100644 index 0000000000000..2d87fe58ca754 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/missing_type_suggestions.ide.exp @@ -0,0 +1,142 @@ +note[I15006]: IDE path autocomplete + ┌─ tests/move_2024/ide_mode/missing_type_suggestions.move:1:1 + │ +1 │ ╭ module a::m { +2 │ │ public struct S { x: T } +3 │ │ public struct A {} +4 │ │ } + │ ╰─^ Possible in-scope names + │ + = members: 'A -> a::m::A', 'Option -> std::option::Option', or 'S -> a::m::S' + = modules: 'Self -> a::m', 'option -> std::option', or 'vector -> std::vector' + = addresses: 'B -> 0x42', 'K -> 0x19', 'M -> 0x40', 'a -> 0x44', 'b -> 0x45', 'k -> 0x19', 'std -> 0x1', or 'sui -> 0x2' + = type params: + +warning[W09009]: unused struct field + ┌─ tests/move_2024/ide_mode/missing_type_suggestions.move:2:26 + │ +2 │ public struct S { x: T } + │ ^ The 'x' field of the 'S' type is unused + │ + = This warning can be suppressed with '#[allow(unused_field)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +note[I15006]: IDE path autocomplete + ┌─ tests/move_2024/ide_mode/missing_type_suggestions.move:2:29 + │ +2 │ public struct S { x: T } + │ ^ Possible in-scope names + │ + = members: 'A -> a::m::A', 'Option -> std::option::Option', or 'S -> a::m::S' + = modules: 'Self -> a::m', 'option -> std::option', or 'vector -> std::vector' + = addresses: 'B -> 0x42', 'K -> 0x19', 'M -> 0x40', 'a -> 0x44', 'b -> 0x45', 'k -> 0x19', 'std -> 0x1', or 'sui -> 0x2' + = type params: 'T' + +note[I15006]: IDE path autocomplete + ┌─ tests/move_2024/ide_mode/missing_type_suggestions.move:6:1 + │ + 6 │ ╭ module a::test { + 7 │ │ use a::m::{Self, S, A}; + 8 │ │ + 9 │ │ public fun p(a: +10 │ │ +11 │ │ public fun q(): +12 │ │ } + │ ╰─^ Possible in-scope names + │ + = members: 'A -> a::m::A', 'Option -> std::option::Option', 'S -> a::m::S', 'p -> a::test::p', or 'q -> a::test::q' + = modules: 'Self -> a::test', 'm -> a::m', 'option -> std::option', or 'vector -> std::vector' + = addresses: 'B -> 0x42', 'K -> 0x19', 'M -> 0x40', 'a -> 0x44', 'b -> 0x45', 'k -> 0x19', 'std -> 0x1', or 'sui -> 0x2' + = type params: + +warning[W09001]: unused alias + ┌─ tests/move_2024/ide_mode/missing_type_suggestions.move:7:16 + │ +7 │ use a::m::{Self, S, A}; + │ ^^^^ Unused 'use' of alias 'm'. Consider removing it + │ + = This warning can be suppressed with '#[allow(unused_use)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +warning[W09001]: unused alias + ┌─ tests/move_2024/ide_mode/missing_type_suggestions.move:7:22 + │ +7 │ use a::m::{Self, S, A}; + │ ^ Unused 'use' of alias 'S'. Consider removing it + │ + = This warning can be suppressed with '#[allow(unused_use)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +warning[W09001]: unused alias + ┌─ tests/move_2024/ide_mode/missing_type_suggestions.move:7:25 + │ +7 │ use a::m::{Self, S, A}; + │ ^ Unused 'use' of alias 'A'. Consider removing it + │ + = This warning can be suppressed with '#[allow(unused_use)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +warning[W09002]: unused variable + ┌─ tests/move_2024/ide_mode/missing_type_suggestions.move:9:18 + │ +9 │ public fun p(a: + │ ^ Unused parameter 'a'. Consider removing or prefixing with an underscore: '_a' + │ + = This warning can be suppressed with '#[allow(unused_variable)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +note[I15006]: IDE path autocomplete + ┌─ tests/move_2024/ide_mode/missing_type_suggestions.move:11:5 + │ + 9 │ │ public fun p(a: + │ ╰───────────────────^ Possible in-scope names +10 │ +11 │ ╭ public fun q(): + │ + = members: 'A -> a::m::A', 'Option -> std::option::Option', 'S -> a::m::S', 'p -> a::test::p', or 'q -> a::test::q' + = modules: 'Self -> a::test', 'm -> a::m', 'option -> std::option', or 'vector -> std::vector' + = addresses: 'B -> 0x42', 'K -> 0x19', 'M -> 0x40', 'a -> 0x44', 'b -> 0x45', 'k -> 0x19', 'std -> 0x1', or 'sui -> 0x2' + = type params: + +error[E01002]: unexpected token + ┌─ tests/move_2024/ide_mode/missing_type_suggestions.move:11:5 + │ + 9 │ public fun p(a: + │ - To match this '(' +10 │ +11 │ public fun q(): + │ ^ Expected ')' + +error[E01002]: unexpected token + ┌─ tests/move_2024/ide_mode/missing_type_suggestions.move:11:5 + │ +11 │ public fun q(): + │ ^^^^^^ + │ │ + │ Unexpected 'public' + │ Expected a type name + +note[I15006]: IDE path autocomplete + ┌─ tests/move_2024/ide_mode/missing_type_suggestions.move:11:16 + │ +11 │ public fun q(): + │ ^ Possible in-scope names + │ + = members: 'A -> a::m::A', 'Option -> std::option::Option', 'S -> a::m::S', 'p -> a::test::p', or 'q -> a::test::q' + = modules: 'Self -> a::test', 'm -> a::m', 'option -> std::option', or 'vector -> std::vector' + = addresses: 'B -> 0x42', 'K -> 0x19', 'M -> 0x40', 'a -> 0x44', 'b -> 0x45', 'k -> 0x19', 'std -> 0x1', or 'sui -> 0x2' + = type params: + +error[E01002]: unexpected token + ┌─ tests/move_2024/ide_mode/missing_type_suggestions.move:12:1 + │ +12 │ } + │ ^ + │ │ + │ Unexpected '}' + │ Expected a type name + +error[E01002]: unexpected token + ┌─ tests/move_2024/ide_mode/missing_type_suggestions.move:14:1 + │ +14 │ + │ ^ + │ + │ Unexpected end-of-file + │ Expected '{' + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/missing_type_suggestions.move b/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/missing_type_suggestions.move new file mode 100644 index 0000000000000..9ac3018bfc162 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/missing_type_suggestions.move @@ -0,0 +1,13 @@ +module a::m { + public struct S { x: T } + public struct A {} +} + +module a::test { + use a::m::{Self, S, A}; + + public fun p(a: + + public fun q(): +} + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/partial_type_suggestions.exp b/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/partial_type_suggestions.exp new file mode 100644 index 0000000000000..e11b3a84f1d49 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/ide_mode/partial_type_suggestions.exp @@ -0,0 +1,66 @@ +warning[W09001]: unused alias + ┌─ tests/move_2024/ide_mode/partial_type_suggestions.move:7:16 + │ +7 │ use a::m::{Self, S, A}; + │ ^^^^ Unused 'use' of alias 'm'. Consider removing it + │ + = This warning can be suppressed with '#[allow(unused_use)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +error[E03008]: too few type arguments + ┌─ tests/move_2024/ide_mode/partial_type_suggestions.move:9:28 + │ +9 │ public fun p(): vector' + +error[E01002]: unexpected token + ┌─ tests/move_2024/ide_mode/partial_type_suggestions.move:11:5 + │ +11 │ public fun q(x: S' + +error[E01002]: unexpected token + ┌─ tests/move_2024/ide_mode/partial_type_suggestions.move:12:1 + │ +12 │ } + │ ^ + │ │ + │ Unexpected '}' + │ Expected ',' or ')' + +error[E01002]: unexpected token + ┌─ tests/move_2024/ide_mode/partial_type_suggestions.move:14:1 + │ +11 │ public fun q(x: S { x: T } +3 │ │ public struct A {} +4 │ │ } + │ ╰─^ Possible in-scope names + │ + = members: 'A -> a::m::A', 'Option -> std::option::Option', or 'S -> a::m::S' + = modules: 'Self -> a::m', 'option -> std::option', or 'vector -> std::vector' + = addresses: 'B -> 0x42', 'K -> 0x19', 'M -> 0x40', 'a -> 0x44', 'b -> 0x45', 'k -> 0x19', 'std -> 0x1', or 'sui -> 0x2' + = type params: + +note[I15006]: IDE path autocomplete + ┌─ tests/move_2024/ide_mode/partial_type_suggestions.move:2:29 + │ +2 │ public struct S { x: T } + │ ^ Possible in-scope names + │ + = members: 'A -> a::m::A', 'Option -> std::option::Option', or 'S -> a::m::S' + = modules: 'Self -> a::m', 'option -> std::option', or 'vector -> std::vector' + = addresses: 'B -> 0x42', 'K -> 0x19', 'M -> 0x40', 'a -> 0x44', 'b -> 0x45', 'k -> 0x19', 'std -> 0x1', or 'sui -> 0x2' + = type params: 'T' + +note[I15006]: IDE path autocomplete + ┌─ tests/move_2024/ide_mode/partial_type_suggestions.move:6:1 + │ + 6 │ ╭ module a::test { + 7 │ │ use a::m::{Self, S, A}; + 8 │ │ + 9 │ │ public fun p(): vector a::m::A', 'Option -> std::option::Option', 'S -> a::m::S', 'p -> a::test::p', or 'q -> a::test::q' + = modules: 'Self -> a::test', 'm -> a::m', 'option -> std::option', or 'vector -> std::vector' + = addresses: 'B -> 0x42', 'K -> 0x19', 'M -> 0x40', 'a -> 0x44', 'b -> 0x45', 'k -> 0x19', 'std -> 0x1', or 'sui -> 0x2' + = type params: + +warning[W09001]: unused alias + ┌─ tests/move_2024/ide_mode/partial_type_suggestions.move:7:16 + │ +7 │ use a::m::{Self, S, A}; + │ ^^^^ Unused 'use' of alias 'm'. Consider removing it + │ + = This warning can be suppressed with '#[allow(unused_use)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +note[I15006]: IDE path autocomplete + ┌─ tests/move_2024/ide_mode/partial_type_suggestions.move:9:28 + │ +9 │ public fun p(): vector a::m::A', 'Option -> std::option::Option', 'S -> a::m::S', 'p -> a::test::p', or 'q -> a::test::q' + = modules: 'Self -> a::test', 'm -> a::m', 'option -> std::option', or 'vector -> std::vector' + = addresses: 'B -> 0x42', 'K -> 0x19', 'M -> 0x40', 'a -> 0x44', 'b -> 0x45', 'k -> 0x19', 'std -> 0x1', or 'sui -> 0x2' + = type params: + +error[E03008]: too few type arguments + ┌─ tests/move_2024/ide_mode/partial_type_suggestions.move:9:28 + │ +9 │ public fun p(): vector' + +error[E01002]: unexpected token + ┌─ tests/move_2024/ide_mode/partial_type_suggestions.move:11:5 + │ +11 │ public fun q(x: S a::m::A', 'Option -> std::option::Option', 'S -> a::m::S', 'p -> a::test::p', or 'q -> a::test::q' + = modules: 'Self -> a::test', 'm -> a::m', 'option -> std::option', or 'vector -> std::vector' + = addresses: 'B -> 0x42', 'K -> 0x19', 'M -> 0x40', 'a -> 0x44', 'b -> 0x45', 'k -> 0x19', 'std -> 0x1', or 'sui -> 0x2' + = type params: + +note[I15006]: IDE path autocomplete + ┌─ tests/move_2024/ide_mode/partial_type_suggestions.move:11:23 + │ +11 │ public fun q(x: S a::m::A', 'Option -> std::option::Option', 'S -> a::m::S', 'p -> a::test::p', or 'q -> a::test::q' + = modules: 'Self -> a::test', 'm -> a::m', 'option -> std::option', or 'vector -> std::vector' + = addresses: 'B -> 0x42', 'K -> 0x19', 'M -> 0x40', 'a -> 0x44', 'b -> 0x45', 'k -> 0x19', 'std -> 0x1', or 'sui -> 0x2' + = type params: + +error[E01002]: unexpected token + ┌─ tests/move_2024/ide_mode/partial_type_suggestions.move:12:1 + │ +11 │ public fun q(x: S' + +error[E01002]: unexpected token + ┌─ tests/move_2024/ide_mode/partial_type_suggestions.move:12:1 + │ +12 │ } + │ ^ + │ │ + │ Unexpected '}' + │ Expected ',' or ')' + +error[E01002]: unexpected token + ┌─ tests/move_2024/ide_mode/partial_type_suggestions.move:14:1 + │ +11 │ public fun q(x: S { x: T } + public struct A {} +} + +module a::test { + use a::m::{Self, S, A}; + + public fun p(): vector