changeset 22415:1cefe1c013ac v8.2.1756

patch 8.2.1756: Vim9: :let will soon be disallowed Commit: https://github.com/vim/vim/commit/cfcd011fcd8021da52fba62dabf7a2497f1879b7 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Sep 27 15:19:27 2020 +0200 patch 8.2.1756: Vim9: :let will soon be disallowed Problem: Vim9: :let will soon be disallowed. Solution: Add v:disallow_let temporarily. Fix tests.
author Bram Moolenaar <Bram@vim.org>
date Sun, 27 Sep 2020 15:30:04 +0200
parents f22c7b159f37
children f94f157b4a1d
files src/errors.h src/evalvars.c src/testdir/test_vim9_script.vim src/userfunc.c src/version.c src/vim.h src/vim9compile.c
diffstat 7 files changed, 237 insertions(+), 210 deletions(-) [+]
line wrap: on
line diff
--- a/src/errors.h
+++ b/src/errors.h
@@ -274,4 +274,6 @@ EXTERN char e_str_cannot_be_used_in_lega
 	INIT(= N_("E1124: \"%s\" cannot be used in legacy Vim script"));
 EXTERN char e_final_requires_a_value[]
 	INIT(= N_("E1125: Final requires a value"));
+EXTERN char e_cannot_use_let_in_vim9_script[]
+	INIT(= N_("E1126: Cannot use :let in Vim9 script"));
 #endif
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -146,6 +146,7 @@ static struct vimvar
     {VV_NAME("echospace",	 VAR_NUMBER), VV_RO},
     {VV_NAME("argv",		 VAR_LIST), VV_RO},
     {VV_NAME("collate",		 VAR_STRING), VV_RO},
+    {VV_NAME("disallow_let",	 VAR_NUMBER), 0}, // TODO: remove
 };
 
 // shorthand
@@ -734,6 +735,12 @@ ex_let(exarg_T *eap)
 	    ex_finally(eap);
 	    return;
     }
+    if (get_vim_var_nr(VV_DISALLOW_LET)
+				      && eap->cmdidx == CMD_let && vim9script)
+    {
+	emsg(_(e_cannot_use_let_in_vim9_script));
+	return;
+    }
     if (eap->cmdidx == CMD_const && !vim9script && !eap->forceit)
 	// In legacy Vim script ":const" works like ":final".
 	eap->cmdidx = CMD_final;
--- a/src/testdir/test_vim9_script.vim
+++ b/src/testdir/test_vim9_script.vim
@@ -6,6 +6,9 @@ source view_util.vim
 source vim9.vim
 source shared.vim
 
+" TODO: remove later
+let v:disallow_let = 1
+
 def Test_range_only()
   new
   setline(1, ['blah', 'Blah'])
@@ -45,17 +48,17 @@ def Test_unlet()
 
   CheckScriptFailure([
    'vim9script',
-   'let svar = 123',
+   'var svar = 123',
    'unlet svar',
    ], 'E1081:')
   CheckScriptFailure([
    'vim9script',
-   'let svar = 123',
+   'var svar = 123',
    'unlet s:svar',
    ], 'E1081:')
   CheckScriptFailure([
    'vim9script',
-   'let svar = 123',
+   'var svar = 123',
    'def Func()',
    '  unlet svar',
    'enddef',
@@ -63,7 +66,7 @@ def Test_unlet()
    ], 'E1081:')
   CheckScriptFailure([
    'vim9script',
-   'let svar = 123',
+   'var svar = 123',
    'def Func()',
    '  unlet s:svar',
    'enddef',
@@ -119,7 +122,7 @@ def Test_delfunction()
       ], 'E1084:')
 
   # Check that global :def function can be replaced and deleted
-  let lines =<< trim END
+  var lines =<< trim END
       vim9script
       def g:Global(): string
         return "yes"
@@ -168,59 +171,59 @@ def Test_delfunction()
 enddef
 
 def Test_wrong_type()
-  CheckDefFailure(['let var: list<nothing>'], 'E1010:')
-  CheckDefFailure(['let var: list<list<nothing>>'], 'E1010:')
-  CheckDefFailure(['let var: dict<nothing>'], 'E1010:')
-  CheckDefFailure(['let var: dict<dict<nothing>>'], 'E1010:')
+  CheckDefFailure(['var name: list<nothing>'], 'E1010:')
+  CheckDefFailure(['var name: list<list<nothing>>'], 'E1010:')
+  CheckDefFailure(['var name: dict<nothing>'], 'E1010:')
+  CheckDefFailure(['var name: dict<dict<nothing>>'], 'E1010:')
 
-  CheckDefFailure(['let var: dict<number'], 'E1009:')
-  CheckDefFailure(['let var: dict<list<number>'], 'E1009:')
+  CheckDefFailure(['var name: dict<number'], 'E1009:')
+  CheckDefFailure(['var name: dict<list<number>'], 'E1009:')
 
-  CheckDefFailure(['let var: ally'], 'E1010:')
-  CheckDefFailure(['let var: bram'], 'E1010:')
-  CheckDefFailure(['let var: cathy'], 'E1010:')
-  CheckDefFailure(['let var: dom'], 'E1010:')
-  CheckDefFailure(['let var: freddy'], 'E1010:')
-  CheckDefFailure(['let var: john'], 'E1010:')
-  CheckDefFailure(['let var: larry'], 'E1010:')
-  CheckDefFailure(['let var: ned'], 'E1010:')
-  CheckDefFailure(['let var: pam'], 'E1010:')
-  CheckDefFailure(['let var: sam'], 'E1010:')
-  CheckDefFailure(['let var: vim'], 'E1010:')
+  CheckDefFailure(['var name: ally'], 'E1010:')
+  CheckDefFailure(['var name: bram'], 'E1010:')
+  CheckDefFailure(['var name: cathy'], 'E1010:')
+  CheckDefFailure(['var name: dom'], 'E1010:')
+  CheckDefFailure(['var name: freddy'], 'E1010:')
+  CheckDefFailure(['var name: john'], 'E1010:')
+  CheckDefFailure(['var name: larry'], 'E1010:')
+  CheckDefFailure(['var name: ned'], 'E1010:')
+  CheckDefFailure(['var name: pam'], 'E1010:')
+  CheckDefFailure(['var name: sam'], 'E1010:')
+  CheckDefFailure(['var name: vim'], 'E1010:')
 
