From fe17fe2788c2c9c361d7dbb2d69dab3cf822faae Mon Sep 17 00:00:00 2001 From: Adam Welc Date: Wed, 25 Sep 2024 23:41:26 +0200 Subject: [PATCH] [move-ide] On-hover improvements (#19437) ## Description This PR improves on several instance of on-hover. 1. Doc comments now preserves indentation ![image](https://github.com/user-attachments/assets/2380db0c-1619-4080-ba48-b438b1b7f0d5) 2. Clean up the way function signatures (in particular type params and regular params) are displayed on-hover instead of always displaying them on a single line: ![image](https://github.com/user-attachments/assets/17056202-4469-4ddb-a2be-a43564e7e0e2) 3. Use information about implicit modules/datatype when displaying type information on-hover to abbreviate types by omitting package (for implicit modules, e.g., `vector::singleton` instead of `std::vector::singleton`) or both package and modules (for implicit datatypes, e.g., `Option`, instead of `std::option::Option`) ![image](https://github.com/user-attachments/assets/ad639db4-a30a-4b70-852f-8614adca812f) ## Test plan All new and old tests must pass --- Cargo.lock | 1 + external-crates/move/Cargo.lock | 1 + .../move/crates/move-analyzer/Cargo.toml | 1 + .../move-analyzer/editors/code/package.json | 2 +- .../move-analyzer/src/completions/utils.rs | 8 +- .../move/crates/move-analyzer/src/symbols.rs | 242 ++++++++-- .../tests/colon_colon_completion.exp | 116 ++--- .../crates/move-analyzer/tests/docstring.exp | 108 ++++- .../crates/move-analyzer/tests/docstring.ide | 12 + .../crates/move-analyzer/tests/dot_calls.exp | 45 +- .../move-analyzer/tests/dot_completion.exp | 68 +-- .../move-analyzer/tests/implicit_uses.exp | 10 +- .../crates/move-analyzer/tests/imports.exp | 2 +- .../crates/move-analyzer/tests/macros.exp | 36 +- .../crates/move-analyzer/tests/mod_access.exp | 12 +- .../crates/move-analyzer/tests/symbols.exp | 121 ++++- .../crates/move-analyzer/tests/symbols.ide | 28 ++ .../tests/symbols/sources/M1.move | 4 +- .../tests/symbols/sources/M4.move | 26 ++ .../tests/symbols/sources/M6.move | 36 +- .../src/expansion/alias_map_builder.rs | 6 +- .../crates/move-compiler/src/expansion/mod.rs | 1 + .../src/expansion/name_validation.rs | 424 +++++++++++++++++ .../src/expansion/path_expander.rs | 5 +- .../move-compiler/src/expansion/translate.rs | 442 +----------------- .../move-compiler/src/naming/translate.rs | 2 +- 26 files changed, 1115 insertions(+), 644 deletions(-) create mode 100644 external-crates/move/crates/move-compiler/src/expansion/name_validation.rs diff --git a/Cargo.lock b/Cargo.lock index 7c0f6725f79eb..7d5b3cb432971 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7090,6 +7090,7 @@ dependencies = [ "lsp-types", "move-command-line-common", "move-compiler", + "move-core-types", "move-ir-types", "move-package", "move-symbol-pool", diff --git a/external-crates/move/Cargo.lock b/external-crates/move/Cargo.lock index e2b63193b177f..1e6ae2e05369e 100644 --- a/external-crates/move/Cargo.lock +++ b/external-crates/move/Cargo.lock @@ -1611,6 +1611,7 @@ dependencies = [ "lsp-types", "move-command-line-common", "move-compiler", + "move-core-types", "move-ir-types", "move-package", "move-symbol-pool", diff --git a/external-crates/move/crates/move-analyzer/Cargo.toml b/external-crates/move/crates/move-analyzer/Cargo.toml index b1be5edc5fae5..0f7977bfca6f5 100644 --- a/external-crates/move/crates/move-analyzer/Cargo.toml +++ b/external-crates/move/crates/move-analyzer/Cargo.toml @@ -23,6 +23,7 @@ lsp-types.workspace = true move-command-line-common.workspace = true move-compiler.workspace = true move-ir-types.workspace = true +move-core-types.workspace = true move-package.workspace = true move-symbol-pool.workspace = true once_cell.workspace = true diff --git a/external-crates/move/crates/move-analyzer/editors/code/package.json b/external-crates/move/crates/move-analyzer/editors/code/package.json index 97bb29c43284d..43a0b2ebd39d5 100644 --- a/external-crates/move/crates/move-analyzer/editors/code/package.json +++ b/external-crates/move/crates/move-analyzer/editors/code/package.json @@ -5,7 +5,7 @@ "publisher": "mysten", "icon": "images/move.png", "license": "Apache-2.0", - "version": "1.0.12", + "version": "1.0.13", "preview": true, "repository": { "url": "https://github.com/MystenLabs/sui.git", diff --git a/external-crates/move/crates/move-analyzer/src/completions/utils.rs b/external-crates/move/crates/move-analyzer/src/completions/utils.rs index 93ac2d7f08686..926ef410e58b1 100644 --- a/external-crates/move/crates/move-analyzer/src/completions/utils.rs +++ b/external-crates/move/crates/move-analyzer/src/completions/utils.rs @@ -56,8 +56,8 @@ pub fn call_completion_item( ) -> CompletionItem { let sig_string = format!( "fun {}({}){}", - type_args_to_ide_string(type_args, /* verbose */ false), - type_list_to_ide_string(arg_types, /* verbose */ false), + type_args_to_ide_string(type_args, /* separate_lines */ false, /* verbose */ false), + type_list_to_ide_string(arg_types, /* separate_lines */ false, /* verbose */ false), ret_type_to_ide_str(ret_type, /* verbose */ false) ); // if it's a method call we omit the first argument which is guaranteed to be there as this is a @@ -83,8 +83,8 @@ pub fn call_completion_item( let macro_suffix = if is_macro { "!" } else { "" }; let label_details = Some(CompletionItemLabelDetails { detail: Some(format!( - " ({}::{})", - mod_ident_to_ide_string(mod_ident), + " ({}{})", + mod_ident_to_ide_string(mod_ident, None, true), function_name )), description: Some(sig_string), diff --git a/external-crates/move/crates/move-analyzer/src/symbols.rs b/external-crates/move/crates/move-analyzer/src/symbols.rs index bcb2eb2df7d5c..1a5732a69d4b6 100644 --- a/external-crates/move/crates/move-analyzer/src/symbols.rs +++ b/external-crates/move/crates/move-analyzer/src/symbols.rs @@ -90,10 +90,13 @@ use move_command_line_common::files::FileHash; use move_compiler::{ command_line::compiler::{construct_pre_compiled_lib, FullyCompiledProgram}, editions::{Edition, FeatureGate, Flavor}, - expansion::ast::{self as E, AbilitySet, ModuleIdent, ModuleIdent_, Value, Value_, Visibility}, + expansion::{ + ast::{self as E, AbilitySet, ModuleIdent, ModuleIdent_, Value, Value_, Visibility}, + name_validation::{IMPLICIT_STD_MEMBERS, IMPLICIT_STD_MODULES}, + }, linters::LintLevel, naming::ast::{DatatypeTypeParameter, StructFields, Type, TypeName_, Type_, VariantFields}, - parser::ast as P, + parser::ast::{self as P}, shared::{ files::{FileId, MappedFiles}, unique_map::UniqueMap, @@ -109,6 +112,7 @@ use move_compiler::{ unit_test::filter_test_members::UNIT_TEST_POISON_FUN_NAME, PASS_CFGIR, PASS_PARSER, PASS_TYPING, }; +use move_core_types::account_address::AccountAddress; use move_ir_types::location::*; use move_package::{ compilation::{build_plan::BuildPlan, compiled_package::ModuleFormat}, @@ -118,7 +122,7 @@ use move_package::{ use move_symbol_pool::Symbol; const MANIFEST_FILE_NAME: &str = "Move.toml"; - +const STD_LIB_PKG_ADDRESS: &str = "0x1"; type SourceFiles = BTreeMap; /// Information about the compiled package and data structures @@ -656,20 +660,35 @@ impl fmt::Display for DefInfo { ret_type, _, ) => { - let type_args_str = type_args_to_ide_string(type_args, /* verbose */ true); + const SINGLE_LINE_TYPE_ARGS_NUM: usize = 2; + // The strategy for displaying function signature is as follows: + // - if there are more than SINGLE_LINE_TYPE_ARGS_NUM type args, + // they are displayed on separate lines + // - "regular" args are always displayed on separate lines, which + // which is motivated by the fact that datatypes are displayed + // in a fully-qualified form (i.e., with package and module name), + // and that makes the function name already long and (likely) + // the length of each individual type also long (modulo primitive + // types of course, but I think we can live with that) + let type_args_str = type_args_to_ide_string( + type_args, + /* separate_lines */ type_args.len() > SINGLE_LINE_TYPE_ARGS_NUM, + /* verbose */ true, + ); + let args_str = typed_id_list_to_ide_string( + arg_names, arg_types, '(', ')', /* separate_lines */ true, + /* verbose */ true, + ); let ret_type_str = ret_type_to_ide_str(ret_type, /* verbose */ true); write!( f, - "{}{}fun {}::{}{}({}){}", + "{}{}fun {}{}{}{}{}", visibility_to_ide_string(visibility), fun_type_to_ide_string(fun_type), - mod_ident_to_ide_string(mod_ident), + mod_ident_to_ide_string(mod_ident, None, true), name, type_args_str, - typed_id_list_to_ide_string( - arg_names, arg_types, /* separate_lines */ false, - /* verbose */ true - ), + args_str, ret_type_str, ) } @@ -689,9 +708,9 @@ impl fmt::Display for DefInfo { if field_names.is_empty() { write!( f, - "{}struct {}::{}{}{} {{}}", + "{}struct {}{}{}{} {{}}", visibility_to_ide_string(visibility), - mod_ident_to_ide_string(mod_ident), + mod_ident_to_ide_string(mod_ident, Some(name), true), name, type_args_str, abilities_str, @@ -699,15 +718,17 @@ impl fmt::Display for DefInfo { } else { write!( f, - "{}struct {}::{}{}{} {{\n{}\n}}", + "{}struct {}{}{}{} {}", visibility_to_ide_string(visibility), - mod_ident_to_ide_string(mod_ident), + mod_ident_to_ide_string(mod_ident, Some(name), true), name, type_args_str, abilities_str, typed_id_list_to_ide_string( field_names, field_types, + '{', + '}', /* separate_lines */ true, /* verbose */ true ), @@ -721,9 +742,9 @@ impl fmt::Display for DefInfo { if variants.is_empty() { write!( f, - "{}enum {}::{}{}{} {{}}", + "{}enum {}{}{}{} {{}}", visibility_to_ide_string(visibility), - mod_ident_to_ide_string(mod_ident), + mod_ident_to_ide_string(mod_ident, Some(name), true), name, type_args_str, abilities_str, @@ -731,9 +752,9 @@ impl fmt::Display for DefInfo { } else { write!( f, - "{}enum {}::{}{}{} {{\n{}\n}}", + "{}enum {}{}{}{} {{\n{}\n}}", visibility_to_ide_string(visibility), - mod_ident_to_ide_string(mod_ident), + mod_ident_to_ide_string(mod_ident, Some(name), true), name, type_args_str, abilities_str, @@ -745,30 +766,36 @@ impl fmt::Display for DefInfo { if field_types.is_empty() { write!( f, - "{}::{}::{}", - mod_ident_to_ide_string(mod_ident), + "{}{}::{}", + mod_ident_to_ide_string(mod_ident, Some(enum_name), true), enum_name, name ) } else if *positional { write!( f, - "{}::{}::{}({})", - mod_ident_to_ide_string(mod_ident), + "{}{}::{}({})", + mod_ident_to_ide_string(mod_ident, Some(enum_name), true), enum_name, name, - type_list_to_ide_string(field_types, /* verbose */ true) + type_list_to_ide_string( + field_types, + /* separate_lines */ false, + /* verbose */ true + ) ) } else { write!( f, - "{}::{}::{}{{{}}}", - mod_ident_to_ide_string(mod_ident), + "{}{}::{}{}", + mod_ident_to_ide_string(mod_ident, Some(enum_name), true), enum_name, name, typed_id_list_to_ide_string( field_names, field_types, + '{', + '}', /* separate_lines */ false, /* verbose */ true, ), @@ -778,8 +805,8 @@ impl fmt::Display for DefInfo { Self::Field(mod_ident, struct_name, name, t, _) => { write!( f, - "{}::{}\n{}: {}", - mod_ident_to_ide_string(mod_ident), + "{}{}\n{}: {}", + mod_ident_to_ide_string(mod_ident, Some(struct_name), true), struct_name, name, type_to_ide_string(t, /* verbose */ true) @@ -916,11 +943,17 @@ fn visibility_to_ide_string(visibility: &Visibility) -> String { visibility_str } -pub fn type_args_to_ide_string(type_args: &[Type], verbose: bool) -> String { +pub fn type_args_to_ide_string(type_args: &[Type], separate_lines: bool, verbose: bool) -> String { let mut type_args_str = "".to_string(); if !type_args.is_empty() { type_args_str.push('<'); - type_args_str.push_str(&type_list_to_ide_string(type_args, verbose)); + if separate_lines { + type_args_str.push('\n'); + } + type_args_str.push_str(&type_list_to_ide_string(type_args, separate_lines, verbose)); + if separate_lines { + type_args_str.push('\n'); + } type_args_str.push('>'); } type_args_str @@ -939,10 +972,12 @@ fn datatype_type_args_to_ide_string(type_args: &[(Type, bool)], verbose: bool) - fn typed_id_list_to_ide_string( names: &[Name], types: &[Type], + list_start: char, + list_end: char, separate_lines: bool, verbose: bool, ) -> String { - names + let list = names .iter() .zip(types.iter()) .map(|(n, t)| { @@ -953,7 +988,12 @@ fn typed_id_list_to_ide_string( } }) .collect::>() - .join(if separate_lines { ",\n" } else { ", " }) + .join(if separate_lines { ",\n" } else { ", " }); + if separate_lines && !list.is_empty() { + format!("{}\n{}\n{}", list_start, list, list_end) + } else { + format!("{}{}{}", list_start, list, list_end) + } } pub fn type_to_ide_string(sp!(_, t): &Type, verbose: bool) -> String { @@ -969,32 +1009,47 @@ pub fn type_to_ide_string(sp!(_, t): &Type, verbose: bool) -> String { } Type_::Apply(_, sp!(_, type_name), ss) => match type_name { TypeName_::Multiple(_) => { - format!("({})", type_list_to_ide_string(ss, verbose)) + format!( + "({})", + type_list_to_ide_string(ss, /* separate_lines */ false, verbose) + ) } TypeName_::Builtin(name) => { if ss.is_empty() { format!("{}", name) } else { - format!("{}<{}>", name, type_list_to_ide_string(ss, verbose)) + format!( + "{}<{}>", + name, + type_list_to_ide_string(ss, /* separate_lines */ false, verbose) + ) } } - TypeName_::ModuleType(sp!(_, module_ident), struct_name) => { + TypeName_::ModuleType(sp!(_, mod_ident), datatype_name) => { let type_args = if ss.is_empty() { "".to_string() } else { - format!("<{}>", type_list_to_ide_string(ss, verbose)) + format!( + "<{}>", + type_list_to_ide_string(ss, /* separate_lines */ false, verbose) + ) }; if verbose { - format!("{}::{}{}", module_ident, struct_name, type_args,) + format!( + "{}{}{}", + mod_ident_to_ide_string(mod_ident, Some(&datatype_name.value()), true), + datatype_name, + type_args + ) } else { - struct_name.to_string() + datatype_name.to_string() } } }, Type_::Fun(args, ret) => { format!( "|{}| -> {}", - type_list_to_ide_string(args, verbose), + type_list_to_ide_string(args, /* separate_lines */ false, verbose), type_to_ide_string(ret, verbose) ) } @@ -1004,12 +1059,18 @@ pub fn type_to_ide_string(sp!(_, t): &Type, verbose: bool) -> String { } } -pub fn type_list_to_ide_string(types: &[Type], verbose: bool) -> String { +pub fn type_list_to_ide_string(types: &[Type], separate_lines: bool, verbose: bool) -> String { types .iter() - .map(|t| type_to_ide_string(t, verbose)) + .map(|t| { + if separate_lines { + format!("\t{}", type_to_ide_string(t, verbose)) + } else { + type_to_ide_string(t, verbose) + } + }) .collect::>() - .join(", ") + .join(if separate_lines { ",\n" } else { ", " }) } fn datatype_type_list_to_ide_string(types: &[(Type, bool)], verbose: bool) -> String { @@ -1127,15 +1188,59 @@ fn ast_value_to_ide_string(sp!(_, val): &Value) -> String { } } -pub fn mod_ident_to_ide_string(mod_ident: &E::ModuleIdent_) -> String { +/// Creates a string representing a module ID, either on it's owne as in `pkg::module` +/// or as part of a datatype or function type, in which it should be `pkg::module::`. +/// If it's part of the datatype, name of the datatype is passed in `datatype_name_opt`. +pub fn mod_ident_to_ide_string( + mod_ident: &ModuleIdent_, + datatype_name_opt: Option<&Symbol>, + is_access_chain_prefix: bool, // part of access chaing that should end with `::` +) -> String { use E::Address as A; + // the module ID is to be a prefix to a data + let suffix = if is_access_chain_prefix { "::" } else { "" }; match mod_ident.address { - A::Numerical { - name: None, value, .. - } => format!("{value}::{}", mod_ident.module).to_string(), - A::Numerical { name: Some(n), .. } | A::NamedUnassigned(n) => { - format!("{n}::{}", mod_ident.module).to_string() + A::Numerical { name, value, .. } => { + let pkg_name = match name { + Some(n) => n.to_string(), + None => value.to_string(), + }; + + let Ok(std_lib_pkg_address) = AccountAddress::from_hex_literal(STD_LIB_PKG_ADDRESS) + else { + // getting stdlib address did not work - use the whole thing + return format!("{pkg_name}::{}{}", mod_ident.module, suffix); + }; + if value.value.into_inner() != std_lib_pkg_address { + // it's not a stdlib package - use the whole thing + return format!("{pkg_name}::{}{}", mod_ident.module, suffix); + } + // try stripping both package and module if this conversion + // is for a datatype, oherwise try only stripping package + if let Some(datatype_name) = datatype_name_opt { + if IMPLICIT_STD_MEMBERS.iter().any( + |(implicit_mod_name, implicit_datatype_name, _)| { + mod_ident.module.value() == *implicit_mod_name + && datatype_name == implicit_datatype_name + }, + ) { + // strip both package and module (whether its meant to be + // part of access chain or not, if there is not module, + // there should be no `::` at the end) + return "".to_string(); + } + } + if IMPLICIT_STD_MODULES + .iter() + .any(|implicit_mod_name| mod_ident.module.value() == *implicit_mod_name) + { + // strip package + return format!("{}{}", mod_ident.module.value(), suffix); + } + // stripping prefix didn't work - use the whole thing + format!("{pkg_name}::{}{}", mod_ident.module, suffix) } + A::NamedUnassigned(n) => format!("{n}::{}", mod_ident.module).to_string(), } } @@ -2575,7 +2680,7 @@ fn get_mod_outer_defs( ); def_info.insert( mod_defs.name_loc, - DefInfo::Module(mod_ident_to_ide_string(&ident), doc_comment), + DefInfo::Module(mod_ident_to_ide_string(&ident, None, false), doc_comment), ); } @@ -2712,7 +2817,7 @@ fn extract_doc_string( // Detect the two different types of docstrings if line_before.starts_with("///") { while let Some(stripped_line) = line_before.strip_prefix("///") { - doc_string = format!("{}\n{}", stripped_line.trim(), doc_string); + doc_string = format!("{}\n{}", stripped_line, doc_string); if iter == 0 { break; } @@ -2721,7 +2826,13 @@ fn extract_doc_string( } } else if line_before.ends_with("*/") { let mut doc_string_found = false; - line_before = file_lines[iter].strip_suffix("*/").unwrap_or("").trim(); + // we need a line with preserved (whitespace) prefix so that + // we can trim other lines in the doc comment to the same prefix + let mut current_line = file_lines[iter].trim_end(); + current_line = current_line.strip_suffix("*/").unwrap_or(""); + let current_line_len = current_line.len(); + line_before = current_line.trim(); + let trimmed_len = current_line_len - line_before.len(); // Loop condition is a safe guard. while !doc_string_found { @@ -2745,7 +2856,36 @@ fn extract_doc_string( } iter -= 1; - line_before = file_lines[iter].trim(); + + // we need to trim the block comment line the same as + // the line containing its ending marker + current_line = file_lines[iter].trim_end(); + let first_non_whitespace = current_line + .chars() + .position(|c| !c.is_whitespace()) + .unwrap_or_default(); + if first_non_whitespace < trimmed_len { + // There is not enough whitespace to trim but trim as much as you can. + // The reason likely is that the docsting is misformatted, for example: + // ``` + // /** + // Properly formatted line + // Another properly formatted line + // Misformatted line + // */ + // ``` + // + // This will result in the following doc comment extracted: + // Properly formatted line + //``` + // Properly formatted line + // Another properly formatted line + //Misformatted line + //``` + line_before = current_line.trim_start(); + } else { + line_before = current_line[trimmed_len..].into(); + } } // No doc_string found - return String::new(); diff --git a/external-crates/move/crates/move-analyzer/tests/colon_colon_completion.exp b/external-crates/move/crates/move-analyzer/tests/colon_colon_completion.exp index fe9138fda0f88..84f7f87073f55 100644 --- a/external-crates/move/crates/move-analyzer/tests/colon_colon_completion.exp +++ b/external-crates/move/crates/move-analyzer/tests/colon_colon_completion.exp @@ -281,119 +281,119 @@ Method 'targ_type()' use line: 25, use_col: 17 Method 'and!()' INSERT TEXT: 'and!(${1:o}, |${2}| ${3})' - TARGET : '(std::option::and)' + TARGET : '(option::and)' TYPE : 'fun <$T, $U>(Option, |$T| -> Option): Option' Method 'and_ref!()' INSERT TEXT: 'and_ref!(${1:o}, |${2}| ${3})' - TARGET : '(std::option::and_ref)' + TARGET : '(option::and_ref)' TYPE : 'fun <$T, $U>(&Option, |&$T| -> Option): Option' Method 'borrow()' INSERT TEXT: 'borrow(${1:t})' - TARGET : '(std::option::borrow)' + TARGET : '(option::borrow)' TYPE : 'fun (&Option): &Element' Method 'borrow_mut()' INSERT TEXT: 'borrow_mut(${1:t})' - TARGET : '(std::option::borrow_mut)' + TARGET : '(option::borrow_mut)' TYPE : 'fun (&mut Option): &mut Element' Method 'borrow_with_default()' INSERT TEXT: 'borrow_with_default(${1:t}, ${2:default_ref})' - TARGET : '(std::option::borrow_with_default)' + TARGET : '(option::borrow_with_default)' TYPE : 'fun (&Option, &Element): &Element' Method 'contains()' INSERT TEXT: 'contains(${1:t}, ${2:e_ref})' - TARGET : '(std::option::contains)' + TARGET : '(option::contains)' TYPE : 'fun (&Option, &Element): bool' Method 'destroy!()' INSERT TEXT: 'destroy!(${1:o}, |${2}| ${3})' - TARGET : '(std::option::destroy)' + TARGET : '(option::destroy)' TYPE : 'fun <$T>(Option, |$T| -> ())' Method 'destroy_none()' INSERT TEXT: 'destroy_none(${1:t})' - TARGET : '(std::option::destroy_none)' + TARGET : '(option::destroy_none)' TYPE : 'fun (Option)' Method 'destroy_or!()' INSERT TEXT: 'destroy_or!(${1:o}, ${2:default})' - TARGET : '(std::option::destroy_or)' + TARGET : '(option::destroy_or)' TYPE : 'fun <$T>(Option, $T): $T' Method 'destroy_some()' INSERT TEXT: 'destroy_some(${1:t})' - TARGET : '(std::option::destroy_some)' + TARGET : '(option::destroy_some)' TYPE : 'fun (Option): Element' Method 'destroy_with_default()' INSERT TEXT: 'destroy_with_default(${1:t}, ${2:default})' - TARGET : '(std::option::destroy_with_default)' + TARGET : '(option::destroy_with_default)' TYPE : 'fun (Option, Element): Element' Method 'do!()' INSERT TEXT: 'do!(${1:o}, |${2}| ${3})' - TARGET : '(std::option::do)' + TARGET : '(option::do)' TYPE : 'fun <$T>(Option, |$T| -> ())' Method 'do_mut!()' INSERT TEXT: 'do_mut!(${1:o}, |${2}| ${3})' - TARGET : '(std::option::do_mut)' + TARGET : '(option::do_mut)' TYPE : 'fun <$T>(&mut Option, |&mut $T| -> ())' Method 'do_ref!()' INSERT TEXT: 'do_ref!(${1:o}, |${2}| ${3})' - TARGET : '(std::option::do_ref)' + TARGET : '(option::do_ref)' TYPE : 'fun <$T>(&Option, |&$T| -> ())' Method 'extract()' INSERT TEXT: 'extract(${1:t})' - TARGET : '(std::option::extract)' + TARGET : '(option::extract)' TYPE : 'fun (&mut Option): Element' Method 'fill()' INSERT TEXT: 'fill(${1:t}, ${2:e})' - TARGET : '(std::option::fill)' + TARGET : '(option::fill)' TYPE : 'fun (&mut Option, Element)' Method 'filter!()' INSERT TEXT: 'filter!(${1:o}, |${2}| ${3})' - TARGET : '(std::option::filter)' + TARGET : '(option::filter)' TYPE : 'fun <$T>(Option, |&$T| -> bool): Option' Method 'get_with_default()' INSERT TEXT: 'get_with_default(${1:t}, ${2:default})' - TARGET : '(std::option::get_with_default)' + TARGET : '(option::get_with_default)' TYPE : 'fun (&Option, Element): Element' Method 'is_none()' INSERT TEXT: 'is_none(${1:t})' - TARGET : '(std::option::is_none)' + TARGET : '(option::is_none)' TYPE : 'fun (&Option): bool' Method 'is_some()' INSERT TEXT: 'is_some(${1:t})' - TARGET : '(std::option::is_some)' + TARGET : '(option::is_some)' TYPE : 'fun (&Option): bool' Method 'is_some_and!()' INSERT TEXT: 'is_some_and!(${1:o}, |${2}| ${3})' - TARGET : '(std::option::is_some_and)' + TARGET : '(option::is_some_and)' TYPE : 'fun <$T>(&Option, |&$T| -> bool): bool' Method 'map!()' INSERT TEXT: 'map!(${1:o}, |${2}| ${3})' - TARGET : '(std::option::map)' + TARGET : '(option::map)' TYPE : 'fun <$T, $U>(Option, |$T| -> $U): Option' Method 'map_ref!()' INSERT TEXT: 'map_ref!(${1:o}, |${2}| ${3})' - TARGET : '(std::option::map_ref)' + TARGET : '(option::map_ref)' TYPE : 'fun <$T, $U>(&Option, |&$T| -> $U): Option' Method 'none()' INSERT TEXT: 'none()' - TARGET : '(std::option::none)' + TARGET : '(option::none)' TYPE : 'fun (): Option' Method 'or!()' INSERT TEXT: 'or!(${1:o}, ${2:default})' - TARGET : '(std::option::or)' + TARGET : '(option::or)' TYPE : 'fun <$T>(Option, Option): Option' Method 'some()' INSERT TEXT: 'some(${1:e})' - TARGET : '(std::option::some)' + TARGET : '(option::some)' TYPE : 'fun (Element): Option' Method 'swap()' INSERT TEXT: 'swap(${1:t}, ${2:e})' - TARGET : '(std::option::swap)' + TARGET : '(option::swap)' TYPE : 'fun (&mut Option, Element): Element' Method 'swap_or_fill()' INSERT TEXT: 'swap_or_fill(${1:t}, ${2:e})' - TARGET : '(std::option::swap_or_fill)' + TARGET : '(option::swap_or_fill)' TYPE : 'fun (&mut Option, Element): Option' Method 'to_vec()' INSERT TEXT: 'to_vec(${1:t})' - TARGET : '(std::option::to_vec)' + TARGET : '(option::to_vec)' TYPE : 'fun (Option): vector' -- test 12 ------------------- @@ -1011,119 +1011,119 @@ EnumMember 'SomeVariant' use line: 25, use_col: 16 Method 'and!()' INSERT TEXT: 'and!(${1:o}, |${2}| ${3})' - TARGET : '(std::option::and)' + TARGET : '(option::and)' TYPE : 'fun <$T, $U>(Option, |$T| -> Option): Option' Method 'and_ref!()' INSERT TEXT: 'and_ref!(${1:o}, |${2}| ${3})' - TARGET : '(std::option::and_ref)' + TARGET : '(option::and_ref)' TYPE : 'fun <$T, $U>(&Option, |&$T| -> Option): Option' Method 'borrow()' INSERT TEXT: 'borrow(${1:t})' - TARGET : '(std::option::borrow)' + TARGET : '(option::borrow)' TYPE : 'fun (&Option): &Element' Method 'borrow_mut()' INSERT TEXT: 'borrow_mut(${1:t})' - TARGET : '(std::option::borrow_mut)' + TARGET : '(option::borrow_mut)' TYPE : 'fun (&mut Option): &mut Element' Method 'borrow_with_default()' INSERT TEXT: 'borrow_with_default(${1:t}, ${2:default_ref})' - TARGET : '(std::option::borrow_with_default)' + TARGET : '(option::borrow_with_default)' TYPE : 'fun (&Option, &Element): &Element' Method 'contains()' INSERT TEXT: 'contains(${1:t}, ${2:e_ref})' - TARGET : '(std::option::contains)' + TARGET : '(option::contains)' TYPE : 'fun (&Option, &Element): bool' Method 'destroy!()' INSERT TEXT: 'destroy!(${1:o}, |${2}| ${3})' - TARGET : '(std::option::destroy)' + TARGET : '(option::destroy)' TYPE : 'fun <$T>(Option, |$T| -> ())' Method 'destroy_none()' INSERT TEXT: 'destroy_none(${1:t})' - TARGET : '(std::option::destroy_none)' + TARGET : '(option::destroy_none)' TYPE : 'fun (Option)' Method 'destroy_or!()' INSERT TEXT: 'destroy_or!(${1:o}, ${2:default})' - TARGET : '(std::option::destroy_or)' + TARGET : '(option::destroy_or)' TYPE : 'fun <$T>(Option, $T): $T' Method 'destroy_some()' INSERT TEXT: 'destroy_some(${1:t})' - TARGET : '(std::option::destroy_some)' + TARGET : '(option::destroy_some)' TYPE : 'fun (Option): Element' Method 'destroy_with_default()' INSERT TEXT: 'destroy_with_default(${1:t}, ${2:default})' - TARGET : '(std::option::destroy_with_default)' + TARGET : '(option::destroy_with_default)' TYPE : 'fun (Option, Element): Element' Method 'do!()' INSERT TEXT: 'do!(${1:o}, |${2}| ${3})' - TARGET : '(std::option::do)' + TARGET : '(option::do)' TYPE : 'fun <$T>(Option, |$T| -> ())' Method 'do_mut!()' INSERT TEXT: 'do_mut!(${1:o}, |${2}| ${3})' - TARGET : '(std::option::do_mut)' + TARGET : '(option::do_mut)' TYPE : 'fun <$T>(&mut Option, |&mut $T| -> ())' Method 'do_ref!()' INSERT TEXT: 'do_ref!(${1:o}, |${2}| ${3})' - TARGET : '(std::option::do_ref)' + TARGET : '(option::do_ref)' TYPE : 'fun <$T>(&Option, |&$T| -> ())' Method 'extract()' INSERT TEXT: 'extract(${1:t})' - TARGET : '(std::option::extract)' + TARGET : '(option::extract)' TYPE : 'fun (&mut Option): Element' Method 'fill()' INSERT TEXT: 'fill(${1:t}, ${2:e})' - TARGET : '(std::option::fill)' + TARGET : '(option::fill)' TYPE : 'fun (&mut Option, Element)' Method 'filter!()' INSERT TEXT: 'filter!(${1:o}, |${2}| ${3})' - TARGET : '(std::option::filter)' + TARGET : '(option::filter)' TYPE : 'fun <$T>(Option, |&$T| -> bool): Option' Method 'get_with_default()' INSERT TEXT: 'get_with_default(${1:t}, ${2:default})' - TARGET : '(std::option::get_with_default)' + TARGET : '(option::get_with_default)' TYPE : 'fun (&Option, Element): Element' Method 'is_none()' INSERT TEXT: 'is_none(${1:t})' - TARGET : '(std::option::is_none)' + TARGET : '(option::is_none)' TYPE : 'fun (&Option): bool' Method 'is_some()' INSERT TEXT: 'is_some(${1:t})' - TARGET : '(std::option::is_some)' + TARGET : '(option::is_some)' TYPE : 'fun (&Option): bool' Method 'is_some_and!()' INSERT TEXT: 'is_some_and!(${1:o}, |${2}| ${3})' - TARGET : '(std::option::is_some_and)' + TARGET : '(option::is_some_and)' TYPE : 'fun <$T>(&Option, |&$T| -> bool): bool' Method 'map!()' INSERT TEXT: 'map!(${1:o}, |${2}| ${3})' - TARGET : '(std::option::map)' + TARGET : '(option::map)' TYPE : 'fun <$T, $U>(Option, |$T| -> $U): Option' Method 'map_ref!()' INSERT TEXT: 'map_ref!(${1:o}, |${2}| ${3})' - TARGET : '(std::option::map_ref)' + TARGET : '(option::map_ref)' TYPE : 'fun <$T, $U>(&Option, |&$T| -> $U): Option' Method 'none()' INSERT TEXT: 'none()' - TARGET : '(std::option::none)' + TARGET : '(option::none)' TYPE : 'fun (): Option' Method 'or!()' INSERT TEXT: 'or!(${1:o}, ${2:default})' - TARGET : '(std::option::or)' + TARGET : '(option::or)' TYPE : 'fun <$T>(Option, Option): Option' Method 'some()' INSERT TEXT: 'some(${1:e})' - TARGET : '(std::option::some)' + TARGET : '(option::some)' TYPE : 'fun (Element): Option' Method 'swap()' INSERT TEXT: 'swap(${1:t}, ${2:e})' - TARGET : '(std::option::swap)' + TARGET : '(option::swap)' TYPE : 'fun (&mut Option, Element): Element' Method 'swap_or_fill()' INSERT TEXT: 'swap_or_fill(${1:t}, ${2:e})' - TARGET : '(std::option::swap_or_fill)' + TARGET : '(option::swap_or_fill)' TYPE : 'fun (&mut Option, Element): Option' Method 'to_vec()' INSERT TEXT: 'to_vec(${1:t})' - TARGET : '(std::option::to_vec)' + TARGET : '(option::to_vec)' TYPE : 'fun (Option): vector' -- test 30 ------------------- diff --git a/external-crates/move/crates/move-analyzer/tests/docstring.exp b/external-crates/move/crates/move-analyzer/tests/docstring.exp index d4fc4dd628c70..d87001f3970f6 100644 --- a/external-crates/move/crates/move-analyzer/tests/docstring.exp +++ b/external-crates/move/crates/move-analyzer/tests/docstring.exp @@ -5,12 +5,12 @@ Use: 'DocumentedStruct', start: 11, end: 27 Def: 'DocumentedStruct', line: 4, def char: 11 TypeDef: 'DocumentedStruct', line: 4, char: 11 On Hover: -struct Symbols::M6::DocumentedStruct has drop, store, key { +struct Symbols::M6::DocumentedStruct has drop, store { documented_field: u64 } -This is a documented struct -With a multi-line docstring + This is a documented struct + With a multi-line docstring -- test 1 ------------------- @@ -21,7 +21,7 @@ TypeDef: no info On Hover: const Symbols::M6::DOCUMENTED_CONSTANT: u64 = 42 -Constant containing the answer to the universe + Constant containing the answer to the universe -- test 2 ------------------- @@ -30,9 +30,11 @@ Use: 'unpack', start: 8, end: 14 Def: 'unpack', line: 14, def char: 8 TypeDef: no info On Hover: -fun Symbols::M6::unpack(s: Symbols::M6::DocumentedStruct): u64 +fun Symbols::M6::unpack( + s: Symbols::M6::DocumentedStruct +): u64 -A documented function that unpacks a DocumentedStruct + A documented function that unpacks a DocumentedStruct -- test 3 ------------------- @@ -49,12 +51,12 @@ Use: 'DocumentedStruct', start: 18, end: 34 Def: 'DocumentedStruct', line: 4, def char: 11 TypeDef: 'DocumentedStruct', line: 4, char: 11 On Hover: -struct Symbols::M6::DocumentedStruct has drop, store, key { +struct Symbols::M6::DocumentedStruct has drop, store { documented_field: u64 } -This is a documented struct -With a multi-line docstring + This is a documented struct + With a multi-line docstring -- test 5 ------------------- @@ -63,12 +65,12 @@ Use: 'DocumentedStruct', start: 12, end: 28 Def: 'DocumentedStruct', line: 4, def char: 11 TypeDef: 'DocumentedStruct', line: 4, char: 11 On Hover: -struct Symbols::M6::DocumentedStruct has drop, store, key { +struct Symbols::M6::DocumentedStruct has drop, store { documented_field: u64 } -This is a documented struct -With a multi-line docstring + This is a documented struct + With a multi-line docstring -- test 6 ------------------- @@ -80,7 +82,7 @@ On Hover: Symbols::M6::DocumentedStruct documented_field: u64 -A documented field + A documented field -- test 7 ------------------- @@ -100,11 +102,11 @@ On Hover: fun Symbols::M6::other_doc_struct(): Symbols::M7::OtherDocStruct -This is a multiline docstring + This is a multiline docstring -This docstring has empty lines. + This docstring has empty lines. -It uses the ** format instead of /// + It uses the ** format instead of /// @@ -114,7 +116,9 @@ Use: 'acq', start: 8, end: 11 Def: 'acq', line: 31, def char: 8 TypeDef: no info On Hover: -fun Symbols::M6::acq(uint: u64): u64 +fun Symbols::M6::acq( + uint: u64 +): u64 Asterix based single-line docstring @@ -129,7 +133,7 @@ struct Symbols::M7::OtherDocStruct has drop { some_field: u64 } -Documented struct in another module + Documented struct in another module -- test 11 ------------------- @@ -138,9 +142,11 @@ Use: 'create_other_struct', start: 21, end: 40 Def: 'create_other_struct', line: 9, def char: 15 TypeDef: no info On Hover: -public fun Symbols::M7::create_other_struct(v: u64): Symbols::M7::OtherDocStruct +public fun Symbols::M7::create_other_struct( + v: u64 +): Symbols::M7::OtherDocStruct -Documented initializer in another module + Documented initializer in another module -- test 12 ------------------- @@ -151,7 +157,7 @@ TypeDef: no info On Hover: const Symbols::M6::DOCUMENTED_CONSTANT: u64 = 42 -Constant containing the answer to the universe + Constant containing the answer to the universe -- test 13 ------------------- @@ -164,7 +170,7 @@ struct Symbols::M7::OtherDocStruct has drop { some_field: u64 } -Documented struct in another module + Documented struct in another module -- test 14 ------------------- @@ -183,3 +189,61 @@ TypeDef: no info On Hover: param: T +-- test 16 ------------------- +use line: 56, use_ndx: 0 +Use: 'code_block_doc_slash', start: 8, end: 28 +Def: 'code_block_doc_slash', line: 55, def char: 8 +TypeDef: no info +On Hover: +fun Symbols::M6::code_block_doc_slash() + + A documented function with code block + (should preserved indentation in the code block) + + ```rust + fun foo() { + 42 + } + ``` + + +-- test 17 ------------------- +use line: 68, use_ndx: 0 +Use: 'code_block_doc_star', start: 8, end: 27 +Def: 'code_block_doc_star', line: 67, def char: 8 +TypeDef: no info +On Hover: +fun Symbols::M6::code_block_doc_star() + + + A documented function with code block + (should preserved indentation in the code block) + + ```rust + fun foo() { + 42 + } + ``` + + + +-- test 18 ------------------- +use line: 80, use_ndx: 0 +Use: 'misformatted_docstring', start: 8, end: 30 +Def: 'misformatted_docstring', line: 79, def char: 8 +TypeDef: no info +On Hover: +fun Symbols::M6::misformatted_docstring() + + + Misformatted docstring to have fewer whitespace in the body than + at the ending marker. + + +Beginning of this string should not disappear. + +Beginning of this string should not disappear either. + + + + diff --git a/external-crates/move/crates/move-analyzer/tests/docstring.ide b/external-crates/move/crates/move-analyzer/tests/docstring.ide index 1aff49d4e0c8e..0b51be5fbcdcc 100644 --- a/external-crates/move/crates/move-analyzer/tests/docstring.ide +++ b/external-crates/move/crates/move-analyzer/tests/docstring.ide @@ -68,6 +68,18 @@ { "use_line": 44, "use_ndx": 2 + }, + { + "use_line": 56, + "use_ndx": 0 + }, + { + "use_line": 68, + "use_ndx": 0 + }, + { + "use_line": 80, + "use_ndx": 0 } ] } diff --git a/external-crates/move/crates/move-analyzer/tests/dot_calls.exp b/external-crates/move/crates/move-analyzer/tests/dot_calls.exp index d7f5f78ba1225..ff4b7ad89d16f 100644 --- a/external-crates/move/crates/move-analyzer/tests/dot_calls.exp +++ b/external-crates/move/crates/move-analyzer/tests/dot_calls.exp @@ -13,7 +13,9 @@ Use: 'foo', start: 25, end: 28 Def: 'foo', line: 13, def char: 15 TypeDef: no info On Hover: -public fun Move2024::M1::foo(s: &Move2024::M1::SomeStruct): u64 +public fun Move2024::M1::foo( + s: &Move2024::M1::SomeStruct +): u64 -- test 2 ------------------- use line: 3, use_ndx: 2 @@ -31,7 +33,9 @@ Use: 'f1', start: 43, end: 45 Def: 'foo', line: 13, def char: 15 TypeDef: no info On Hover: -public fun Move2024::M1::foo(s: &Move2024::M1::SomeStruct): u64 +public fun Move2024::M1::foo( + s: &Move2024::M1::SomeStruct +): u64 -- test 4 ------------------- use line: 4, use_ndx: 0 @@ -47,7 +51,9 @@ Use: 'foo', start: 33, end: 36 Def: 'foo', line: 13, def char: 15 TypeDef: no info On Hover: -public fun Move2024::M1::foo(s: &Move2024::M1::SomeStruct): u64 +public fun Move2024::M1::foo( + s: &Move2024::M1::SomeStruct +): u64 -- test 6 ------------------- use line: 4, use_ndx: 2 @@ -65,7 +71,9 @@ Use: 'f2', start: 51, end: 53 Def: 'foo', line: 13, def char: 15 TypeDef: no info On Hover: -public fun Move2024::M1::foo(s: &Move2024::M1::SomeStruct): u64 +public fun Move2024::M1::foo( + s: &Move2024::M1::SomeStruct +): u64 -- test 8 ------------------- use line: 27, use_ndx: 0 @@ -81,7 +89,10 @@ Use: 'bar', start: 22, end: 25 Def: 'bar', line: 17, def char: 15 TypeDef: no info On Hover: -public fun Move2024::M1::bar(s: &Move2024::M1::SomeStruct, v: u64): u64 +public fun Move2024::M1::bar( + s: &Move2024::M1::SomeStruct, + v: u64 +): u64 -- test 10 ------------------- use line: 27, use_ndx: 2 @@ -107,7 +118,10 @@ Use: 'f3', start: 54, end: 56 Def: 'bar', line: 17, def char: 15 TypeDef: no info On Hover: -public fun Move2024::M1::bar(s: &Move2024::M1::SomeStruct, v: u64): u64 +public fun Move2024::M1::bar( + s: &Move2024::M1::SomeStruct, + v: u64 +): u64 -- test 13 ------------------- use line: 30, use_ndx: 0 @@ -123,7 +137,10 @@ Use: 'bar', start: 20, end: 23 Def: 'bar', line: 17, def char: 15 TypeDef: no info On Hover: -public fun Move2024::M1::bar(s: &Move2024::M1::SomeStruct, v: u64): u64 +public fun Move2024::M1::bar( + s: &Move2024::M1::SomeStruct, + v: u64 +): u64 -- test 15 ------------------- use line: 30, use_ndx: 2 @@ -141,7 +158,10 @@ Use: 'f4', start: 43, end: 45 Def: 'bar', line: 17, def char: 15 TypeDef: no info On Hover: -public fun Move2024::M1::bar(s: &Move2024::M1::SomeStruct, v: u64): u64 +public fun Move2024::M1::bar( + s: &Move2024::M1::SomeStruct, + v: u64 +): u64 -- test 17 ------------------- use line: 34, use_ndx: 0 @@ -157,7 +177,9 @@ Use: 'f1', start: 28, end: 30 Def: 'foo', line: 13, def char: 15 TypeDef: no info On Hover: -public fun Move2024::M1::foo(s: &Move2024::M1::SomeStruct): u64 +public fun Move2024::M1::foo( + s: &Move2024::M1::SomeStruct +): u64 -- test 19 ------------------- use line: 35, use_ndx: 0 @@ -173,7 +195,10 @@ Use: 'f3', start: 28, end: 30 Def: 'bar', line: 17, def char: 15 TypeDef: no info On Hover: -public fun Move2024::M1::bar(s: &Move2024::M1::SomeStruct, v: u64): u64 +public fun Move2024::M1::bar( + s: &Move2024::M1::SomeStruct, + v: u64 +): u64 -- test 21 ------------------- use line: 35, use_ndx: 2 diff --git a/external-crates/move/crates/move-analyzer/tests/dot_completion.exp b/external-crates/move/crates/move-analyzer/tests/dot_completion.exp index 547bc99ee900a..3a43095ec41d4 100644 --- a/external-crates/move/crates/move-analyzer/tests/dot_completion.exp +++ b/external-crates/move/crates/move-analyzer/tests/dot_completion.exp @@ -54,115 +54,115 @@ Method 'test()' use line: 4, use_col: 10 Method 'all!()' INSERT TEXT: 'all!(|${1}| ${2})' - TARGET : '(std::vector::all)' + TARGET : '(vector::all)' TYPE : 'fun <$T>(&vector<$T>, |&$T| -> bool): bool' Method 'any!()' INSERT TEXT: 'any!(|${1}| ${2})' - TARGET : '(std::vector::any)' + TARGET : '(vector::any)' TYPE : 'fun <$T>(&vector<$T>, |&$T| -> bool): bool' Method 'append()' INSERT TEXT: 'append(${1:other})' - TARGET : '(std::vector::append)' + TARGET : '(vector::append)' TYPE : 'fun (&mut vector, vector)' Method 'borrow()' INSERT TEXT: 'borrow(${1:i})' - TARGET : '(std::vector::borrow)' + TARGET : '(vector::borrow)' TYPE : 'fun (&vector, u64): &Element' Method 'borrow_mut()' INSERT TEXT: 'borrow_mut(${1:i})' - TARGET : '(std::vector::borrow_mut)' + TARGET : '(vector::borrow_mut)' TYPE : 'fun (&mut vector, u64): &mut Element' Method 'contains()' INSERT TEXT: 'contains(${1:e})' - TARGET : '(std::vector::contains)' + TARGET : '(vector::contains)' TYPE : 'fun (&vector, &Element): bool' Method 'count!()' INSERT TEXT: 'count!(|${1}| ${2})' - TARGET : '(std::vector::count)' + TARGET : '(vector::count)' TYPE : 'fun <$T>(&vector<$T>, |&$T| -> bool): u64' Method 'destroy!()' INSERT TEXT: 'destroy!(|${1}| ${2})' - TARGET : '(std::vector::destroy)' + TARGET : '(vector::destroy)' TYPE : 'fun <$T>(vector<$T>, |$T| -> ())' Method 'destroy_empty()' INSERT TEXT: 'destroy_empty()' - TARGET : '(std::vector::destroy_empty)' + TARGET : '(vector::destroy_empty)' TYPE : 'fun (vector)' Method 'do!()' INSERT TEXT: 'do!(|${1}| ${2})' - TARGET : '(std::vector::do)' + TARGET : '(vector::do)' TYPE : 'fun <$T>(vector<$T>, |$T| -> ())' Method 'do_mut!()' INSERT TEXT: 'do_mut!(|${1}| ${2})' - TARGET : '(std::vector::do_mut)' + TARGET : '(vector::do_mut)' TYPE : 'fun <$T>(&mut vector<$T>, |&mut $T| -> ())' Method 'do_ref!()' INSERT TEXT: 'do_ref!(|${1}| ${2})' - TARGET : '(std::vector::do_ref)' + TARGET : '(vector::do_ref)' TYPE : 'fun <$T>(&vector<$T>, |&$T| -> ())' Method 'filter!()' INSERT TEXT: 'filter!(|${1}| ${2})' - TARGET : '(std::vector::filter)' + TARGET : '(vector::filter)' TYPE : 'fun <$T>(vector<$T>, |&$T| -> bool): vector<$T>' Method 'find_index!()' INSERT TEXT: 'find_index!(|${1}| ${2})' - TARGET : '(std::vector::find_index)' + TARGET : '(vector::find_index)' TYPE : 'fun <$T>(&vector<$T>, |&$T| -> bool): Option' Method 'fold!()' INSERT TEXT: 'fold!(${1:init}, |${2}, ${3}| ${4})' - TARGET : '(std::vector::fold)' + TARGET : '(vector::fold)' TYPE : 'fun <$T, $Acc>(vector<$T>, $Acc, |$Acc, $T| -> $Acc): $Acc' Method 'index_of()' INSERT TEXT: 'index_of(${1:e})' - TARGET : '(std::vector::index_of)' + TARGET : '(vector::index_of)' TYPE : 'fun (&vector, &Element): (bool, u64)' Method 'insert()' INSERT TEXT: 'insert(${1:e}, ${2:i})' - TARGET : '(std::vector::insert)' + TARGET : '(vector::insert)' TYPE : 'fun (&mut vector, Element, u64)' Method 'is_empty()' INSERT TEXT: 'is_empty()' - TARGET : '(std::vector::is_empty)' + TARGET : '(vector::is_empty)' TYPE : 'fun (&vector): bool' Method 'length()' INSERT TEXT: 'length()' - TARGET : '(std::vector::length)' + TARGET : '(vector::length)' TYPE : 'fun (&vector): u64' Method 'map!()' INSERT TEXT: 'map!(|${1}| ${2})' - TARGET : '(std::vector::map)' + TARGET : '(vector::map)' TYPE : 'fun <$T, $U>(vector<$T>, |$T| -> $U): vector<$U>' Method 'map_ref!()' INSERT TEXT: 'map_ref!(|${1}| ${2})' - TARGET : '(std::vector::map_ref)' + TARGET : '(vector::map_ref)' TYPE : 'fun <$T, $U>(&vector<$T>, |&$T| -> $U): vector<$U>' Method 'partition!()' INSERT TEXT: 'partition!(|${1}| ${2})' - TARGET : '(std::vector::partition)' + TARGET : '(vector::partition)' TYPE : 'fun <$T>(vector<$T>, |&$T| -> bool): (vector<$T>, vector<$T>)' Method 'pop_back()' INSERT TEXT: 'pop_back()' - TARGET : '(std::vector::pop_back)' + TARGET : '(vector::pop_back)' TYPE : 'fun (&mut vector): Element' Method 'push_back()' INSERT TEXT: 'push_back(${1:e})' - TARGET : '(std::vector::push_back)' + TARGET : '(vector::push_back)' TYPE : 'fun (&mut vector, Element)' Method 'remove()' INSERT TEXT: 'remove(${1:i})' - TARGET : '(std::vector::remove)' + TARGET : '(vector::remove)' TYPE : 'fun (&mut vector, u64): Element' Method 'reverse()' INSERT TEXT: 'reverse()' - TARGET : '(std::vector::reverse)' + TARGET : '(vector::reverse)' TYPE : 'fun (&mut vector)' Method 'swap()' INSERT TEXT: 'swap(${1:i}, ${2:j})' - TARGET : '(std::vector::swap)' + TARGET : '(vector::swap)' TYPE : 'fun (&mut vector, u64, u64)' Method 'swap_remove()' INSERT TEXT: 'swap_remove(${1:i})' - TARGET : '(std::vector::swap_remove)' + TARGET : '(vector::swap_remove)' TYPE : 'fun (&mut vector, u64): Element' Method 'to_ascii_string()' INSERT TEXT: 'to_ascii_string()' @@ -182,26 +182,26 @@ Method 'try_to_string()' TYPE : 'fun (vector): Option' Method 'zip_do!()' INSERT TEXT: 'zip_do!(${1:v2}, |${2}, ${3}| ${4})' - TARGET : '(std::vector::zip_do)' + TARGET : '(vector::zip_do)' TYPE : 'fun <$T1, $T2>(vector<$T1>, vector<$T2>, |$T1, $T2| -> ())' Method 'zip_do_mut!()' INSERT TEXT: 'zip_do_mut!(${1:v2}, |${2}, ${3}| ${4})' - TARGET : '(std::vector::zip_do_mut)' + TARGET : '(vector::zip_do_mut)' TYPE : 'fun <$T1, $T2>(&mut vector<$T1>, &mut vector<$T2>, |&mut $T1, &mut $T2| -> ())' Method 'zip_do_ref!()' INSERT TEXT: 'zip_do_ref!(${1:v2}, |${2}, ${3}| ${4})' - TARGET : '(std::vector::zip_do_ref)' + TARGET : '(vector::zip_do_ref)' TYPE : 'fun <$T1, $T2>(&vector<$T1>, &vector<$T2>, |&$T1, &$T2| -> ())' Method 'zip_do_reverse!()' INSERT TEXT: 'zip_do_reverse!(${1:v2}, |${2}, ${3}| ${4})' - TARGET : '(std::vector::zip_do_reverse)' + TARGET : '(vector::zip_do_reverse)' TYPE : 'fun <$T1, $T2>(vector<$T1>, vector<$T2>, |$T1, $T2| -> ())' Method 'zip_map!()' INSERT TEXT: 'zip_map!(${1:v2}, |${2}, ${3}| ${4})' - TARGET : '(std::vector::zip_map)' + TARGET : '(vector::zip_map)' TYPE : 'fun <$T1, $T2, $U>(vector<$T1>, vector<$T2>, |$T1, $T2| -> $U): vector<$U>' Method 'zip_map_ref!()' INSERT TEXT: 'zip_map_ref!(${1:v2}, |${2}, ${3}| ${4})' - TARGET : '(std::vector::zip_map_ref)' + TARGET : '(vector::zip_map_ref)' TYPE : 'fun <$T1, $T2, $U>(&vector<$T1>, &vector<$T2>, |&$T1, &$T2| -> $U): vector<$U>' diff --git a/external-crates/move/crates/move-analyzer/tests/implicit_uses.exp b/external-crates/move/crates/move-analyzer/tests/implicit_uses.exp index 1ae18e4ecb1c0..0ec6c07129038 100644 --- a/external-crates/move/crates/move-analyzer/tests/implicit_uses.exp +++ b/external-crates/move/crates/move-analyzer/tests/implicit_uses.exp @@ -5,12 +5,12 @@ Use: 'Option', start: 13, end: 19 Def: 'Option', line: 7, def char: 18 TypeDef: 'Option', line: 7, char: 18 On Hover: -public struct std::option::Option has copy, drop, store { +public struct Option has copy, drop, store { vec: vector } -Abstraction of a value that may or may not be present. Implemented with a vector of size -zero or one because Move bytecode does not have ADTs. + Abstraction of a value that may or may not be present. Implemented with a vector of size + zero or one because Move bytecode does not have ADTs. -- test 1 ------------------- @@ -19,8 +19,8 @@ Use: 'option', start: 26, end: 32 Def: 'option', line: 4, def char: 12 TypeDef: no info On Hover: -module std::option +module option -This module defines the Option type and its methods to represent and handle an optional value. + This module defines the Option type and its methods to represent and handle an optional value. diff --git a/external-crates/move/crates/move-analyzer/tests/imports.exp b/external-crates/move/crates/move-analyzer/tests/imports.exp index 9fde0cfd229db..60e3f1f7a3f80 100644 --- a/external-crates/move/crates/move-analyzer/tests/imports.exp +++ b/external-crates/move/crates/move-analyzer/tests/imports.exp @@ -7,7 +7,7 @@ TypeDef: no info On Hover: module Symbols::M9 -A module doc comment + A module doc comment -- test 1 ------------------- diff --git a/external-crates/move/crates/move-analyzer/tests/macros.exp b/external-crates/move/crates/move-analyzer/tests/macros.exp index d0e0324404abf..105506c083abc 100644 --- a/external-crates/move/crates/move-analyzer/tests/macros.exp +++ b/external-crates/move/crates/move-analyzer/tests/macros.exp @@ -38,10 +38,10 @@ Use: 'n foo(', start: 12, end: 18 Def: 'vector', line: 6, def char: 12 TypeDef: no info On Hover: -module std::vector +module vector -A variable-sized container that can hold any type. Indexing is 0-based, and -vectors are growable. This module has many native functions. + A variable-sized container that can hold any type. Indexing is 0-based, and + vectors are growable. This module has many native functions. -- test 1 ------------------- @@ -50,7 +50,10 @@ Use: 'foo', start: 14, end: 17 Def: 'foo', line: 6, def char: 14 TypeDef: no info On Hover: -macro fun Macros::macros::foo($i: u64, $body: |u64| -> u64): u64 +macro fun Macros::macros::foo( + $i: u64, + $body: |u64| -> u64 +): u64 -- test 2 ------------------- use line: 7, use_ndx: 2 @@ -74,7 +77,10 @@ Use: 'bar', start: 14, end: 17 Def: 'bar', line: 14, def char: 14 TypeDef: no info On Hover: -macro fun Macros::macros::bar($i: Macros::macros::SomeStruct, $body: |Macros::macros::SomeStruct| -> Macros::macros::SomeStruct): Macros::macros::SomeStruct +macro fun Macros::macros::bar( + $i: Macros::macros::SomeStruct, + $body: |Macros::macros::SomeStruct| -> Macros::macros::SomeStruct +): Macros::macros::SomeStruct -- test 5 ------------------- use line: 15, use_ndx: 1 @@ -138,7 +144,10 @@ Use: 'for_each', start: 14, end: 22 Def: 'for_each', line: 18, def char: 14 TypeDef: no info On Hover: -macro fun Macros::macros::for_each<$T>($v: &vector<$T>, $body: |&$T| -> ()) +macro fun Macros::macros::for_each<$T>( + $v: &vector<$T>, + $body: |&$T| -> () +) -- test 12 ------------------- use line: 19, use_ndx: 1 @@ -194,7 +203,10 @@ Use: 'foo', start: 24, end: 27 Def: 'foo', line: 6, def char: 14 TypeDef: no info On Hover: -macro fun Macros::macros::foo($i: u64, $body: |u64| -> u64): u64 +macro fun Macros::macros::foo( + $i: u64, + $body: |u64| -> u64 +): u64 -- test 19 ------------------- use line: 33, use_ndx: 2 @@ -234,7 +246,10 @@ Use: 'foo', start: 68, end: 71 Def: 'foo', line: 6, def char: 14 TypeDef: no info On Hover: -macro fun Macros::macros::foo($i: u64, $body: |u64| -> u64): u64 +macro fun Macros::macros::foo( + $i: u64, + $body: |u64| -> u64 +): u64 -- test 24 ------------------- use line: 38, use_ndx: 8 @@ -282,7 +297,10 @@ Use: 'feach', start: 11, end: 16 Def: 'for_each', line: 18, def char: 14 TypeDef: no info On Hover: -macro fun Macros::macros::for_each<$T>($v: &vector<$T>, $body: |&$T| -> ()) +macro fun Macros::macros::for_each<$T>( + $v: &vector<$T>, + $body: |&$T| -> () +) -- test 30 ------------------- use line: 52, use_ndx: 2 diff --git a/external-crates/move/crates/move-analyzer/tests/mod_access.exp b/external-crates/move/crates/move-analyzer/tests/mod_access.exp index 6296b548ccba4..a11681c4f0db0 100644 --- a/external-crates/move/crates/move-analyzer/tests/mod_access.exp +++ b/external-crates/move/crates/move-analyzer/tests/mod_access.exp @@ -7,7 +7,7 @@ TypeDef: no info On Hover: module Symbols::M9 -A module doc comment + A module doc comment -- test 1 ------------------- @@ -18,7 +18,7 @@ TypeDef: no info On Hover: module Symbols::M9 -A module doc comment + A module doc comment -- test 2 ------------------- @@ -29,7 +29,7 @@ TypeDef: no info On Hover: module Symbols::M9 -A module doc comment + A module doc comment -- test 3 ------------------- @@ -40,7 +40,7 @@ TypeDef: no info On Hover: module Symbols::M9 -A module doc comment + A module doc comment -- test 4 ------------------- @@ -51,7 +51,7 @@ TypeDef: no info On Hover: module Symbols::M9 -A module doc comment + A module doc comment -- test 5 ------------------- @@ -86,6 +86,6 @@ TypeDef: no info On Hover: module Symbols::M9 -A module doc comment + A module doc comment diff --git a/external-crates/move/crates/move-analyzer/tests/symbols.exp b/external-crates/move/crates/move-analyzer/tests/symbols.exp index 7e06c2c210085..b87b53013fcda 100644 --- a/external-crates/move/crates/move-analyzer/tests/symbols.exp +++ b/external-crates/move/crates/move-analyzer/tests/symbols.exp @@ -5,7 +5,7 @@ Use: 'SomeStruct', start: 11, end: 21 Def: 'SomeStruct', line: 2, def char: 11 TypeDef: 'SomeStruct', line: 2, char: 11 On Hover: -struct Symbols::M1::SomeStruct has drop, store, key { +struct Symbols::M1::SomeStruct has drop, store { some_field: u64 } @@ -23,7 +23,9 @@ Use: 'unpack', start: 8, end: 14 Def: 'unpack', line: 9, def char: 8 TypeDef: no info On Hover: -fun Symbols::M1::unpack(s: Symbols::M1::SomeStruct): u64 +fun Symbols::M1::unpack( + s: Symbols::M1::SomeStruct +): u64 -- test 3 ------------------- use line: 10, use_ndx: 1 @@ -39,7 +41,7 @@ Use: 'SomeStruct', start: 18, end: 28 Def: 'SomeStruct', line: 2, def char: 11 TypeDef: 'SomeStruct', line: 2, char: 11 On Hover: -struct Symbols::M1::SomeStruct has drop, store, key { +struct Symbols::M1::SomeStruct has drop, store { some_field: u64 } @@ -49,7 +51,7 @@ Use: 'SomeStruct', start: 12, end: 22 Def: 'SomeStruct', line: 2, def char: 11 TypeDef: 'SomeStruct', line: 2, char: 11 On Hover: -struct Symbols::M1::SomeStruct has drop, store, key { +struct Symbols::M1::SomeStruct has drop, store { some_field: u64 } @@ -92,7 +94,7 @@ Use: 'SomeStruct', start: 16, end: 26 Def: 'SomeStruct', line: 2, def char: 11 TypeDef: 'SomeStruct', line: 2, char: 11 On Hover: -struct Symbols::M1::SomeStruct has drop, store, key { +struct Symbols::M1::SomeStruct has drop, store { some_field: u64 } @@ -102,7 +104,7 @@ Use: 'SomeStruct', start: 18, end: 28 Def: 'SomeStruct', line: 2, def char: 11 TypeDef: 'SomeStruct', line: 2, char: 11 On Hover: -struct Symbols::M1::SomeStruct has drop, store, key { +struct Symbols::M1::SomeStruct has drop, store { some_field: u64 } @@ -139,7 +141,9 @@ Use: 'some_other_struct', start: 21, end: 38 Def: 'some_other_struct', line: 6, def char: 15 TypeDef: no info On Hover: -public fun Symbols::M2::some_other_struct(v: u64): Symbols::M2::SomeOtherStruct +public fun Symbols::M2::some_other_struct( + v: u64 +): Symbols::M2::SomeOtherStruct -- test 16 ------------------- use line: 26, use_ndx: 2 @@ -165,7 +169,9 @@ Use: 'acq', start: 8, end: 11 Def: 'acq', line: 34, def char: 8 TypeDef: no info On Hover: -fun Symbols::M1::acq(uint: u64): u64 +fun Symbols::M1::acq( + uint: u64 +): u64 -- test 19 ------------------- use line: 41, use_ndx: 2 @@ -197,7 +203,7 @@ Use: 'SomeStruct', start: 15, end: 25 Def: 'SomeStruct', line: 2, def char: 11 TypeDef: 'SomeStruct', line: 2, char: 11 On Hover: -struct Symbols::M1::SomeStruct has drop, store, key { +struct Symbols::M1::SomeStruct has drop, store { some_field: u64 } @@ -207,7 +213,7 @@ Use: 'SomeStruct', start: 27, end: 37 Def: 'SomeStruct', line: 2, def char: 11 TypeDef: 'SomeStruct', line: 2, char: 11 On Hover: -struct Symbols::M1::SomeStruct has drop, store, key { +struct Symbols::M1::SomeStruct has drop, store { some_field: u64 } @@ -258,7 +264,10 @@ Use: 'ret', start: 8, end: 11 Def: 'ret', line: 61, def char: 8 TypeDef: no info On Hover: -fun Symbols::M1::ret(p1: bool, p2: u64): u64 +fun Symbols::M1::ret( + p1: bool, + p2: u64 +): u64 -- test 30 ------------------- use line: 64, use_ndx: 0 @@ -605,3 +614,93 @@ TypeDef: no info On Hover: const Symbols::M5::SOME_CONST: u64 = 7 +-- test 11 ------------------- +use line: 58, use_ndx: 0 +Use: 'long_param_list', start: 15, end: 30 +Def: 'long_param_list', line: 57, def char: 15 +TypeDef: no info +On Hover: +public fun Symbols::M5::long_param_list( + foo: u64, + bar: u64, + baz: u64, + qux: u64 +) + +-- test 12 ------------------- +use line: 60, use_ndx: 0 +Use: 'short_type_param_list', start: 15, end: 36 +Def: 'short_type_param_list', line: 59, def char: 15 +TypeDef: no info +On Hover: +public fun Symbols::M5::short_type_param_list() + +-- test 13 ------------------- +use line: 62, use_ndx: 0 +Use: 'long_type_param_list', start: 15, end: 35 +Def: 'long_type_param_list', line: 61, def char: 15 +TypeDef: no info +On Hover: +public fun Symbols::M5::long_type_param_list< + TYPE1, + TYPE2, + TYPE3 +>() + +-- test 14 ------------------- +use line: 64, use_ndx: 0 +Use: 'combined_short_type_param_list', start: 15, end: 45 +Def: 'combined_short_type_param_list', line: 63, def char: 15 +TypeDef: no info +On Hover: +public fun Symbols::M5::combined_short_type_param_list( + foo: u64, + bar: u64, + baz: u64, + qux: u64 +) + +-- test 15 ------------------- +use line: 68, use_ndx: 0 +Use: 'combined_long_type_param_list', start: 15, end: 44 +Def: 'combined_long_type_param_list', line: 67, def char: 15 +TypeDef: no info +On Hover: +public fun Symbols::M5::combined_long_type_param_list< + TYPE1, + TYPE2, + TYPE3 +>( + foo: u64, + bar: u64, + baz: u64, + qux: u64 +) + +-- test 16 ------------------- +use line: 75, use_ndx: 2 +Use: 'extract', start: 37, end: 44 +Def: 'extract', line: 80, def char: 15 +TypeDef: no info +On Hover: +public fun option::extract( + t: &mut Option +): Element + + Convert a `some` option to a `none` by removing and returning the value stored inside `t` + Aborts if `t` does not hold a value + + +-- test 17 ------------------- +use line: 78, use_ndx: 1 +Use: 'singleton', start: 21, end: 30 +Def: 'singleton', line: 64, def char: 15 +TypeDef: no info +On Hover: +public fun vector::singleton( + e: Element +): vector + + Return an vector of size one containing element `e`. + + diff --git a/external-crates/move/crates/move-analyzer/tests/symbols.ide b/external-crates/move/crates/move-analyzer/tests/symbols.ide index bce3e5c8683f6..7bf47ee1ef15a 100644 --- a/external-crates/move/crates/move-analyzer/tests/symbols.ide +++ b/external-crates/move/crates/move-analyzer/tests/symbols.ide @@ -295,6 +295,34 @@ { "use_line": 56, "use_ndx": 0 + }, + { + "use_line": 58, + "use_ndx": 0 + }, + { + "use_line": 60, + "use_ndx": 0 + }, + { + "use_line": 62, + "use_ndx": 0 + }, + { + "use_line": 64, + "use_ndx": 0 + }, + { + "use_line": 68, + "use_ndx": 0 + }, + { + "use_line": 75, + "use_ndx": 2 + }, + { + "use_line": 78, + "use_ndx": 1 } ] } diff --git a/external-crates/move/crates/move-analyzer/tests/symbols/sources/M1.move b/external-crates/move/crates/move-analyzer/tests/symbols/sources/M1.move index b1cd4139af6d8..6d7e40b0fa386 100644 --- a/external-crates/move/crates/move-analyzer/tests/symbols/sources/M1.move +++ b/external-crates/move/crates/move-analyzer/tests/symbols/sources/M1.move @@ -1,6 +1,6 @@ module Symbols::M1 { - struct SomeStruct has key, drop, store { + struct SomeStruct has drop, store { some_field: u64, } @@ -85,7 +85,7 @@ module Symbols::M1 { *tmp } - struct OuterStruct has key, drop { + struct OuterStruct has drop { some_struct: SomeStruct, } diff --git a/external-crates/move/crates/move-analyzer/tests/symbols/sources/M4.move b/external-crates/move/crates/move-analyzer/tests/symbols/sources/M4.move index de30eea4afd37..7b985a5c94652 100644 --- a/external-crates/move/crates/move-analyzer/tests/symbols/sources/M4.move +++ b/external-crates/move/crates/move-analyzer/tests/symbols/sources/M4.move @@ -55,4 +55,30 @@ module Symbols::M5 { const SOME_CONST: u64 = 7; + public fun long_param_list(foo: u64, bar: u64, baz: u64, qux: u64) {} + + public fun short_type_param_list() {} + + public fun long_type_param_list() {} + + public fun combined_short_type_param_list( + foo: u64, bar: u64, baz: u64, qux: u64 + ) {} + + public fun combined_long_type_param_list( + foo: u64, bar: u64, baz: u64, qux: u64 + ) {} + + public fun stripped_types(opt: std::option::Option): vector { + // hovering over `extract` should strip `std::option` from parameter type + // `std` from the (qualified) function name + let elem: u64 = std::option::extract(&mut opt); + // hovering over `singleton` should strip `std` from the (qualified) + // function name + std::vector::singleton(elem) + + + } + + } diff --git a/external-crates/move/crates/move-analyzer/tests/symbols/sources/M6.move b/external-crates/move/crates/move-analyzer/tests/symbols/sources/M6.move index 1b60cf1a8971a..843575932ac11 100644 --- a/external-crates/move/crates/move-analyzer/tests/symbols/sources/M6.move +++ b/external-crates/move/crates/move-analyzer/tests/symbols/sources/M6.move @@ -2,7 +2,7 @@ module Symbols::M6 { /// This is a documented struct /// With a multi-line docstring - struct DocumentedStruct has key, drop, store { + struct DocumentedStruct has drop, store { /// A documented field documented_field: u64, } @@ -45,4 +45,38 @@ module Symbols::M6 { param } + /// A documented function with code block + /// (should preserved indentation in the code block) + /// + /// ```rust + /// fun foo() { + /// 42 + /// } + /// ``` + fun code_block_doc_slash() {} + + /** + A documented function with code block + (should preserved indentation in the code block) + + ```rust + fun foo() { + 42 + } + ``` + */ + fun code_block_doc_star() {} + + /** + Misformatted docstring to have fewer whitespace in the body than + at the ending marker. + + + Beginning of this string should not disappear. + +Beginning of this string should not disappear either. + + */ + fun misformatted_docstring() {} + } diff --git a/external-crates/move/crates/move-compiler/src/expansion/alias_map_builder.rs b/external-crates/move/crates/move-compiler/src/expansion/alias_map_builder.rs index 30ed68580776e..9b8a7b44c103e 100644 --- a/external-crates/move/crates/move-compiler/src/expansion/alias_map_builder.rs +++ b/external-crates/move/crates/move-compiler/src/expansion/alias_map_builder.rs @@ -2,8 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{ - expansion::ast::{self as E, ModuleIdent}, - expansion::translate::ModuleMemberKind, + expansion::{ + ast::{self as E, ModuleIdent}, + name_validation::ModuleMemberKind, + }, parser::ast::{self as P}, shared::{unique_map::UniqueMap, *}, }; diff --git a/external-crates/move/crates/move-compiler/src/expansion/mod.rs b/external-crates/move/crates/move-compiler/src/expansion/mod.rs index ddff273766cdb..54bb6be1e514d 100644 --- a/external-crates/move/crates/move-compiler/src/expansion/mod.rs +++ b/external-crates/move/crates/move-compiler/src/expansion/mod.rs @@ -8,6 +8,7 @@ pub mod ast; mod byte_string; mod hex_string; mod legacy_aliases; +pub mod name_validation; mod path_expander; mod primitive_definers; pub(crate) mod translate; diff --git a/external-crates/move/crates/move-compiler/src/expansion/name_validation.rs b/external-crates/move/crates/move-compiler/src/expansion/name_validation.rs new file mode 100644 index 0000000000000..0d33aa7c8d3a7 --- /dev/null +++ b/external-crates/move/crates/move-compiler/src/expansion/name_validation.rs @@ -0,0 +1,424 @@ +// Copyright (c) The Move Contributors +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + diag, + diagnostics::Diagnostic, + parser::ast::{self as P, ModuleName, Var, MACRO_MODIFIER}, + shared::*, +}; +use move_ir_types::location::*; +use move_symbol_pool::Symbol; +use std::collections::BTreeSet; + +// Implicit aliases for the Move Stdlib: +// use std::vector; +// use std::option::{Self, Option}; +pub const IMPLICIT_STD_MODULES: &[Symbol] = &[symbol!("option"), symbol!("vector")]; +pub const IMPLICIT_STD_MEMBERS: &[(Symbol, Symbol, ModuleMemberKind)] = &[( + symbol!("option"), + symbol!("Option"), + ModuleMemberKind::Struct, +)]; + +// Implicit aliases for Sui mode: +// use sui::object::{Self, ID, UID}; +// use sui::transfer; +// use sui::tx_context::{Self, TxContext}; +pub const IMPLICIT_SUI_MODULES: &[Symbol] = &[ + symbol!("object"), + symbol!("transfer"), + symbol!("tx_context"), +]; +pub const IMPLICIT_SUI_MEMBERS: &[(Symbol, Symbol, ModuleMemberKind)] = &[ + (symbol!("object"), symbol!("ID"), ModuleMemberKind::Struct), + (symbol!("object"), symbol!("UID"), ModuleMemberKind::Struct), + ( + symbol!("tx_context"), + symbol!("TxContext"), + ModuleMemberKind::Struct, + ), +]; + +#[derive(Copy, Clone, Debug)] +pub enum ModuleMemberKind { + Constant, + Function, + Struct, + Enum, +} + +#[derive(Copy, Clone, Debug)] +pub enum NameCase { + Constant, + Function, + Struct, + Enum, + Module, + ModuleMemberAlias(ModuleMemberKind), + ModuleAlias, + Variable, + Address, + TypeParameter, +} + +//************************************************************************************************** +// impls +//************************************************************************************************** + +impl ModuleMemberKind { + pub fn case(self) -> NameCase { + match self { + ModuleMemberKind::Constant => NameCase::Constant, + ModuleMemberKind::Function => NameCase::Function, + ModuleMemberKind::Struct => NameCase::Struct, + ModuleMemberKind::Enum => NameCase::Enum, + } + } +} + +impl NameCase { + pub const fn name(&self) -> &'static str { + match self { + NameCase::Constant => "constant", + NameCase::Function => "function", + NameCase::Struct => "struct", + NameCase::Enum => "enum", + NameCase::Module => "module", + NameCase::ModuleMemberAlias(ModuleMemberKind::Function) => "function alias", + NameCase::ModuleMemberAlias(ModuleMemberKind::Constant) => "constant alias", + NameCase::ModuleMemberAlias(ModuleMemberKind::Struct) => "struct alias", + NameCase::ModuleMemberAlias(ModuleMemberKind::Enum) => "enum alias", + NameCase::ModuleAlias => "module alias", + NameCase::Variable => "variable", + NameCase::Address => "address", + NameCase::TypeParameter => "type parameter", + } + } +} + +//************************************************************************************************** +// Valid names +//************************************************************************************************** + +#[allow(clippy::result_unit_err)] +pub fn check_valid_address_name( + env: &mut CompilationEnv, + sp!(_, ln_): &P::LeadingNameAccess, +) -> Result<(), ()> { + use P::LeadingNameAccess_ as LN; + match ln_ { + LN::AnonymousAddress(_) => Ok(()), + LN::GlobalAddress(n) | LN::Name(n) => { + check_restricted_name_all_cases(env, NameCase::Address, n) + } + } +} + +pub fn valid_local_variable_name(s: Symbol) -> bool { + s.starts_with('_') || s.starts_with(|c: char| c.is_ascii_lowercase()) +} + +#[allow(clippy::result_unit_err)] +pub fn check_valid_function_parameter_name( + env: &mut CompilationEnv, + is_macro: Option, + v: &Var, +) { + const SYNTAX_IDENTIFIER_NOTE: &str = + "'macro' parameters start with '$' to indicate that their arguments are not evaluated \ + before the macro is expanded, meaning the entire expression is substituted. \ + This is different from regular function parameters that are evaluated before the \ + function is called."; + let is_syntax_identifier = v.is_syntax_identifier(); + if let Some(macro_loc) = is_macro { + if !is_syntax_identifier && !v.is_underscore() { + let msg = format!( + "Invalid parameter name '{}'. '{}' parameter names must start with '$' (or must be '_')", + v, MACRO_MODIFIER, + ); + let macro_msg = format!("Declared '{}' here", MACRO_MODIFIER); + let mut diag = diag!( + Declarations::InvalidName, + (v.loc(), msg), + (macro_loc, macro_msg), + ); + diag.add_note(SYNTAX_IDENTIFIER_NOTE); + env.add_diag(diag); + } + } else if is_syntax_identifier { + let msg = format!( + "Invalid parameter name '{}'. Non-'{}' parameter names cannot start with '$'", + v, MACRO_MODIFIER, + ); + let mut diag = diag!(Declarations::InvalidName, (v.loc(), msg)); + diag.add_note(SYNTAX_IDENTIFIER_NOTE); + env.add_diag(diag); + } else if !is_valid_local_variable_name(v.value()) { + let msg = format!( + "Invalid parameter name '{}'. Local variable names must start with 'a'..'z', '_', \ + or be a valid name quoted with backticks (`name`)", + v, + ); + env.add_diag(diag!(Declarations::InvalidName, (v.loc(), msg))); + } + let _ = check_restricted_name_all_cases(env, NameCase::Variable, &v.0); +} + +pub fn check_valid_local_name(env: &mut CompilationEnv, v: &Var) { + if !is_valid_local_variable_name(v.value()) { + let msg = format!( + "Invalid local name '{}'. Local variable names must start with 'a'..'z', '_', \ + or be a valid name quoted with backticks (`name`)", + v, + ); + env.add_diag(diag!(Declarations::InvalidName, (v.loc(), msg))); + } + let _ = check_restricted_name_all_cases(env, NameCase::Variable, &v.0); +} + +fn is_valid_local_variable_name(s: Symbol) -> bool { + Var::is_valid_name(s) && !Var::is_syntax_identifier_name(s) +} + +pub fn check_valid_module_member_name( + env: &mut CompilationEnv, + member: ModuleMemberKind, + name: Name, +) -> Option { + match check_valid_module_member_name_impl(env, member, &name, member.case()) { + Err(()) => None, + Ok(()) => Some(name), + } +} + +pub fn check_valid_module_member_alias( + env: &mut CompilationEnv, + member: ModuleMemberKind, + alias: Name, +) -> Option { + match check_valid_module_member_name_impl( + env, + member, + &alias, + NameCase::ModuleMemberAlias(member), + ) { + Err(()) => None, + Ok(()) => Some(alias), + } +} + +fn check_valid_module_member_name_impl( + env: &mut CompilationEnv, + member: ModuleMemberKind, + n: &Name, + case: NameCase, +) -> Result<(), ()> { + use ModuleMemberKind as M; + fn upper_first_letter(s: &str) -> String { + let mut chars = s.chars(); + match chars.next() { + None => String::new(), + Some(c) => c.to_uppercase().collect::() + chars.as_str(), + } + } + match member { + M::Function => { + if n.value.starts_with(|c| c == '_') { + let msg = format!( + "Invalid {} name '{}'. {} names cannot start with '_'", + case.name(), + n, + upper_first_letter(case.name()), + ); + env.add_diag(diag!(Declarations::InvalidName, (n.loc, msg))); + return Err(()); + } + } + M::Constant | M::Struct | M::Enum => { + if !is_valid_datatype_or_constant_name(&n.value) { + let msg = format!( + "Invalid {} name '{}'. {} names must start with 'A'..'Z'", + case.name(), + n, + upper_first_letter(case.name()), + ); + env.add_diag(diag!(Declarations::InvalidName, (n.loc, msg))); + return Err(()); + } + } + } + + // TODO move these names to a more central place? + check_restricted_names( + env, + case, + n, + crate::naming::ast::BuiltinFunction_::all_names(), + )?; + check_restricted_names( + env, + case, + n, + crate::naming::ast::BuiltinTypeName_::all_names(), + )?; + + // Restricting Self for now in the case where we ever have impls + // Otherwise, we could allow it + check_restricted_name_all_cases(env, case, n)?; + + Ok(()) +} + +#[allow(clippy::result_unit_err)] +pub fn check_valid_type_parameter_name( + env: &mut CompilationEnv, + is_macro: Option, + n: &Name, +) -> Result<(), ()> { + // TODO move these names to a more central place? + if n.value == symbol!("_") { + let diag = restricted_name_error(NameCase::TypeParameter, n.loc, "_"); + env.add_diag(diag); + return Err(()); + } + + const SYNTAX_IDENTIFIER_NOTE: &str = "Type parameter names starting with '$' indicate that \ + their arguments do not have to satisfy certain constraints before the macro is expanded, \ + meaning types like '&mut u64' or '(bool, u8)' may be used as arguments."; + + let is_syntax_ident = Var::is_syntax_identifier_name(n.value); + if let Some(macro_loc) = is_macro { + if !is_syntax_ident { + let msg = format!( + "Invalid type parameter name. \ + '{} fun' type parameter names must start with '$'", + MACRO_MODIFIER + ); + let macro_msg = format!("Declared '{}' here", MACRO_MODIFIER); + let mut diag = diag!( + Declarations::InvalidName, + (n.loc, msg), + (macro_loc, macro_msg), + ); + diag.add_note(SYNTAX_IDENTIFIER_NOTE); + env.add_diag(diag); + } else { + let next_char = n.value.chars().nth(1).unwrap(); + if !next_char.is_ascii_alphabetic() { + let msg = format!( + "Invalid type parameter name '{}'. \ + Following the '$', the '{} fun' type parameter must be have a valid type \ + parameter name starting with a letter 'a'..'z' or 'A'..'Z'", + n, MACRO_MODIFIER + ); + let mut diag = diag!(Declarations::InvalidName, (n.loc, msg)); + diag.add_note(SYNTAX_IDENTIFIER_NOTE); + env.add_diag(diag); + } + } + } else if is_syntax_ident { + let msg = format!( + "Invalid type parameter name. \ + Only '{} fun' type parameter names cat start with '$'", + MACRO_MODIFIER + ); + let mut diag = diag!(Declarations::InvalidName, (n.loc, msg)); + diag.add_note(SYNTAX_IDENTIFIER_NOTE); + env.add_diag(diag); + } + + // TODO move these names to a more central place? + check_restricted_names( + env, + NameCase::TypeParameter, + n, + crate::naming::ast::BuiltinFunction_::all_names(), + )?; + check_restricted_names( + env, + NameCase::TypeParameter, + n, + crate::naming::ast::BuiltinTypeName_::all_names(), + )?; + + check_restricted_name_all_cases(env, NameCase::TypeParameter, n) +} + +pub fn is_valid_datatype_or_constant_name(s: &str) -> bool { + s.starts_with(|c: char| c.is_ascii_uppercase()) +} + +#[allow(clippy::result_unit_err)] +// Checks for a restricted name in any decl case +// Self and vector are not allowed +pub fn check_restricted_name_all_cases( + env: &mut CompilationEnv, + case: NameCase, + n: &Name, +) -> Result<(), ()> { + match case { + NameCase::Constant + | NameCase::Function + | NameCase::Struct + | NameCase::Enum + | NameCase::Module + | NameCase::ModuleMemberAlias(_) + | NameCase::ModuleAlias + | NameCase::Address => { + if Var::is_syntax_identifier_name(n.value) { + let msg = format!( + "Invalid {} name '{}'. Identifiers starting with '$' can be used only for \ + parameters and type paramters", + case.name(), + n, + ); + env.add_diag(diag!(Declarations::InvalidName, (n.loc, msg))); + return Err(()); + } + } + NameCase::Variable | NameCase::TypeParameter => (), + } + + let n_str = n.value.as_str(); + let can_be_vector = matches!(case, NameCase::Module | NameCase::ModuleAlias); + if n_str == ModuleName::SELF_NAME + || (!can_be_vector && n_str == crate::naming::ast::BuiltinTypeName_::VECTOR) + { + env.add_diag(restricted_name_error(case, n.loc, n_str)); + Err(()) + } else { + Ok(()) + } +} + +fn check_restricted_names( + env: &mut CompilationEnv, + case: NameCase, + sp!(loc, n_): &Name, + all_names: &BTreeSet, +) -> Result<(), ()> { + if all_names.contains(n_) { + env.add_diag(restricted_name_error(case, *loc, n_)); + Err(()) + } else { + Ok(()) + } +} + +fn restricted_name_error(case: NameCase, loc: Loc, restricted: &str) -> Diagnostic { + let a_or_an = match case.name().chars().next().unwrap() { + // TODO this is not exhaustive to the indefinite article rules in English + // but 'case' is never user generated, so it should be okay for a while/forever... + 'a' | 'e' | 'i' | 'o' | 'u' => "an", + _ => "a", + }; + let msg = format!( + "Invalid {case} name '{restricted}'. '{restricted}' is restricted and cannot be used to \ + name {a_or_an} {case}", + a_or_an = a_or_an, + case = case.name(), + restricted = restricted, + ); + diag!(NameResolution::ReservedName, (loc, msg)) +} 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 8891e077adcdc..70e7cde321745 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 @@ -12,9 +12,10 @@ use crate::{ aliases::{AliasMap, AliasSet}, ast::{self as E, Address, ModuleIdent, ModuleIdent_}, legacy_aliases, + name_validation::is_valid_datatype_or_constant_name, translate::{ - is_valid_datatype_or_constant_name, make_address, module_ident, top_level_address, - top_level_address_opt, value, DefnContext, + make_address, module_ident, top_level_address, top_level_address_opt, value, + DefnContext, }, }, ice, ice_assert, 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 093a28e8f9af0..e339f4ba035ed 100644 --- a/external-crates/move/crates/move-compiler/src/expansion/translate.rs +++ b/external-crates/move/crates/move-compiler/src/expansion/translate.rs @@ -13,6 +13,13 @@ use crate::{ aliases::AliasSet, ast::{self as E, Address, Fields, ModuleIdent, ModuleIdent_, TargetKind}, byte_string, hex_string, + name_validation::{ + check_restricted_name_all_cases, check_valid_address_name, + check_valid_function_parameter_name, check_valid_local_name, + check_valid_module_member_alias, check_valid_module_member_name, + check_valid_type_parameter_name, valid_local_variable_name, ModuleMemberKind, NameCase, + IMPLICIT_STD_MEMBERS, IMPLICIT_STD_MODULES, IMPLICIT_SUI_MEMBERS, IMPLICIT_SUI_MODULES, + }, path_expander::{ access_result, Access, LegacyPathExpander, ModuleAccessResult, Move2024PathExpander, PathExpander, @@ -327,35 +334,6 @@ fn compute_address_conflicts( .collect() } -// Implicit aliases for the Move Stdlib: -// use std::vector; -// use std::option::{Self, Option}; -const IMPLICIT_STD_MODULES: &[Symbol] = &[symbol!("option"), symbol!("vector")]; -const IMPLICIT_STD_MEMBERS: &[(Symbol, Symbol, ModuleMemberKind)] = &[( - symbol!("option"), - symbol!("Option"), - ModuleMemberKind::Struct, -)]; - -// Implicit aliases for Sui mode: -// use sui::object::{Self, ID, UID}; -// use sui::transfer; -// use sui::tx_context::{Self, TxContext}; -const IMPLICIT_SUI_MODULES: &[Symbol] = &[ - symbol!("object"), - symbol!("transfer"), - symbol!("tx_context"), -]; -const IMPLICIT_SUI_MEMBERS: &[(Symbol, Symbol, ModuleMemberKind)] = &[ - (symbol!("object"), symbol!("ID"), ModuleMemberKind::Struct), - (symbol!("object"), symbol!("UID"), ModuleMemberKind::Struct), - ( - symbol!("tx_context"), - symbol!("TxContext"), - ModuleMemberKind::Struct, - ), -]; - fn default_aliases(context: &mut Context) -> AliasMapBuilder { let current_package = context.current_package(); let mut builder = context.new_alias_map_builder(); @@ -623,7 +601,7 @@ fn top_level_address_( suggest_declaration: bool, ln: P::LeadingNameAccess, ) -> Address { - let name_res = check_valid_address_name(context, &ln); + let name_res = check_valid_address_name(context.env, &ln); let sp!(loc, ln_) = ln; match ln_ { P::LeadingNameAccess_::AnonymousAddress(bytes) => { @@ -661,7 +639,7 @@ pub(super) fn top_level_address_opt( context: &mut DefnContext, ln: P::LeadingNameAccess, ) -> Option
{ - let name_res = check_valid_address_name(context, &ln); + let name_res = check_valid_address_name(context.env, &ln); let named_address_mapping = context.named_address_mapping.as_ref().unwrap(); let sp!(loc, ln_) = ln; match ln_ { @@ -847,7 +825,7 @@ fn module_( assert!(context.address.is_none()); assert!(address.is_none()); set_module_address(context, &name, module_address); - let _ = check_restricted_name_all_cases(&mut context.defn_context, NameCase::Module, &name.0); + let _ = check_restricted_name_all_cases(context.defn_context.env, NameCase::Module, &name.0); if name.value().starts_with(|c| c == '_') { let msg = format!( "Invalid module name '{}'. Module names cannot start with '_'", @@ -1461,7 +1439,7 @@ fn aliases_from_member( ) -> Option { macro_rules! check_name_and_add_implicit_alias { ($kind:expr, $name:expr) => {{ - if let Some(n) = check_valid_module_member_name(context, $kind, $name) { + if let Some(n) = check_valid_module_member_name(&mut context.env(), $kind, $name) { if let Err(loc) = acc.add_implicit_member_alias( n.clone(), current_module.clone(), @@ -1598,7 +1576,7 @@ fn module_use( macro_rules! add_module_alias { ($ident:expr, $alias:expr) => {{ if let Err(()) = check_restricted_name_all_cases( - &mut context.defn_context, + &mut context.defn_context.env, NameCase::ModuleAlias, &$alias, ) { @@ -1678,7 +1656,8 @@ fn module_use( let alias = alias_opt.unwrap_or(member); - let alias = match check_valid_module_member_alias(context, member_kind, alias) { + let alias = match check_valid_module_member_alias(context.env(), member_kind, alias) + { None => continue, Some(alias) => alias, }; @@ -2259,7 +2238,7 @@ fn function_signature( .map(|(pmut, v, t)| (mutability(context, v.loc(), pmut), v, type_(context, t))) .collect::>(); for (_, v, _) in ¶meters { - check_valid_function_parameter_name(context, is_macro, v) + check_valid_function_parameter_name(context.env(), is_macro, v) } let return_type = type_(context, pret_ty); E::FunctionSignature { @@ -2307,7 +2286,7 @@ fn function_type_parameters( .into_iter() .map(|(name, constraints_vec)| { let constraints = ability_set(context, "constraint", constraints_vec); - let _ = check_valid_type_parameter_name(context, is_macro, &name); + let _ = check_valid_type_parameter_name(context.env(), is_macro, &name); (name, constraints) }) .collect() @@ -2320,7 +2299,7 @@ fn datatype_type_parameters( pty_params .into_iter() .map(|param| { - let _ = check_valid_type_parameter_name(context, None, ¶m.name); + let _ = check_valid_type_parameter_name(context.env(), None, ¶m.name); E::DatatypeTypeParameter { is_phantom: param.is_phantom, name: param.name, @@ -3331,7 +3310,7 @@ fn bind(context: &mut Context, sp!(loc, pb_): P::Bind) -> Option { let b_ = match pb_ { PB::Var(pmut, v) => { let emut = mutability(context, v.loc(), pmut); - check_valid_local_name(context, &v); + check_valid_local_name(context.env(), &v); EL::Var(Some(emut), sp(loc, E::ModuleAccess_::Name(v.0)), None) } PB::Unpack(ptn, pfields) => { @@ -3574,388 +3553,3 @@ fn mutability(context: &mut Context, _loc: Loc, pmut: P::Mutability) -> E::Mutab None => E::Mutability::Either, } } - -//************************************************************************************************** -// Valid names -//************************************************************************************************** - -fn check_valid_address_name( - context: &mut DefnContext, - sp!(_, ln_): &P::LeadingNameAccess, -) -> Result<(), ()> { - use P::LeadingNameAccess_ as LN; - match ln_ { - LN::AnonymousAddress(_) => Ok(()), - LN::GlobalAddress(n) | LN::Name(n) => { - check_restricted_name_all_cases(context, NameCase::Address, n) - } - } -} - -fn valid_local_variable_name(s: Symbol) -> bool { - s.starts_with('_') || s.starts_with(|c: char| c.is_ascii_lowercase()) -} - -fn check_valid_function_parameter_name(context: &mut Context, is_macro: Option, v: &Var) { - const SYNTAX_IDENTIFIER_NOTE: &str = - "'macro' parameters start with '$' to indicate that their arguments are not evaluated \ - before the macro is expanded, meaning the entire expression is substituted. \ - This is different from regular function parameters that are evaluated before the \ - function is called."; - let is_syntax_identifier = v.is_syntax_identifier(); - if let Some(macro_loc) = is_macro { - if !is_syntax_identifier && !v.is_underscore() { - let msg = format!( - "Invalid parameter name '{}'. '{}' parameter names must start with '$' (or must be '_')", - v, MACRO_MODIFIER, - ); - let macro_msg = format!("Declared '{}' here", MACRO_MODIFIER); - let mut diag = diag!( - Declarations::InvalidName, - (v.loc(), msg), - (macro_loc, macro_msg), - ); - diag.add_note(SYNTAX_IDENTIFIER_NOTE); - context.env().add_diag(diag); - } - } else if is_syntax_identifier { - let msg = format!( - "Invalid parameter name '{}'. Non-'{}' parameter names cannot start with '$'", - v, MACRO_MODIFIER, - ); - let mut diag = diag!(Declarations::InvalidName, (v.loc(), msg)); - diag.add_note(SYNTAX_IDENTIFIER_NOTE); - context.env().add_diag(diag); - } else if !is_valid_local_variable_name(v.value()) { - let msg = format!( - "Invalid parameter name '{}'. Local variable names must start with 'a'..'z', '_', \ - or be a valid name quoted with backticks (`name`)", - v, - ); - context - .env() - .add_diag(diag!(Declarations::InvalidName, (v.loc(), msg))); - } - let _ = check_restricted_name_all_cases(&mut context.defn_context, NameCase::Variable, &v.0); -} - -fn check_valid_local_name(context: &mut Context, v: &Var) { - if !is_valid_local_variable_name(v.value()) { - let msg = format!( - "Invalid local name '{}'. Local variable names must start with 'a'..'z', '_', \ - or be a valid name quoted with backticks (`name`)", - v, - ); - context - .env() - .add_diag(diag!(Declarations::InvalidName, (v.loc(), msg))); - } - let _ = check_restricted_name_all_cases(&mut context.defn_context, NameCase::Variable, &v.0); -} - -fn is_valid_local_variable_name(s: Symbol) -> bool { - Var::is_valid_name(s) && !Var::is_syntax_identifier_name(s) -} - -#[derive(Copy, Clone, Debug)] -pub enum ModuleMemberKind { - Constant, - Function, - Struct, - Enum, -} - -impl ModuleMemberKind { - pub fn case(self) -> NameCase { - match self { - ModuleMemberKind::Constant => NameCase::Constant, - ModuleMemberKind::Function => NameCase::Function, - ModuleMemberKind::Struct => NameCase::Struct, - ModuleMemberKind::Enum => NameCase::Enum, - } - } -} - -#[derive(Copy, Clone, Debug)] -pub enum NameCase { - Constant, - Function, - Struct, - Enum, - Module, - ModuleMemberAlias(ModuleMemberKind), - ModuleAlias, - Variable, - Address, - TypeParameter, -} - -impl NameCase { - pub const fn name(&self) -> &'static str { - match self { - NameCase::Constant => "constant", - NameCase::Function => "function", - NameCase::Struct => "struct", - NameCase::Enum => "enum", - NameCase::Module => "module", - NameCase::ModuleMemberAlias(ModuleMemberKind::Function) => "function alias", - NameCase::ModuleMemberAlias(ModuleMemberKind::Constant) => "constant alias", - NameCase::ModuleMemberAlias(ModuleMemberKind::Struct) => "struct alias", - NameCase::ModuleMemberAlias(ModuleMemberKind::Enum) => "enum alias", - NameCase::ModuleAlias => "module alias", - NameCase::Variable => "variable", - NameCase::Address => "address", - NameCase::TypeParameter => "type parameter", - } - } -} - -fn check_valid_module_member_name( - context: &mut Context, - member: ModuleMemberKind, - name: Name, -) -> Option { - match check_valid_module_member_name_impl(context, member, &name, member.case()) { - Err(()) => None, - Ok(()) => Some(name), - } -} - -fn check_valid_module_member_alias( - context: &mut Context, - member: ModuleMemberKind, - alias: Name, -) -> Option { - match check_valid_module_member_name_impl( - context, - member, - &alias, - NameCase::ModuleMemberAlias(member), - ) { - Err(()) => None, - Ok(()) => Some(alias), - } -} - -fn check_valid_module_member_name_impl( - context: &mut Context, - member: ModuleMemberKind, - n: &Name, - case: NameCase, -) -> Result<(), ()> { - use ModuleMemberKind as M; - fn upper_first_letter(s: &str) -> String { - let mut chars = s.chars(); - match chars.next() { - None => String::new(), - Some(c) => c.to_uppercase().collect::() + chars.as_str(), - } - } - match member { - M::Function => { - if n.value.starts_with(|c| c == '_') { - let msg = format!( - "Invalid {} name '{}'. {} names cannot start with '_'", - case.name(), - n, - upper_first_letter(case.name()), - ); - context - .env() - .add_diag(diag!(Declarations::InvalidName, (n.loc, msg))); - return Err(()); - } - } - M::Constant | M::Struct | M::Enum => { - if !is_valid_datatype_or_constant_name(&n.value) { - let msg = format!( - "Invalid {} name '{}'. {} names must start with 'A'..'Z'", - case.name(), - n, - upper_first_letter(case.name()), - ); - context - .env() - .add_diag(diag!(Declarations::InvalidName, (n.loc, msg))); - return Err(()); - } - } - } - - // TODO move these names to a more central place? - check_restricted_names( - context, - case, - n, - crate::naming::ast::BuiltinFunction_::all_names(), - )?; - check_restricted_names( - context, - case, - n, - crate::naming::ast::BuiltinTypeName_::all_names(), - )?; - - // Restricting Self for now in the case where we ever have impls - // Otherwise, we could allow it - check_restricted_name_all_cases(&mut context.defn_context, case, n)?; - - Ok(()) -} - -fn check_valid_type_parameter_name( - context: &mut Context, - is_macro: Option, - n: &Name, -) -> Result<(), ()> { - // TODO move these names to a more central place? - if n.value == symbol!("_") { - let diag = restricted_name_error(NameCase::TypeParameter, n.loc, "_"); - context.env().add_diag(diag); - return Err(()); - } - - const SYNTAX_IDENTIFIER_NOTE: &str = "Type parameter names starting with '$' indicate that \ - their arguments do not have to satisfy certain constraints before the macro is expanded, \ - meaning types like '&mut u64' or '(bool, u8)' may be used as arguments."; - - let is_syntax_ident = Var::is_syntax_identifier_name(n.value); - if let Some(macro_loc) = is_macro { - if !is_syntax_ident { - let msg = format!( - "Invalid type parameter name. \ - '{} fun' type parameter names must start with '$'", - MACRO_MODIFIER - ); - let macro_msg = format!("Declared '{}' here", MACRO_MODIFIER); - let mut diag = diag!( - Declarations::InvalidName, - (n.loc, msg), - (macro_loc, macro_msg), - ); - diag.add_note(SYNTAX_IDENTIFIER_NOTE); - context.env().add_diag(diag); - } else { - let next_char = n.value.chars().nth(1).unwrap(); - if !next_char.is_ascii_alphabetic() { - let msg = format!( - "Invalid type parameter name '{}'. \ - Following the '$', the '{} fun' type parameter must be have a valid type \ - parameter name starting with a letter 'a'..'z' or 'A'..'Z'", - n, MACRO_MODIFIER - ); - let mut diag = diag!(Declarations::InvalidName, (n.loc, msg)); - diag.add_note(SYNTAX_IDENTIFIER_NOTE); - context.env().add_diag(diag); - } - } - } else if is_syntax_ident { - let msg = format!( - "Invalid type parameter name. \ - Only '{} fun' type parameter names cat start with '$'", - MACRO_MODIFIER - ); - let mut diag = diag!(Declarations::InvalidName, (n.loc, msg)); - diag.add_note(SYNTAX_IDENTIFIER_NOTE); - context.env().add_diag(diag); - } - - // TODO move these names to a more central place? - check_restricted_names( - context, - NameCase::TypeParameter, - n, - crate::naming::ast::BuiltinFunction_::all_names(), - )?; - check_restricted_names( - context, - NameCase::TypeParameter, - n, - crate::naming::ast::BuiltinTypeName_::all_names(), - )?; - - check_restricted_name_all_cases(&mut context.defn_context, NameCase::TypeParameter, n) -} - -pub fn is_valid_datatype_or_constant_name(s: &str) -> bool { - s.starts_with(|c: char| c.is_ascii_uppercase()) -} - -// Checks for a restricted name in any decl case -// Self and vector are not allowed -fn check_restricted_name_all_cases( - context: &mut DefnContext, - case: NameCase, - n: &Name, -) -> Result<(), ()> { - match case { - NameCase::Constant - | NameCase::Function - | NameCase::Struct - | NameCase::Enum - | NameCase::Module - | NameCase::ModuleMemberAlias(_) - | NameCase::ModuleAlias - | NameCase::Address => { - if Var::is_syntax_identifier_name(n.value) { - let msg = format!( - "Invalid {} name '{}'. Identifiers starting with '$' can be used only for \ - parameters and type paramters", - case.name(), - n, - ); - context - .env - .add_diag(diag!(Declarations::InvalidName, (n.loc, msg))); - return Err(()); - } - } - NameCase::Variable | NameCase::TypeParameter => (), - } - - let n_str = n.value.as_str(); - let can_be_vector = matches!(case, NameCase::Module | NameCase::ModuleAlias); - if n_str == ModuleName::SELF_NAME - || (!can_be_vector && n_str == crate::naming::ast::BuiltinTypeName_::VECTOR) - { - context - .env - .add_diag(restricted_name_error(case, n.loc, n_str)); - Err(()) - } else { - Ok(()) - } -} - -fn check_restricted_names( - context: &mut Context, - case: NameCase, - sp!(loc, n_): &Name, - all_names: &BTreeSet, -) -> Result<(), ()> { - if all_names.contains(n_) { - context - .env() - .add_diag(restricted_name_error(case, *loc, n_)); - Err(()) - } else { - Ok(()) - } -} - -fn restricted_name_error(case: NameCase, loc: Loc, restricted: &str) -> Diagnostic { - let a_or_an = match case.name().chars().next().unwrap() { - // TODO this is not exhaustive to the indefinite article rules in English - // but 'case' is never user generated, so it should be okay for a while/forever... - 'a' | 'e' | 'i' | 'o' | 'u' => "an", - _ => "a", - }; - let msg = format!( - "Invalid {case} name '{restricted}'. '{restricted}' is restricted and cannot be used to \ - name {a_or_an} {case}", - a_or_an = a_or_an, - case = case.name(), - restricted = restricted, - ); - diag!(NameResolution::ReservedName, (loc, msg)) -} 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 f238eb60f6ff1..be361bc2b9b52 100644 --- a/external-crates/move/crates/move-compiler/src/naming/translate.rs +++ b/external-crates/move/crates/move-compiler/src/naming/translate.rs @@ -12,7 +12,7 @@ use crate::{ editions::FeatureGate, expansion::{ ast::{self as E, AbilitySet, Ellipsis, ModuleIdent, Mutability, Visibility}, - translate::is_valid_datatype_or_constant_name as is_constant_name, + name_validation::is_valid_datatype_or_constant_name as is_constant_name, }, ice, naming::{