diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index b8bc435e23..bdfc660624 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -1019,9 +1019,10 @@ pub mut: imports []Import // all the imports auto_imports []string // imports that were implicitly added used_imports []string - implied_imports []string // ​imports that the user's code uses but omitted to import explicitly, used by `vfmt` - embedded_files []EmbeddedFile // list of files to embed in the binary - imported_symbols map[string]string // used for `import {symbol}`, it maps symbol => module.symbol + implied_imports []string // ​imports that the user's code uses but omitted to import explicitly, used by `vfmt` + embedded_files []EmbeddedFile // list of files to embed in the binary + imported_symbols map[string]string // used for `import {symbol}`, it maps symbol => module.symbol + imported_symbols_trie token.KeywordsMatcherTrie // constructed from imported_symbols, to accelerate presense checks imported_symbols_used map[string]bool errors []errors.Error // all the checker errors in the file warnings []errors.Warning // all the checker warnings in the file diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 3fccd9661b..34b2cb8fc2 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -4261,7 +4261,7 @@ fn (mut c Checker) ident(mut node ast.Ident) ast.Type { } mut name := node.name // check for imported symbol - if name in c.file.imported_symbols { + if c.file.imported_symbols_trie.matches(name) { name = c.file.imported_symbols[name] } // prepend mod to look for fn call or const diff --git a/vlib/v/parser/enum.v b/vlib/v/parser/enum.v index f6d0ed8468..5ce4120ee1 100644 --- a/vlib/v/parser/enum.v +++ b/vlib/v/parser/enum.v @@ -61,7 +61,7 @@ fn (mut p Parser) enum_decl() ast.EnumDecl { end_pos) return ast.EnumDecl{} } - if enum_name in p.imported_symbols { + if p.is_imported_symbol(enum_name) { p.error_with_pos('cannot register enum `${enum_name}`, this type was already imported', end_pos) return ast.EnumDecl{} diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index a62901e096..46be078400 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -89,7 +89,7 @@ fn (mut p Parser) call_expr(language ast.Language, mod string) ast.CallExpr { } or_kind = if is_not { .propagate_result } else { .propagate_option } } - if fn_name in p.imported_symbols { + if p.is_imported_symbol(fn_name) { check := !p.imported_symbols_used[fn_name] fn_name = p.imported_symbols[fn_name] if check { @@ -152,7 +152,7 @@ fn (mut p Parser) call_args() []ast.CallArg { } else { expr = p.expr(0) if mut expr is ast.Ident { - if expr.name in p.imported_symbols && !p.imported_symbols_used[expr.name] { + if p.is_imported_symbol(expr.name) && !p.imported_symbols_used[expr.name] { // func call arg is another function call // import term { bright_cyan, colorize } ... colorize(bright_cyan, 'hello') p.register_used_import_for_symbol_name(p.imported_symbols[expr.name]) @@ -393,7 +393,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl { } } if !p.pref.is_fmt { - if name in p.imported_symbols { + if p.is_imported_symbol(name) { p.error_with_pos('cannot redefine imported function `${name}`', name_pos) return ast.FnDecl{ scope: unsafe { nil } diff --git a/vlib/v/parser/module.v b/vlib/v/parser/module.v index 8e8d8bd5fd..a9eb511694 100644 --- a/vlib/v/parser/module.v +++ b/vlib/v/parser/module.v @@ -36,7 +36,7 @@ fn (mut p Parser) register_used_import(alias string) { fn (mut p Parser) register_used_import_for_symbol_name(sym_name string) { short_import_name := sym_name.all_before_last('.').all_after_last('.') short_symbol_name := sym_name.all_after_last('.') - if short_symbol_name in p.imported_symbols { + if p.is_imported_symbol(short_symbol_name) { p.imported_symbols_used[short_symbol_name] = true } for alias, mod in p.imports { @@ -322,12 +322,13 @@ fn (mut p Parser) import_syms(mut parent ast.Import) { for p.tok.kind == .name { pos := p.tok.pos() alias := p.check_name() - if alias in p.imported_symbols { + if p.is_imported_symbol(alias) { p.error_with_pos('cannot register symbol `${alias}`, it was already imported', pos) return } p.imported_symbols[alias] = parent.mod + '.' + alias + p.rebuild_imported_symbols_matcher(alias) // so we can work with this in fmt+checker parent.syms << ast.ImportSymbol{ pos: pos @@ -347,3 +348,12 @@ fn (mut p Parser) import_syms(mut parent ast.Import) { } p.next() } + +fn (mut p Parser) rebuild_imported_symbols_matcher(name string) { + p.imported_symbols_trie = token.new_keywords_matcher_from_array_trie(p.imported_symbols.keys()) +} + +@[inline] +fn (mut p Parser) is_imported_symbol(name string) bool { + return p.imported_symbols_trie.matches(name) +} diff --git a/vlib/v/parser/parse_type.v b/vlib/v/parser/parse_type.v index 613b12155d..3e025b609e 100644 --- a/vlib/v/parser/parse_type.v +++ b/vlib/v/parser/parse_type.v @@ -636,7 +636,7 @@ fn (mut p Parser) parse_any_type(language ast.Language, is_ptr bool, check_dot b } else if p.expr_mod != '' && !p.inside_generic_params { // p.expr_mod is from the struct and not from the generic parameter name = p.expr_mod + '.' + name - } else if name in p.imported_symbols { + } else if p.is_imported_symbol(name) { check := !p.imported_symbols_used[name] name = p.imported_symbols[name] if check { diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index e3e12dc099..f22ff8cc37 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -86,6 +86,7 @@ mut: implied_imports []string // ​imports that the user's code uses but omitted to import explicitly, used by `vfmt` imported_symbols map[string]string imported_symbols_used map[string]bool + imported_symbols_trie token.KeywordsMatcherTrie is_amp bool // for generating the right code for `&Foo{}` returns bool is_stmt_ident bool // true while the beginning of a statement is an ident/selector @@ -343,6 +344,7 @@ pub fn (mut p Parser) parse() &ast.File { mod: module_decl imports: p.ast_imports imported_symbols: p.imported_symbols + imported_symbols_trie: token.new_keywords_matcher_from_array_trie(p.imported_symbols.keys()) imported_symbols_used: p.imported_symbols_used auto_imports: p.auto_imports used_imports: p.used_imports @@ -592,8 +594,7 @@ fn (mut p Parser) check_name() string { name := p.tok.lit if p.tok.kind != .name && p.peek_tok.kind == .dot && name in p.imports { p.register_used_import(name) - } else if p.tok.kind == .name && p.peek_tok.kind == .dot && name in p.imported_symbols - && !p.imported_symbols_used[name] { + } else if p.tok.kind == .name && p.is_imported_symbol(name) && !p.imported_symbols_used[name] { // symbols like Enum.field_name p.register_used_import_for_symbol_name(p.imported_symbols[name]) } @@ -2698,7 +2699,7 @@ fn (mut p Parser) type_decl() ast.TypeDecl { return ast.FnTypeDecl{} } } - if name in p.imported_symbols { + if p.is_imported_symbol(name) { p.error_with_pos('cannot register alias `${name}`, this type was already imported', end_pos) return ast.AliasTypeDecl{} diff --git a/vlib/v/parser/struct.v b/vlib/v/parser/struct.v index a5c7d69f3b..df1cee88ba 100644 --- a/vlib/v/parser/struct.v +++ b/vlib/v/parser/struct.v @@ -73,7 +73,7 @@ fn (mut p Parser) struct_decl(is_anon bool) ast.StructDecl { p.error_with_pos('struct names must have more than one character', name_pos) return ast.StructDecl{} } - if name in p.imported_symbols { + if p.is_imported_symbol(name) { p.error_with_pos('cannot register struct `${name}`, this type was already imported', name_pos) return ast.StructDecl{} @@ -651,7 +651,7 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl { mut pre_comments := p.eat_comments() p.check(.lcbr) pre_comments << p.eat_comments() - if modless_name in p.imported_symbols { + if p.is_imported_symbol(modless_name) { p.error_with_pos('cannot register interface `${interface_name}`, this type was already imported', name_pos) return ast.InterfaceDecl{} diff --git a/vlib/v/token/keywords_matcher_trie.v b/vlib/v/token/keywords_matcher_trie.v index 2f0ed5953e..34efb8b7f6 100644 --- a/vlib/v/token/keywords_matcher_trie.v +++ b/vlib/v/token/keywords_matcher_trie.v @@ -11,6 +11,11 @@ pub mut: max_len int } +// str returns a short representation of matcher +pub fn (km &KeywordsMatcherTrie) str() string { + return 'KeywordsMatcherTrie{ /* nodes.len: ${km.nodes.len} */ min_len: ${km.min_len}, max_len: ${km.max_len} }' +} + // TrieNode is a single node from a trie, used by KeywordsMatcherTrie pub struct TrieNode { pub mut: @@ -18,6 +23,14 @@ pub mut: value int = -1 // when != -1, it is a leaf node representing a match } +// str returns a string representation of the node content +pub fn (node &TrieNode) str() string { + if isnil(node) { + return '&TrieNode(nil)' + } + return '&TrieNode{value: ${node.value}}' +} + // find tries to find the given `word` in the set of all previously added words // to the KeywordsMatcherTrie instance. It returns -1 if the word was NOT found // there at all. If the word was found, find will return the `value` (value => 0),