-  CheckDefFailure(['let Ref: number', 'Ref()'], 'E1085:')
-  CheckDefFailure(['let Ref: string', 'let res = Ref()'], 'E1085:')
+  CheckDefFailure(['var Ref: number', 'Ref()'], 'E1085:')
+  CheckDefFailure(['var Ref: string', 'var res = Ref()'], 'E1085:')
 enddef
 
 def Test_const()
-  CheckDefFailure(['final var = 234', 'var = 99'], 'E1018:')
-  CheckDefFailure(['final one = 234', 'let one = 99'], 'E1017:')
-  CheckDefFailure(['final list = [1, 2]', 'let list = [3, 4]'], 'E1017:')
+  CheckDefFailure(['final name = 234', 'name = 99'], 'E1018:')
+  CheckDefFailure(['final one = 234', 'var one = 99'], 'E1017:')
+  CheckDefFailure(['final list = [1, 2]', 'var list = [3, 4]'], 'E1017:')
   CheckDefFailure(['final two'], 'E1125:')
   CheckDefFailure(['final &option'], 'E996:')
 
-  let lines =<< trim END
+  var lines =<< trim END
     final list = [1, 2, 3]
     list[0] = 4
     list->assert_equal([4, 2, 3])
     const other = [5, 6, 7]
     other->assert_equal([5, 6, 7])
 
-    let varlist = [7, 8]
+    var varlist = [7, 8]
     const constlist = [1, varlist, 3]
     varlist[0] = 77
     # TODO: does not work yet
     # constlist[1][1] = 88
-    let cl = constlist[1]
+    var cl = constlist[1]
     cl[1] = 88
     constlist->assert_equal([1, [77, 88], 3])
 
-    let vardict = #{five: 5, six: 6}
+    var vardict = #{five: 5, six: 6}
     const constdict = #{one: 1, two: vardict, three: 3}
     vardict['five'] = 55
     # TODO: does not work yet
     # constdict['two']['six'] = 66
-    let cd = constdict['two']
+    var cd = constdict['two']
     cd['six'] = 66
     constdict->assert_equal(#{one: 1, two: #{five: 55, six: 66}, three: 3})
   END
@@ -228,7 +231,7 @@ def Test_const()
 enddef
 
 def Test_const_bang()
-  let lines =<< trim END
+  var lines =<< trim END
       const var = 234
       var = 99
   END
@@ -273,9 +276,9 @@ enddef
 
 
 def Test_block()
-  let outer = 1
+  var outer = 1
   {
-    let inner = 2
+    var inner = 2
     assert_equal(1, outer)
     assert_equal(2, inner)
   }
@@ -283,7 +286,7 @@ def Test_block()
 enddef
 
 def Test_block_failure()
-  CheckDefFailure(['{', 'let inner = 1', '}', 'echo inner'], 'E1001:')
+  CheckDefFailure(['{', 'var inner = 1', '}', 'echo inner'], 'E1001:')
   CheckDefFailure(['}'], 'E1025:')
   CheckDefFailure(['{', 'echo 1'], 'E1026:')
 enddef
@@ -293,7 +296,7 @@ func g:NoSuchFunc()
 endfunc
 
 def Test_try_catch()
-  let l = []
+  var l = []
   try # comment
     add(l, '1')
     throw 'wrong'
@@ -321,7 +324,7 @@ def Test_try_catch()
   endtry
   assert_equal(['1', 'caught', 'finally'], l)
 
-  let n: number
+  var n: number
   try
     n = l[3]
   catch /E684:/
@@ -358,7 +361,7 @@ def Test_try_catch()
   endtry
   assert_equal(121, n)
 
-  let d = #{one: 1}
+  var d = #{one: 1}
   try
     n = d[g:astring]
   catch /E716:/
@@ -387,7 +390,7 @@ def Test_try_catch()
   endtry
   assert_equal(255, n)
 
-  let nd: dict<any>
+  var nd: dict<any>
   try
     nd = {g:anumber: 1}
   catch /E1012:/
@@ -452,7 +455,7 @@ def Test_try_catch()
   endtry
   assert_equal(355, n)
 
-  let P = function('g:NoSuchFunc')
+  var P = function('g:NoSuchFunc')
   delfunc g:NoSuchFunc
   try
     echo P()
@@ -553,7 +556,7 @@ enddef
 
 def TryTwo(n: number): string
   try
-    let x = {}
+    var x = {}
   catch
   endtry
   return 'text'
@@ -564,7 +567,7 @@ def Test_try_catch_twice()
 enddef
 
 def Test_try_catch_match()
-  let seq = 'a'
+  var seq = 'a'
   try
     throw 'something'
   catch /nothing/
@@ -599,7 +602,7 @@ enddef
 
 def Test_throw_vimscript()
   # only checks line continuation
-  let lines =<< trim END
+  var lines =<< trim END
       vim9script
       try
         throw 'one'
@@ -613,7 +616,7 @@ enddef
 
 def Test_error_in_nested_function()
   # an error in a nested :function aborts executin in the calling :def function
-  let lines =<< trim END
+  var lines =<< trim END
       vim9script
       def Func()
         Error()
@@ -632,7 +635,7 @@ enddef
 def Test_cexpr_vimscript()
   # only checks line continuation
   set errorformat=File\ %f\ line\ %l
-  let lines =<< trim END
+  var lines =<< trim END
       vim9script
       cexpr 'File'
                 .. ' someFile' ..
@@ -645,7 +648,7 @@ enddef
 
 def Test_statusline_syntax()
   # legacy syntax is used for 'statusline'
-  let lines =<< trim END
+  var lines =<< trim END
       vim9script
       func g:Status()
         return '%{"x" is# "x"}'
@@ -659,9 +662,9 @@ enddef
 
 def Test_list_vimscript()
   # checks line continuation and comments
-  let lines =<< trim END
+  var lines =<< trim END
       vim9script
-      let mylist = [
+      var mylist = [
             'one',
             # comment
             'two', # empty line follows
@@ -702,7 +705,7 @@ endif
 
 let s:export_script_lines =<< trim END
   vim9script
-  let name: string = 'bob'
+  var name: string = 'bob'
   def Concat(arg: string): string
     return name .. arg
   enddef
@@ -710,8 +713,8 @@ let s:export_script_lines =<< trim END
   g:localname = name
 
   export const CONST = 1234
-  export let exported = 9876
-  export let exp_name = 'John'
+  export var exported = 9876
+  export var exp_name = 'John'
   export def Exported(): string
     return 'Exported'
   enddef
@@ -723,7 +726,7 @@ def Undo_export_script_lines()
 enddef
 
 def Test_vim9_import_export()
-  let import_script_lines =<< trim END
+  var import_script_lines =<< trim END
     vim9script
     import {exported, Exported} from './Xexport.vim'
     g:imported = exported
@@ -732,7 +735,7 @@ def Test_vim9_import_export()
     g:imported_func = Exported()
 
     def GetExported(): string
-      let local_dict = #{ref: Exported}
+      var local_dict = #{ref: Exported}
       return local_dict.ref()
     enddef
     g:funcref_result = GetExported()
@@ -769,7 +772,7 @@ def Test_vim9_import_export()
   delete('Ximport.vim')
 
   # similar, with line breaks
-  let import_line_break_script_lines =<< trim END
+  var import_line_break_script_lines =<< trim END
     vim9script
     import {
         exported,
@@ -797,7 +800,7 @@ def Test_vim9_import_export()
   delete('Ximport_lbr.vim')
 
   # import inside :def function
-  let import_in_def_lines =<< trim END
+  var import_in_def_lines =<< trim END
     vim9script
     def ImportInDef()
       import exported from './Xexport.vim'
@@ -816,7 +819,7 @@ def Test_vim9_import_export()
   unlet g:imported_added
   delete('Ximport2.vim')
 
-  let import_star_as_lines =<< trim END
+  var import_star_as_lines =<< trim END
     vim9script
     import * as Export from './Xexport.vim'
     def UseExport()
@@ -828,41 +831,41 @@ def Test_vim9_import_export()
   source Ximport.vim
   assert_equal(9883, g:imported)
 
-  let import_star_as_lines_no_dot =<< trim END
+  var import_star_as_lines_no_dot =<< trim END
     vim9script
     import * as Export from './Xexport.vim'
     def Func()
-      let dummy = 1
-      let imported = Export + dummy
+      var dummy = 1
+      var imported = Export + dummy
     enddef
     defcompile
   END
   writefile(import_star_as_lines_no_dot, 'Ximport.vim')
   assert_fails('source Ximport.vim', 'E1060:', '', 2, 'Func')
 
-  let import_star_as_lines_dot_space =<< trim END
+  var import_star_as_lines_dot_space =<< trim END
     vim9script
     import * as Export from './Xexport.vim'
     def Func()
-      let imported = Export . exported
+      var imported = Export . exported
     enddef
     defcompile
   END
   writefile(import_star_as_lines_dot_space, 'Ximport.vim')
   assert_fails('source Ximport.vim', 'E1074:', '', 1, 'Func')
 
-  let import_star_as_lines_missing_name =<< trim END
+  var import_star_as_lines_missing_name =<< trim END
     vim9script
     import * as Export from './Xexport.vim'
     def Func()
-      let imported = Export.
+      var imported = Export.
     enddef
     defcompile
   END
   writefile(import_star_as_lines_missing_name, 'Ximport.vim')
   assert_fails('source Ximport.vim', 'E1048:', '', 1, 'Func')
 
-  let import_star_as_lbr_lines =<< trim END
+  var import_star_as_lbr_lines =<< trim END
     vim9script
     import *
         as Export
@@ -877,7 +880,7 @@ def Test_vim9_import_export()
   source Ximport.vim
   assert_equal(9883, g:imported)
 
-  let import_star_lines =<< trim END
+  var import_star_lines =<< trim END
     vim9script
     import * from './Xexport.vim'
   END
@@ -885,7 +888,7 @@ def Test_vim9_import_export()
   assert_fails('source Ximport.vim', 'E1045:', '', 2, 'Ximport.vim')
 
   # try to import something that exists but is not exported
-  let import_not_exported_lines =<< trim END
+  var import_not_exported_lines =<< trim END
     vim9script
     import name from './Xexport.vim'
   END
@@ -893,9 +896,9 @@ def Test_vim9_import_export()
   assert_fails('source Ximport.vim', 'E1049:', '', 2, 'Ximport.vim')
 
   # try to import something that is already defined
-  let import_already_defined =<< trim END
+  var import_already_defined =<< trim END
     vim9script
-    let exported = 'something'
+    var exported = 'something'
     import exported from './Xexport.vim'
   END
   writefile(import_already_defined, 'Ximport.vim')
@@ -904,7 +907,7 @@ def Test_vim9_import_export()
   # try to import something that is already defined
   import_already_defined =<< trim END
     vim9script
-    let exported = 'something'
+    var exported = 'something'
     import * as exported from './Xexport.vim'
   END
   writefile(import_already_defined, 'Ximport.vim')
@@ -913,42 +916,42 @@ def Test_vim9_import_export()
   # try to import something that is already defined
   import_already_defined =<< trim END
     vim9script
-    let exported = 'something'
+    var exported = 'something'
     import {exported} from './Xexport.vim'
   END
   writefile(import_already_defined, 'Ximport.vim')
   assert_fails('source Ximport.vim', 'E1073:', '', 3, 'Ximport.vim')
 
   # import a very long name, requires making a copy
-  let import_long_name_lines =<< trim END
+  var import_long_name_lines =<< trim END
     vim9script
     import name012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 from './Xexport.vim'
   END
   writefile(import_long_name_lines, 'Ximport.vim')
   assert_fails('source Ximport.vim', 'E1048:', '', 2, 'Ximport.vim')
 
-  let import_no_from_lines =<< trim END
+  var import_no_from_lines =<< trim END
     vim9script
     import name './Xexport.vim'
   END
   writefile(import_no_from_lines, 'Ximport.vim')
   assert_fails('source Ximport.vim', 'E1070:', '', 2, 'Ximport.vim')
 
-  let import_invalid_string_lines =<< trim END
+  var import_invalid_string_lines =<< trim END
     vim9script
     import name from Xexport.vim
   END
   writefile(import_invalid_string_lines, 'Ximport.vim')
   assert_fails('source Ximport.vim', 'E1071:', '', 2, 'Ximport.vim')
 
-  let import_wrong_name_lines =<< trim END
+  var import_wrong_name_lines =<< trim END
     vim9script
     import name from './XnoExport.vim'
   END
   writefile(import_wrong_name_lines, 'Ximport.vim')
   assert_fails('source Ximport.vim', 'E1053:', '', 2, 'Ximport.vim')
 
-  let import_missing_comma_lines =<< trim END
+  var import_missing_comma_lines =<< trim END
     vim9script
     import {exported name} from './Xexport.vim'
   END
@@ -961,8 +964,8 @@ def Test_vim9_import_export()
 
   # Check that in a Vim9 script 'cpo' is set to the Vim default.
   set cpo&vi
-  let cpo_before = &cpo
-  let lines =<< trim END
+  var cpo_before = &cpo
+  var lines =<< trim END
     vim9script
     g:cpo_in_vim9script = &cpo
   END
@@ -981,7 +984,7 @@ endfunc
 
 def Test_import_export_expr_map()
   # check that :import and :export work when buffer is locked
-  let export_lines =<< trim END
+  var export_lines =<< trim END
     vim9script
     export def That(): string
       return 'yes'
@@ -989,7 +992,7 @@ def Test_import_export_expr_map()
   END
   writefile(export_lines, 'Xexport_that.vim')
 
-  let import_lines =<< trim END
+  var import_lines =<< trim END
     vim9script
     import That from './Xexport_that.vim'
     assert_equal('yes', That())
@@ -1007,13 +1010,13 @@ enddef
 def Test_import_in_filetype()
   # check that :import works when the buffer is locked
   mkdir('ftplugin', 'p')
-  let export_lines =<< trim END
+  var export_lines =<< trim END
     vim9script
-    export let That = 'yes'
+    export var That = 'yes'
   END
   writefile(export_lines, 'ftplugin/Xexport_ft.vim')
 
-  let import_lines =<< trim END
+  var import_lines =<< trim END
     vim9script
     import That from './Xexport_ft.vim'
     assert_equal('yes', That)
@@ -1021,7 +1024,7 @@ def Test_import_in_filetype()
   END
   writefile(import_lines, 'ftplugin/qf.vim')
 
-  let save_rtp = &rtp
+  var save_rtp = &rtp
   &rtp = getcwd() .. ',' .. &rtp
 
   filetype plugin on
@@ -1035,7 +1038,7 @@ def Test_import_in_filetype()
 enddef
 
 def Test_use_import_in_mapping()
-  let lines =<< trim END
+  var lines =<< trim END
       vim9script
       export def Funcx()
         g:result = 42
@@ -1062,12 +1065,12 @@ enddef
 def Test_vim9script_fails()
   CheckScriptFailure(['scriptversion 2', 'vim9script'], 'E1039:')
   CheckScriptFailure(['vim9script', 'scriptversion 2'], 'E1040:')
-  CheckScriptFailure(['export let some = 123'], 'E1042:')
+  CheckScriptFailure(['export var some = 123'], 'E1042:')
   CheckScriptFailure(['import some from "./Xexport.vim"'], 'E1048:')
-  CheckScriptFailure(['vim9script', 'export let g:some'], 'E1022:')
+  CheckScriptFailure(['vim9script', 'export var g:some'], 'E1022:')
   CheckScriptFailure(['vim9script', 'export echo 134'], 'E1043:')
 
-  CheckScriptFailure(['vim9script', 'let str: string', 'str = 1234'], 'E1012:')
+  CheckScriptFailure(['vim9script', 'var str: string', 'str = 1234'], 'E1012:')
   CheckScriptFailure(['vim9script', 'const str = "asdf"', 'str = "xxx"'], 'E46:')
 
   assert_fails('vim9script', 'E1038:')
@@ -1082,7 +1085,7 @@ func Test_import_fails_without_script()
 endfunc
 
 def Run_Test_import_fails_on_command_line()
-  let export =<< trim END
+  var export =<< trim END
     vim9script
     export def Foo(): number
         return 0
@@ -1090,7 +1093,7 @@ def Run_Test_import_fails_on_command_lin
   END
   writefile(export, 'XexportCmd.vim')
 
-  let buf = RunVimInTerminal('-c "import Foo from ''./XexportCmd.vim''"', #{
+  var buf = RunVimInTerminal('-c "import Foo from ''./XexportCmd.vim''"', #{
                 rows: 6, wait_for_ruler: 0})
   WaitForAssert({-> assert_match('^E1094:', term_getline(buf, 5))})
 
@@ -1099,16 +1102,16 @@ def Run_Test_import_fails_on_command_lin
 enddef
 
 def Test_vim9script_reload_import()
-  let lines =<< trim END
+  var lines =<< trim END
     vim9script
     const var = ''
-    let valone = 1234
+    var valone = 1234
     def MyFunc(arg: string)
        valone = 5678
     enddef
   END
-  let morelines =<< trim END
-    let valtwo = 222
+  var morelines =<< trim END
+    var valtwo = 222
     export def GetValtwo(): number
       return valtwo
     enddef
@@ -1118,7 +1121,7 @@ def Test_vim9script_reload_import()
   source Xreload.vim
   source Xreload.vim
 
-  let testlines =<< trim END
+  var testlines =<< trim END
     vim9script
     def TheFunc()
       import GetValtwo from './Xreload.vim'
@@ -1137,8 +1140,8 @@ def Test_vim9script_reload_import()
   # cannot declare a var twice
   lines =<< trim END
     vim9script
-    let valone = 1234
-    let valone = 5678
+    var valone = 1234
+    var valone = 5678
   END
   writefile(lines, 'Xreload.vim')
   assert_fails('source Xreload.vim', 'E1041:', '', 3, 'Xreload.vim')
@@ -1154,7 +1157,7 @@ enddef
 " Not exported function that is referenced needs to be accessed by the
 " script-local name.
 def Test_vim9script_funcref()
-  let sortlines =<< trim END
+  var sortlines =<< trim END
       vim9script
       def Compare(i1: number, i2: number): number
         return i2 - i1
@@ -1166,7 +1169,7 @@ def Test_vim9script_funcref()
   END
   writefile(sortlines, 'Xsort.vim')
 
-  let lines =<< trim END
+  var lines =<< trim END
     vim9script
     import FastSort from './Xsort.vim'
     def Test()
@@ -1183,7 +1186,7 @@ def Test_vim9script_funcref()
   delete('Xsort.vim')
   delete('Xscript.vim')
 
-  let Funcref = function('s:RetSome')
+  var Funcref = function('s:RetSome')
   assert_equal('some', Funcref())
 enddef
 
@@ -1191,7 +1194,7 @@ enddef
 " script where FastFilter() is called from, both as a string and as a direct
 " function reference.
 def Test_vim9script_funcref_other_script()
-  let filterLines =<< trim END
+  var filterLines =<< trim END
     vim9script
     export def FilterFunc(idx: number, val: number): bool
       return idx % 2 == 1
@@ -1205,15 +1208,15 @@ def Test_vim9script_funcref_other_script
   END
   writefile(filterLines, 'Xfilter.vim')
 
-  let lines =<< trim END
+  var lines =<< trim END
     vim9script
     import {FilterFunc, FastFilter, FastFilterDirect} from './Xfilter.vim'
     def Test()
-      let x: list<number> = FastFilter()
+      var x: list<number> = FastFilter()
     enddef
     Test()
     def TestDirect()
-      let x: list<number> = FastFilterDirect()
+      var x: list<number> = FastFilterDirect()
     enddef
     TestDirect()
   END
@@ -1222,13 +1225,13 @@ def Test_vim9script_funcref_other_script
 enddef
 
 def Test_vim9script_reload_delfunc()
-  let first_lines =<< trim END
+  var first_lines =<< trim END
     vim9script
     def FuncYes(): string
       return 'yes'
     enddef
   END
-  let withno_lines =<< trim END
+  var withno_lines =<< trim END
     def FuncNo(): string
       return 'no'
     enddef
@@ -1237,7 +1240,7 @@ def Test_vim9script_reload_delfunc()
       assert_equal('no', FuncNo())
     enddef
   END
-  let nono_lines =<< trim END
+  var nono_lines =<< trim END
     def g:DoCheck(no_exists: bool)
       assert_equal('yes', FuncYes())
       assert_fails('FuncNo()', 'E117:', '', 2, 'DoCheck')
@@ -1264,9 +1267,9 @@ enddef
 
 def Test_vim9script_reload_delvar()
   # write the script with a script-local variable
-  let lines =<< trim END
+  var lines =<< trim END
     vim9script
-    let var = 'string'
+    var name = 'string'
   END
   writefile(lines, 'XreloadVar.vim')
   source XreloadVar.vim
@@ -1275,7 +1278,7 @@ def Test_vim9script_reload_delvar()
   lines =<< trim END
     vim9script
     def Func()
-      let var = 'string'
+      var name = 'string'
     enddef
   END
   writefile(lines, 'XreloadVar.vim')
@@ -1285,7 +1288,7 @@ def Test_vim9script_reload_delvar()
 enddef
 
 def Test_import_absolute()
-  let import_lines = [
+  var import_lines = [
         'vim9script',
         'import exported from "' .. escape(getcwd(), '\') .. '/Xexport_abs.vim"',
         'def UseExported()',
@@ -1323,7 +1326,7 @@ def Test_import_absolute()
 enddef
 
 def Test_import_rtp()
-  let import_lines = [
+  var import_lines = [
         'vim9script',
         'import exported from "Xexport_rtp.vim"',
         'g:imported_rtp = exported',
@@ -1332,7 +1335,7 @@ def Test_import_rtp()
   mkdir('import')
   writefile(s:export_script_lines, 'import/Xexport_rtp.vim')
 
-  let save_rtp = &rtp
+  var save_rtp = &rtp
   &rtp = getcwd()
   source Ximport_rtp.vim
   &rtp = save_rtp
@@ -1346,7 +1349,7 @@ def Test_import_rtp()
 enddef
 
 def Test_import_compile_error()
-  let export_lines = [
+  var export_lines = [
         'vim9script',
         'export def ExpFunc(): string',
         '  return notDefined',
@@ -1354,7 +1357,7 @@ def Test_import_compile_error()
         ]
   writefile(export_lines, 'Xexported.vim')
 
-  let import_lines = [
+  var import_lines = [
         'vim9script',
         'import ExpFunc from "./Xexported.vim"',
         'def ImpFunc()',
@@ -1377,7 +1380,7 @@ def Test_import_compile_error()
 enddef
 
 def Test_func_redefine_error()
-  let lines = [
+  var lines = [
         'vim9script',
         'def Func()',
         '  eval [][0]',
@@ -1400,7 +1403,7 @@ def Test_func_redefine_error()
 enddef
 
 def Test_func_overrules_import_fails()
-  let export_lines =<< trim END
+  var export_lines =<< trim END
       vim9script
       export def Func()
         echo 'imported'
@@ -1408,7 +1411,7 @@ def Test_func_overrules_import_fails()
   END
   writefile(export_lines, 'XexportedFunc.vim')
 
-  let lines =<< trim END
+  var lines =<< trim END
     vim9script
     import Func from './XexportedFunc.vim'
     def Func()
@@ -1433,7 +1436,7 @@ def Test_func_overrules_import_fails()
 enddef
 
 def Test_func_redefine_fails()
-  let lines =<< trim END
+  var lines =<< trim END
     vim9script
     def Func()
       echo 'one'
@@ -1450,7 +1453,7 @@ def Test_func_redefine_fails()
       return 'foo'
       enddef
     def Func()
-      let  Foo = {-> 'lambda'}
+      var  Foo = {-> 'lambda'}
     enddef
     defcompile
   END
@@ -1459,7 +1462,7 @@ enddef
 
 def Test_fixed_size_list()
   # will be allocated as one piece of memory, check that changes work
-  let l = [1, 2, 3, 4]
+  var l = [1, 2, 3, 4]
   l->remove(0)
   l->add(5)
   l->insert(99, 1)
@@ -1485,7 +1488,7 @@ def Test_no_insert_xit()
 enddef
 
 def IfElse(what: number): string
-  let res = ''
+  var res = ''
   if what == 1
     res = "one"
   elseif what == 2
@@ -1514,7 +1517,7 @@ let g:bool_true = v:true
 let g:bool_false = v:false
 
 def Test_if_const_expr()
-  let res = false
+  var res = false
   if true ? true : false
     res = true
   endif
@@ -1643,7 +1646,7 @@ def Test_if_const_expr_fails()
 enddef
 
 def RunNested(i: number): number
-  let x: number = 0
+  var x: number = 0
   if i % 2
     if 1
       # comment
@@ -1671,21 +1674,21 @@ def Test_execute_cmd()
   execute "setline(1, 'execute-string')"
   assert_equal('execute-string', getline(1))
 
-  let cmd1 = 'setline(1,'
-  let cmd2 = '"execute-var")'
+  var cmd1 = 'setline(1,'
+  var cmd2 = '"execute-var")'
   execute cmd1 cmd2 # comment
   assert_equal('execute-var', getline(1))
 
   execute cmd1 cmd2 '|setline(1, "execute-var-string")'
   assert_equal('execute-var-string', getline(1))
 
-  let cmd_first = 'call '
-  let cmd_last = 'setline(1, "execute-var-var")'
+  var cmd_first = 'call '
+  var cmd_last = 'setline(1, "execute-var-var")'
   execute cmd_first .. cmd_last
   assert_equal('execute-var-var', getline(1))
   bwipe!
 
-  let n = true
+  var n = true
   execute 'echomsg' (n ? '"true"' : '"no"')
   assert_match('^true$', Screenline(&lines))
 
@@ -1699,7 +1702,7 @@ enddef
 
 def Test_execute_cmd_vimscript()
   # only checks line continuation
-  let lines =<< trim END
+  var lines =<< trim END
       vim9script
       execute 'g:someVar'
                 .. ' = ' ..
@@ -1719,8 +1722,8 @@ def Test_echo_cmd()
   echon "thing"
   assert_match('^something$', Screenline(&lines))
 
-  let str1 = 'some'
-  let str2 = 'more'
+  var str1 = 'some'
+  var str2 = 'more'
   echo str1 str2
   assert_match('^some more$', Screenline(&lines))
 
@@ -1739,7 +1742,7 @@ enddef
 
 def Test_echomsg_cmd_vimscript()
   # only checks line continuation
-  let lines =<< trim END
+  var lines =<< trim END
       vim9script
       echomsg 'here'
                 .. ' is ' ..
@@ -1759,7 +1762,7 @@ enddef
 
 def Test_echoerr_cmd_vimscript()
   # only checks line continuation
-  let lines =<< trim END
+  var lines =<< trim END
       vim9script
       try
         echoerr 'this'
@@ -1773,7 +1776,7 @@ def Test_echoerr_cmd_vimscript()
 enddef
 
 def Test_for_outside_of_function()
-  let lines =<< trim END
+  var lines =<< trim END
     vim9script
     new
     for var in range(0, 3)
@@ -1788,7 +1791,7 @@ def Test_for_outside_of_function()
 enddef
 
 def Test_for_loop()
-  let result = ''
+  var result = ''
   for cnt in range(7)
     if cnt == 4
       break
@@ -1800,7 +1803,7 @@ def Test_for_loop()
   endfor
   assert_equal('0_1_3_', result)
 
-  let concat = ''
+  var concat = ''
   for str in eval('["one", "two"]')
     concat ..= str
   endfor
@@ -1810,7 +1813,7 @@ enddef
 def Test_for_loop_fails()
   CheckDefFailure(['for # in range(5)'], 'E690:')
   CheckDefFailure(['for i In range(5)'], 'E690:')
-  CheckDefFailure(['let x = 5', 'for x in range(5)'], 'E1017:')
+  CheckDefFailure(['var x = 5', 'for x in range(5)'], 'E1017:')
   CheckScriptFailure(['def Func(arg: any)', 'for arg in range(5)', 'enddef', 'defcompile'], 'E1006:')
   CheckDefFailure(['for i in "text"'], 'E1012:')
   CheckDefFailure(['for i in xxx'], 'E1001:')
@@ -1819,8 +1822,8 @@ def Test_for_loop_fails()
 enddef
 
 def Test_while_loop()
-  let result = ''
-  let cnt = 0
+  var result = ''
+  var cnt = 0
   while cnt < 555
     if cnt == 3
       break
@@ -1845,8 +1848,8 @@ def Test_while_loop_fails()
 enddef
 
 def Test_interrupt_loop()
-  let caught = false
-  let x = 0
+  var caught = false
+  var x = 0
   try
     while 1
       x += 1
@@ -1864,14 +1867,14 @@ def Test_interrupt_loop()
 enddef
 
 def Test_automatic_line_continuation()
-  let mylist = [
+  var mylist = [
       'one',
       'two',
       'three',
       ] # comment
   assert_equal(['one', 'two', 'three'], mylist)
 
-  let mydict = {
+  var mydict = {
       'one': 1,
       'two': 2,
       'three':
@@ -2340,32 +2343,37 @@ def Test_vim9_comment_not_compiled()
 
   CheckScriptFailure([
       'vim9script',
-      'let g:var = 123',
+      'let var = 123',
+      ], 'E1126: Cannot use :let in Vim9 script')
+
+  CheckScriptFailure([
+      'vim9script',
+      'var g:var = 123',
       ], 'E1016: Cannot declare a global variable:')
 
   CheckScriptFailure([
       'vim9script',
-      'let b:var = 123',
+      'var b:var = 123',
       ], 'E1016: Cannot declare a buffer variable:')
 
   CheckScriptFailure([
       'vim9script',
-      'let w:var = 123',
+      'var w:var = 123',
       ], 'E1016: Cannot declare a window variable:')
 
   CheckScriptFailure([
       'vim9script',
-      'let t:var = 123',
+      'var t:var = 123',
       ], 'E1016: Cannot declare a tab variable:')
 
   CheckScriptFailure([
       'vim9script',
-      'let v:version = 123',
+      'var v:version = 123',
       ], 'E1016: Cannot declare a v: variable:')
 
   CheckScriptFailure([
       'vim9script',
-      'let $VARIABLE = "text"',
+      'var $VARIABLE = "text"',
       ], 'E1016: Cannot declare an environment variable:')
 
   CheckScriptFailure([
@@ -2406,12 +2414,12 @@ def Test_vim9_comment_not_compiled()
 
   CheckScriptSuccess([
       'vim9script',
-      'let v = 1 # comment5',
+      'var v = 1 # comment5',
       ])
 
   CheckScriptFailure([
       'vim9script',
-      'let v = 1# comment6',
+      'var v = 1# comment6',
       ], 'E15:')
 
   CheckScriptSuccess([
@@ -2439,7 +2447,7 @@ def Test_vim9_comment_not_compiled()
 enddef
 
 def Test_finish()
-  let lines =<< trim END
+  var lines =<< trim END
     vim9script
     g:res = 'one'
     if v:false | finish | endif
@@ -2456,7 +2464,7 @@ def Test_finish()
 enddef
 
 def Test_let_func_call()
-  let lines =<< trim END
+  var lines =<< trim END
     vim9script
     func GetValue()
       if exists('g:count')
@@ -2466,9 +2474,9 @@ def Test_let_func_call()
       endif
       return 'this'
     endfunc
-    let val: string = GetValue() 
+    var val: string = GetValue() 
     # env var is always a string
-    let env = $TERM
+    var env = $TERM
   END
   writefile(lines, 'Xfinished')
   source Xfinished
@@ -2480,32 +2488,32 @@ def Test_let_func_call()
 enddef
 
 def Test_let_missing_type()
-  let lines =<< trim END
+  var lines =<< trim END
     vim9script
-    let var = g:unknown
+    var name = g:unknown
   END
   CheckScriptFailure(lines, 'E121:')
 
   lines =<< trim END
     vim9script
-    let nr: number = 123
-    let var = nr
+    var nr: number = 123
+    var name = nr
   END
   CheckScriptSuccess(lines)
 enddef
 
 def Test_let_declaration()
-  let lines =<< trim END
+  var lines =<< trim END
     vim9script
-    let var: string
-    g:var_uninit = var
-    var = 'text'
-    g:var_test = var
+    var name: string
+    g:var_uninit = name
+    name = 'text'
+    g:var_test = name
     # prefixing s: is optional
-    s:var = 'prefixed'
-    g:var_prefixed = s:var
+    s:name = 'prefixed'
+    g:var_prefixed = s:name
 
-    let s:other: number
+    var s:other: number
     other = 1234
     g:other_var = other
 
@@ -2530,7 +2538,7 @@ def Test_let_declaration()
 enddef
 
 def Test_let_declaration_fails()
-  let lines =<< trim END
+  var lines =<< trim END
     vim9script
     final var: string
   END
@@ -2544,41 +2552,41 @@ def Test_let_declaration_fails()
 
   lines =<< trim END
     vim9script
-    let 9var: string
+    var 9var: string
   END
   CheckScriptFailure(lines, 'E475:')
 enddef
 
 def Test_let_type_check()
-  let lines =<< trim END
+  var lines =<< trim END
     vim9script
-    let var: string
-    var = 1234
+    var name: string
+    name = 1234
   END
   CheckScriptFailure(lines, 'E1012:')
 
   lines =<< trim END
     vim9script
-    let var:string
+    var name:string
   END
   CheckScriptFailure(lines, 'E1069:')
 
   lines =<< trim END
     vim9script
-    let var: asdf
+    var name: asdf
   END
   CheckScriptFailure(lines, 'E1010:')
 
   lines =<< trim END
     vim9script
-    let s:l: list<number>
+    var s:l: list<number>
     s:l = []
   END
   CheckScriptSuccess(lines)
 
   lines =<< trim END
     vim9script
-    let s:d: dict<number>
+    var s:d: dict<number>
     s:d = {}
   END
   CheckScriptSuccess(lines)
@@ -2587,28 +2595,28 @@ enddef
 let g:dict_number = #{one: 1, two: 2}
 
 def Test_let_list_dict_type()
-  let ll: list<number>
+  var ll: list<number>
   ll = [1, 2, 2, 3, 3, 3]->uniq()
   ll->assert_equal([1, 2, 3])
 
-  let dd: dict<number>
+  var dd: dict<number>
   dd = g:dict_number
   dd->assert_equal(g:dict_number)
 
-  let lines =<< trim END
-      let ll: list<number>
+  var lines =<< trim END
+      var ll: list<number>
       ll = [1, 2, 3]->map('"one"')
   END
   CheckDefExecFailure(lines, 'E1012: Type mismatch; expected list<number> but got list<string>')
 enddef
 
 def Test_forward_declaration()
-  let lines =<< trim END
+  var lines =<< trim END
     vim9script
     def GetValue(): string
       return theVal
     enddef
-    let theVal = 'something'
+    var theVal = 'something'
     g:initVal = GetValue()
     theVal = 'else'
     g:laterVal = GetValue()
@@ -2624,7 +2632,7 @@ def Test_forward_declaration()
 enddef
 
 def Test_source_vim9_from_legacy()
-  let legacy_lines =<< trim END
+  var legacy_lines =<< trim END
     source Xvim9_script.vim
 
     call assert_false(exists('local'))
@@ -2645,11 +2653,11 @@ def Test_source_vim9_from_legacy()
   END
   writefile(legacy_lines, 'Xlegacy_script.vim')
 
-  let vim9_lines =<< trim END
+  var vim9_lines =<< trim END
     vim9script
-    let local = 'local'
+    var local = 'local'
     g:global = 'global'
-    export let exported = 'exported'
+    export var exported = 'exported'
     export def GetText(): string
        return 'text'
     enddef
@@ -2669,7 +2677,7 @@ func Test_vim9script_not_global()
   " check that items defined in Vim9 script are script-local, not global
   let vim9lines =<< trim END
     vim9script
-    let var = 'local'
+    var name = 'local'
     func TheFunc()
       echo 'local'
     endfunc
@@ -2709,7 +2717,7 @@ enddef
 
 " test using a vim9script that is auto-loaded from an autocmd
 def Test_vim9_autoload()
-  let lines =<< trim END
+  var lines =<< trim END
      vim9script
      def foo#test()
          echomsg getreg('"')
@@ -2718,7 +2726,7 @@ def Test_vim9_autoload()
 
   mkdir('Xdir/autoload', 'p')
   writefile(lines, 'Xdir/autoload/foo.vim')
-  let save_rtp = &rtp
+  var save_rtp = &rtp
   exe 'set rtp^=' .. getcwd() .. '/Xdir'
   augroup test
     autocmd TextYankPost * call foo#test()
@@ -2735,7 +2743,7 @@ enddef
 
 " This was causing a crash because suppress_errthrow wasn't reset.
 def Test_vim9_autoload_error()
-  let lines =<< trim END
+  var lines =<< trim END
       vim9script
       def crash#func()
           try
@@ -2775,7 +2783,7 @@ enddef
 def Test_script_var_in_autocmd()
   # using a script variable from an autocommand, defined in a :def function in a
   # legacy Vim script, cannot check the variable type.
-  let lines =<< trim END
+  var lines =<< trim END
     let s:counter = 1
     def s:Func()
       au! CursorHold
@@ -2793,17 +2801,17 @@ def Test_cmdline_win()
   # if the Vim syntax highlighting uses Vim9 constructs they can be used from
   # the command line window.
   mkdir('rtp/syntax', 'p')
-  let export_lines =<< trim END
+  var export_lines =<< trim END
     vim9script
-    export let That = 'yes'
+    export var That = 'yes'
   END
   writefile(export_lines, 'rtp/syntax/Xexport.vim')
-  let import_lines =<< trim END
+  var import_lines =<< trim END
     vim9script
     import That from './Xexport.vim'
   END
   writefile(import_lines, 'rtp/syntax/vim.vim')
-  let save_rtp = &rtp
+  var save_rtp = &rtp
   &rtp = getcwd() .. '/rtp' .. ',' .. &rtp
   syntax on
   augroup CmdWin
@@ -2830,9 +2838,9 @@ def Test_invalid_sid()
 enddef
 
 def Test_unset_any_variable()
-  let lines =<< trim END
-    let var: any
-    assert_equal(0, var)
+  var lines =<< trim END
+    var name: any
+    assert_equal(0, name)
   END
   CheckDefAndScriptSuccess(lines)
 enddef
@@ -2846,7 +2854,7 @@ endfunc
 
 def Run_Test_define_func_at_command_line()
   # run in a separate Vim instance to avoid the script context
-  let lines =<< trim END
+  var lines =<< trim END
     func CheckAndQuit()
       call assert_fails('call Afunc()', 'E117: Unknown function: Bfunc')
       call writefile(['errors: ' .. string(v:errors)], 'Xdidcmd')
@@ -2854,7 +2862,7 @@ def Run_Test_define_func_at_command_line
   END
   writefile([''], 'Xdidcmd')
   writefile(lines, 'XcallFunc')
-  let buf = RunVimInTerminal('-S XcallFunc', #{rows: 6})
+  var buf = RunVimInTerminal('-S XcallFunc', #{rows: 6})
   # define Afunc() on the command line
   term_sendkeys(buf, ":def Afunc()\<CR>Bfunc()\<CR>enddef\<CR>")
   term_sendkeys(buf, ":call CheckAndQuit()\<CR>")
@@ -2874,7 +2882,7 @@ def Test_substitute_cmd()
   bwipe!
 
   # also when the context is Vim9 script
-  let lines =<< trim END
+  var lines =<< trim END
     vim9script
     new
     setline(1, 'something')
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -3214,19 +3214,20 @@ def_function(exarg_T *eap, char_u *name_
 		is_heredoc = TRUE;
 	    }
 
-	    // Check for ":let v =<< [trim] EOF"
-	    //       and ":let [a, b] =<< [trim] EOF"
+	    // Check for ":cmd v =<< [trim] EOF"
+	    //       and ":cmd [a, b] =<< [trim] EOF"
+	    // Where "cmd" can be "let", "var", "final" or "const".
 	    arg = skipwhite(skiptowhite(p));
 	    if (*arg == '[')
 		arg = vim_strchr(arg, ']');
 	    if (arg != NULL)
 	    {
 		arg = skipwhite(skiptowhite(arg));
-		if ( arg[0] == '=' && arg[1] == '<' && arg[2] =='<'
-			&& ((p[0] == 'l'
-				&& p[1] == 'e'
-				&& (!ASCII_ISALNUM(p[2])
-				    || (p[2] == 't' && !ASCII_ISALNUM(p[3]))))))
+		if (arg[0] == '=' && arg[1] == '<' && arg[2] =='<'
+			&& (checkforcmd(&p, "let", 2)
+			    || checkforcmd(&p, "var", 3)
+			    || checkforcmd(&p, "final", 5)
+			    || checkforcmd(&p, "const", 5)))
 		{
 		    p = skipwhite(arg + 3);
 		    if (STRNCMP(p, "trim", 4) == 0)
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1756,
+/**/
     1755,
 /**/
     1754,
--- a/src/vim.h
+++ b/src/vim.h
@@ -1992,7 +1992,8 @@ typedef int sock_T;
 #define VV_ECHOSPACE	93
 #define VV_ARGV		94
 #define VV_COLLATE      95
-#define VV_LEN		96	// number of v: vars
+#define VV_DISALLOW_LET 96	// TODO: remove again
+#define VV_LEN		97	// number of v: vars
 
 // used for v_number in VAR_BOOL and VAR_SPECIAL
 #define VVAL_FALSE	0L	// VAR_BOOL
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -6979,6 +6979,12 @@ compile_def_function(ufunc_T *ufunc, int
 		    break;
 
 	    case CMD_let:
+		    if (get_vim_var_nr(VV_DISALLOW_LET))
+		    {
+			emsg(_(e_cannot_use_let_in_vim9_script));
+			break;
+		    }
+		    // FALLTHROUGH
 	    case CMD_var:
 	    case CMD_final:
 	    case CMD_const: