mirror of
https://github.com/vim/vim.git
synced 2025-09-24 03:44:06 -04:00
patch 9.0.1909: Vim9: problem calling class method from other class
Problem: Vim9: problem calling class method from other class Solution: Fix this problem, fix readonly object access, update error messages. Calling a class method from another method without the class name prefix doesn't work properly. A readonly object variable is modifiable outside the class using a nested object assignment. Remove the unused E1338 error message. Update error messages. closes: #13116 Signed-off-by: Christian Brabandt <cb@256bit.org> Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
This commit is contained in:
committed by
Christian Brabandt
parent
d25021cf03
commit
00cd18222e
@@ -4418,13 +4418,11 @@ E1325 vim9class.txt /*E1325*
|
|||||||
E1326 vim9class.txt /*E1326*
|
E1326 vim9class.txt /*E1326*
|
||||||
E1327 vim9class.txt /*E1327*
|
E1327 vim9class.txt /*E1327*
|
||||||
E1328 vim9class.txt /*E1328*
|
E1328 vim9class.txt /*E1328*
|
||||||
E1329 vim9class.txt /*E1329*
|
|
||||||
E133 userfunc.txt /*E133*
|
E133 userfunc.txt /*E133*
|
||||||
E1330 vim9class.txt /*E1330*
|
E1330 vim9class.txt /*E1330*
|
||||||
E1331 vim9class.txt /*E1331*
|
E1331 vim9class.txt /*E1331*
|
||||||
E1332 vim9class.txt /*E1332*
|
E1332 vim9class.txt /*E1332*
|
||||||
E1333 vim9class.txt /*E1333*
|
E1333 vim9class.txt /*E1333*
|
||||||
E1334 vim9class.txt /*E1334*
|
|
||||||
E1335 vim9class.txt /*E1335*
|
E1335 vim9class.txt /*E1335*
|
||||||
E1336 options.txt /*E1336*
|
E1336 options.txt /*E1336*
|
||||||
E1337 vim9class.txt /*E1337*
|
E1337 vim9class.txt /*E1337*
|
||||||
@@ -4458,9 +4456,27 @@ E1361 syntax.txt /*E1361*
|
|||||||
E1362 vim9class.txt /*E1362*
|
E1362 vim9class.txt /*E1362*
|
||||||
E1363 vim9class.txt /*E1363*
|
E1363 vim9class.txt /*E1363*
|
||||||
E1364 recover.txt /*E1364*
|
E1364 recover.txt /*E1364*
|
||||||
|
E1365 vim9class.txt /*E1365*
|
||||||
|
E1366 vim9class.txt /*E1366*
|
||||||
|
E1367 vim9class.txt /*E1367*
|
||||||
|
E1368 vim9class.txt /*E1368*
|
||||||
|
E1369 vim9class.txt /*E1369*
|
||||||
E137 starting.txt /*E137*
|
E137 starting.txt /*E137*
|
||||||
E1370 vim9class.txt /*E1370*
|
E1370 vim9class.txt /*E1370*
|
||||||
|
E1371 vim9class.txt /*E1371*
|
||||||
|
E1372 vim9class.txt /*E1372*
|
||||||
|
E1373 vim9class.txt /*E1373*
|
||||||
|
E1374 vim9class.txt /*E1374*
|
||||||
|
E1375 vim9class.txt /*E1375*
|
||||||
|
E1376 vim9class.txt /*E1376*
|
||||||
|
E1377 vim9class.txt /*E1377*
|
||||||
|
E1378 vim9class.txt /*E1378*
|
||||||
|
E1379 vim9class.txt /*E1379*
|
||||||
E138 starting.txt /*E138*
|
E138 starting.txt /*E138*
|
||||||
|
E1380 vim9class.txt /*E1380*
|
||||||
|
E1381 vim9class.txt /*E1381*
|
||||||
|
E1382 vim9class.txt /*E1382*
|
||||||
|
E1383 vim9class.txt /*E1383*
|
||||||
E139 message.txt /*E139*
|
E139 message.txt /*E139*
|
||||||
E140 message.txt /*E140*
|
E140 message.txt /*E140*
|
||||||
E1400 builtin.txt /*E1400*
|
E1400 builtin.txt /*E1400*
|
||||||
@@ -6367,7 +6383,7 @@ cino-{ indent.txt /*cino-{*
|
|||||||
cino-} indent.txt /*cino-}*
|
cino-} indent.txt /*cino-}*
|
||||||
cinoptions-values indent.txt /*cinoptions-values*
|
cinoptions-values indent.txt /*cinoptions-values*
|
||||||
class vim9class.txt /*class*
|
class vim9class.txt /*class*
|
||||||
class-function vim9class.txt /*class-function*
|
class-method vim9class.txt /*class-method*
|
||||||
clear-undo undo.txt /*clear-undo*
|
clear-undo undo.txt /*clear-undo*
|
||||||
clearmatches() builtin.txt /*clearmatches()*
|
clearmatches() builtin.txt /*clearmatches()*
|
||||||
client-server remote.txt /*client-server*
|
client-server remote.txt /*client-server*
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
*vim9class.txt* For Vim version 9.0. Last change: 2023 Mar 22
|
*vim9class.txt* For Vim version 9.0. Last change: 2023 Sep 18
|
||||||
|
|
||||||
|
|
||||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||||
@@ -11,7 +11,7 @@ Vim9 classes, objects, interfaces, types and enums.
|
|||||||
|
|
||||||
1. Overview |Vim9-class-overview|
|
1. Overview |Vim9-class-overview|
|
||||||
2. A simple class |Vim9-simple-class|
|
2. A simple class |Vim9-simple-class|
|
||||||
3. Class members and functions |Vim9-class-member|
|
3. Class variables and methods |Vim9-class-member|
|
||||||
4. Using an abstract class |Vim9-abstract-class|
|
4. Using an abstract class |Vim9-abstract-class|
|
||||||
5. Using an interface |Vim9-using-interface|
|
5. Using an interface |Vim9-using-interface|
|
||||||
6. More class details |Vim9-class|
|
6. More class details |Vim9-class|
|
||||||
@@ -139,11 +139,13 @@ changed at any time, you can make it public: >
|
|||||||
|
|
||||||
Now you don't need the SetLnum(), SetCol() and SetPosition() methods, setting
|
Now you don't need the SetLnum(), SetCol() and SetPosition() methods, setting
|
||||||
"pos.lnum" directly above will no longer give an error.
|
"pos.lnum" directly above will no longer give an error.
|
||||||
*E1334*
|
*E1326*
|
||||||
If you try to set an object member that doesn't exist you get an error: >
|
If you try to set an object member that doesn't exist you get an error: >
|
||||||
pos.other = 9
|
pos.other = 9
|
||||||
< E1334: Object member not found: other ~
|
< E1326: Member not found on object "TextPosition": other ~
|
||||||
|
|
||||||
|
*E1376*
|
||||||
|
A object member cannot be accessed using the class name.
|
||||||
|
|
||||||
Private members ~
|
Private members ~
|
||||||
*E1332* *E1333*
|
*E1332* *E1333*
|
||||||
@@ -176,9 +178,9 @@ number to the total number of lines: >
|
|||||||
endif
|
endif
|
||||||
return this._lnum
|
return this._lnum
|
||||||
enddef
|
enddef
|
||||||
|
<
|
||||||
|
|
||||||
Private methods ~
|
Private methods ~
|
||||||
|
*E1366*
|
||||||
If you want object methods to be accessible only from other methods of the
|
If you want object methods to be accessible only from other methods of the
|
||||||
same class and not used from outside the class, then you can make them
|
same class and not used from outside the class, then you can make them
|
||||||
private. This is done by prefixing the method name with an underscore: >
|
private. This is done by prefixing the method name with an underscore: >
|
||||||
@@ -252,16 +254,17 @@ If the class extends a parent class, the same thing happens. In the second
|
|||||||
step the members of the parent class are done first. There is no need to call
|
step the members of the parent class are done first. There is no need to call
|
||||||
"super()" or "new()" on the parent.
|
"super()" or "new()" on the parent.
|
||||||
|
|
||||||
|
*E1365*
|
||||||
When defining the new() method the return type should not be specified. It
|
When defining the new() method the return type should not be specified. It
|
||||||
always returns an object of the class.
|
always returns an object of the class.
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
|
|
||||||
3. class members and functions *Vim9-class-member*
|
3. Class Variables and Methods *Vim9-class-member*
|
||||||
|
|
||||||
*:static* *E1337* *E1338*
|
*:static* *E1337* *E1338* *E1368*
|
||||||
Class members are declared with "static". They are used by the name without a
|
Class members are declared with "static". They are used by the name without a
|
||||||
prefix: >
|
prefix in the class where they are defined: >
|
||||||
|
|
||||||
class OtherThing
|
class OtherThing
|
||||||
this.size: number
|
this.size: number
|
||||||
@@ -275,6 +278,10 @@ prefix: >
|
|||||||
Since the name is used as-is, shadowing the name by a function argument name
|
Since the name is used as-is, shadowing the name by a function argument name
|
||||||
or local variable name is not allowed.
|
or local variable name is not allowed.
|
||||||
|
|
||||||
|
*E1374* *E1375*
|
||||||
|
To access a class member outside of the class where it is defined, the class
|
||||||
|
name prefix must be used. A class member cannot be accessed using an object.
|
||||||
|
|
||||||
Just like object members the access can be made private by using an underscore
|
Just like object members the access can be made private by using an underscore
|
||||||
as the first character in the name, and it can be made public by prefixing
|
as the first character in the name, and it can be made public by prefixing
|
||||||
"public": >
|
"public": >
|
||||||
@@ -285,10 +292,11 @@ as the first character in the name, and it can be made public by prefixing
|
|||||||
public static result: number # anybody can read and write
|
public static result: number # anybody can read and write
|
||||||
endclass
|
endclass
|
||||||
<
|
<
|
||||||
*class-function*
|
*class-method*
|
||||||
Class functions are also declared with "static". They have no access to
|
Class methods are also declared with "static". They can use the class
|
||||||
object members, they cannot use the "this" keyword. >
|
variables but they have no access to the object variables, they cannot use the
|
||||||
|
"this" keyword.
|
||||||
|
>
|
||||||
class OtherThing
|
class OtherThing
|
||||||
this.size: number
|
this.size: number
|
||||||
static totalSize: number
|
static totalSize: number
|
||||||
@@ -301,8 +309,9 @@ object members, they cannot use the "this" keyword. >
|
|||||||
enddef
|
enddef
|
||||||
endclass
|
endclass
|
||||||
|
|
||||||
Inside the class the function can be called by name directly, outside the
|
Inside the class the class method can be called by name directly, outside the
|
||||||
class the class name must be prefixed: `OtherThing.ClearTotalSize()`.
|
class the class name must be prefixed: `OtherThing.ClearTotalSize()`. To use
|
||||||
|
a super class method in a child class, the class name must be prefixed.
|
||||||
|
|
||||||
Just like object methods the access can be made private by using an underscore
|
Just like object methods the access can be made private by using an underscore
|
||||||
as the first character in the method name: >
|
as the first character in the method name: >
|
||||||
@@ -312,7 +321,7 @@ as the first character in the method name: >
|
|||||||
echo "Foo"
|
echo "Foo"
|
||||||
enddef
|
enddef
|
||||||
def Bar()
|
def Bar()
|
||||||
OtherThing._Foo()
|
_Foo()
|
||||||
enddef
|
enddef
|
||||||
endclass
|
endclass
|
||||||
<
|
<
|
||||||
@@ -320,6 +329,31 @@ as the first character in the method name: >
|
|||||||
Note that constructors cannot be declared as "static", because they always
|
Note that constructors cannot be declared as "static", because they always
|
||||||
are.
|
are.
|
||||||
|
|
||||||
|
To access the class methods and class variables of a super class in an
|
||||||
|
extended class, the class name prefix should be used just as from anywhere
|
||||||
|
outside of the defining class: >
|
||||||
|
|
||||||
|
vim9script
|
||||||
|
class Vehicle
|
||||||
|
static nextID: number = 1000
|
||||||
|
static def GetID(): number
|
||||||
|
nextID += 1
|
||||||
|
return nextID
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
class Car extends Vehicle
|
||||||
|
this.myID: number
|
||||||
|
def new()
|
||||||
|
this.myID = Vehicle.GetID()
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
<
|
||||||
|
Class variables and methods are not inherited by a child class. A child class
|
||||||
|
can declare a static variable or a method with the same name as the one in the
|
||||||
|
super class. Depending on the class where the member is used the
|
||||||
|
corresponding class member will be used. The type of the class member in a
|
||||||
|
child class can be different from that in the super class.
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
|
|
||||||
4. Using an abstract class *Vim9-abstract-class*
|
4. Using an abstract class *Vim9-abstract-class*
|
||||||
@@ -358,16 +392,19 @@ class, for which objects can be created. Example: >
|
|||||||
An abstract class is defined the same way as a normal class, except that it
|
An abstract class is defined the same way as a normal class, except that it
|
||||||
does not have any new() method. *E1359*
|
does not have any new() method. *E1359*
|
||||||
|
|
||||||
*abstract-method*
|
*abstract-method* *E1371* *E1372*
|
||||||
An abstract method can be defined in an abstract class by using the "abstract"
|
An abstract method can be defined in an abstract class by using the "abstract"
|
||||||
prefix when defining the function: >
|
prefix when defining the function: >
|
||||||
|
|
||||||
abstract class Shape
|
abstract class Shape
|
||||||
abstract def Draw()
|
abstract def Draw()
|
||||||
|
abstract static def SetColor()
|
||||||
endclass
|
endclass
|
||||||
|
<
|
||||||
|
*E1373*
|
||||||
A class extending the abstract class must implement all the abstract methods.
|
A class extending the abstract class must implement all the abstract methods.
|
||||||
Class methods in an abstract class can also be abstract methods.
|
The signature (arguments, argument types and return type) must be exactly the
|
||||||
|
same. Class methods in an abstract class can also be abstract methods.
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
|
|
||||||
@@ -409,9 +446,10 @@ a number. This example extends the one above: >
|
|||||||
return this.base * this.height / 2
|
return this.base * this.height / 2
|
||||||
enddef
|
enddef
|
||||||
endclass
|
endclass
|
||||||
|
<
|
||||||
|
*E1348* *E1349* *E1367* *E1382* *E1383*
|
||||||
If a class declares to implement an interface, all the items specified in the
|
If a class declares to implement an interface, all the items specified in the
|
||||||
interface must appear in the class, with the same types. *E1348* *E1349*
|
interface must appear in the class, with the same types.
|
||||||
|
|
||||||
The interface name can be used as a type: >
|
The interface name can be used as a type: >
|
||||||
|
|
||||||
@@ -422,7 +460,14 @@ The interface name can be used as a type: >
|
|||||||
for shape in shapes
|
for shape in shapes
|
||||||
echo $'the surface is {shape.Surface()}'
|
echo $'the surface is {shape.Surface()}'
|
||||||
endfor
|
endfor
|
||||||
|
<
|
||||||
|
*E1378* *E1379* *E1380*
|
||||||
|
An interface can have only instance variables (read-only and read-write
|
||||||
|
access) and methods. An interface cannot contain private variables, private
|
||||||
|
methods, class variables and class methods.
|
||||||
|
|
||||||
|
An interface can extend another interface using "extends". The sub-interface
|
||||||
|
inherits all the instance variables and methods from the super interface.
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
|
|
||||||
@@ -464,9 +509,12 @@ once. They can appear in any order, although this order is recommended: >
|
|||||||
extends ClassName
|
extends ClassName
|
||||||
implements InterfaceName, OtherInterface
|
implements InterfaceName, OtherInterface
|
||||||
specifies SomeInterface
|
specifies SomeInterface
|
||||||
< *E1355*
|
< *E1355* *E1369*
|
||||||
Each member and function name can be used only once. It is not possible to
|
Each member and function name can be used only once. It is not possible to
|
||||||
define a function with the same name and different type of arguments.
|
define a function with the same name and different type of arguments. It is
|
||||||
|
not possible to use a public and private member variable with the same name.
|
||||||
|
A object variable name used in a super class cannot be reused in a child
|
||||||
|
class.
|
||||||
|
|
||||||
|
|
||||||
Member Initialization ~
|
Member Initialization ~
|
||||||
@@ -491,6 +539,10 @@ Object methods of the base class can be overruled. The signature (arguments,
|
|||||||
argument types and return type) must be exactly the same. The method of the
|
argument types and return type) must be exactly the same. The method of the
|
||||||
base class can be called by prefixing "super.".
|
base class can be called by prefixing "super.".
|
||||||
|
|
||||||
|
*E1377*
|
||||||
|
The access level of a method (public or private) in a child class should be
|
||||||
|
the same as the super class.
|
||||||
|
|
||||||
Other object methods of the base class are taken over by the child class.
|
Other object methods of the base class are taken over by the child class.
|
||||||
|
|
||||||
Class functions, including functions starting with "new", can be overruled,
|
Class functions, including functions starting with "new", can be overruled,
|
||||||
@@ -523,18 +575,26 @@ interface, which is often done in many languages, especially Java.
|
|||||||
|
|
||||||
|
|
||||||
Items in a class ~
|
Items in a class ~
|
||||||
*E1318* *E1325* *E1326*
|
*E1318* *E1325*
|
||||||
Inside a class, in between `:class` and `:endclass`, these items can appear:
|
Inside a class, in between `:class` and `:endclass`, these items can appear:
|
||||||
- An object member declaration: >
|
- An object member declaration: >
|
||||||
this._memberName: memberType
|
this._privateMemberName: memberType
|
||||||
this.memberName: memberType
|
this.readonlyMemberName: memberType
|
||||||
public this.memberName: memberType
|
public this.readwriteMemberName: memberType
|
||||||
|
- A class member declaration: >
|
||||||
|
static this._privateMemberName: memberType
|
||||||
|
static this.readonlyMemberName: memberType
|
||||||
|
static public this.readwriteMemberName: memberType
|
||||||
- A constructor method: >
|
- A constructor method: >
|
||||||
def new(arguments)
|
def new(arguments)
|
||||||
def newName(arguments)
|
def newName(arguments)
|
||||||
|
- A class method: >
|
||||||
|
static def SomeMethod(arguments)
|
||||||
|
static def _PrivateMethod(arguments)
|
||||||
- An object method: >
|
- An object method: >
|
||||||
def SomeMethod(arguments)
|
def SomeMethod(arguments)
|
||||||
< *E1329*
|
def _PrivateMethod(arguments)
|
||||||
|
|
||||||
For the object member the type must be specified. The best way is to do this
|
For the object member the type must be specified. The best way is to do this
|
||||||
explicitly with ": {type}". For simple types you can also use an initializer,
|
explicitly with ": {type}". For simple types you can also use an initializer,
|
||||||
such as "= 123", and Vim will see that the type is a number. Avoid doing this
|
such as "= 123", and Vim will see that the type is a number. Avoid doing this
|
||||||
@@ -573,6 +633,8 @@ An interface name must start with an uppercase letter. *E1343*
|
|||||||
The "Has" prefix can be used to make it easier to guess this is an interface
|
The "Has" prefix can be used to make it easier to guess this is an interface
|
||||||
name, with a hint about what it provides.
|
name, with a hint about what it provides.
|
||||||
An interface can only be defined in a |Vim9| script file. *E1342*
|
An interface can only be defined in a |Vim9| script file. *E1342*
|
||||||
|
An interface cannot "implement" another interface but it can "extend" another
|
||||||
|
interface. *E1381*
|
||||||
|
|
||||||
|
|
||||||
null object ~
|
null object ~
|
||||||
|
11
src/errors.h
11
src/errors.h
@@ -3418,8 +3418,7 @@ EXTERN char e_internal_error_shortmess_too_long[]
|
|||||||
#ifdef FEAT_EVAL
|
#ifdef FEAT_EVAL
|
||||||
EXTERN char e_class_member_str_not_found_in_class_str[]
|
EXTERN char e_class_member_str_not_found_in_class_str[]
|
||||||
INIT(= N_("E1337: Class member \"%s\" not found in class \"%s\""));
|
INIT(= N_("E1337: Class member \"%s\" not found in class \"%s\""));
|
||||||
EXTERN char e_interface_static_direct_access_str[]
|
// E1338 unused
|
||||||
INIT(= N_("E1338: Cannot directly access interface \"%s\" static member \"%s\""));
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef FEAT_PROP_POPUP
|
#ifdef FEAT_PROP_POPUP
|
||||||
EXTERN char e_cannot_add_textprop_with_text_after_using_textprop_with_negative_id[]
|
EXTERN char e_cannot_add_textprop_with_text_after_using_textprop_with_negative_id[]
|
||||||
@@ -3506,11 +3505,11 @@ EXTERN char e_object_member_str_accessible_only_using_object_str[]
|
|||||||
INIT(= N_("E1376: Object member \"%s\" accessible only using class \"%s\" object"));
|
INIT(= N_("E1376: Object member \"%s\" accessible only using class \"%s\" object"));
|
||||||
EXTERN char e_method_str_of_class_str_has_different_access[]
|
EXTERN char e_method_str_of_class_str_has_different_access[]
|
||||||
INIT(= N_("E1377: Access level of method \"%s\" is different in class \"%s\""));
|
INIT(= N_("E1377: Access level of method \"%s\" is different in class \"%s\""));
|
||||||
EXTERN char e_static_cannot_be_used_in_interface[]
|
EXTERN char e_static_member_not_supported_in_interface[]
|
||||||
INIT(= N_("E1378: Static cannot be used in an interface"));
|
INIT(= N_("E1378: Static member not supported in an interface"));
|
||||||
EXTERN char e_private_variable_str_in_interface[]
|
EXTERN char e_private_variable_not_supported_in_interface[]
|
||||||
INIT(= N_("E1379: Private variable not supported in an interface"));
|
INIT(= N_("E1379: Private variable not supported in an interface"));
|
||||||
EXTERN char e_private_method_str_in_interface[]
|
EXTERN char e_private_method_not_supported_in_interface[]
|
||||||
INIT(= N_("E1380: Private method not supported in an interface"));
|
INIT(= N_("E1380: Private method not supported in an interface"));
|
||||||
EXTERN char e_interface_cannot_use_implements[]
|
EXTERN char e_interface_cannot_use_implements[]
|
||||||
INIT(= N_("E1381: Interface cannot use \"implements\""));
|
INIT(= N_("E1381: Interface cannot use \"implements\""));
|
||||||
|
@@ -1180,14 +1180,6 @@ get_lval(
|
|||||||
return NULL;
|
return NULL;
|
||||||
lp->ll_tv = &v->di_tv;
|
lp->ll_tv = &v->di_tv;
|
||||||
}
|
}
|
||||||
if (vim9script && writing && lp->ll_tv->v_type == VAR_CLASS
|
|
||||||
&& (lp->ll_tv->vval.v_class->class_flags & CLASS_INTERFACE) != 0)
|
|
||||||
{
|
|
||||||
if (!quiet)
|
|
||||||
semsg(_(e_interface_static_direct_access_str),
|
|
||||||
lp->ll_tv->vval.v_class->class_name, lp->ll_name);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vim9script && (flags & GLV_NO_DECL) == 0)
|
if (vim9script && (flags & GLV_NO_DECL) == 0)
|
||||||
{
|
{
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
/* vim9class.c */
|
/* vim9class.c */
|
||||||
int object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl, int is_static);
|
int object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl, int is_static);
|
||||||
void ex_class(exarg_T *eap);
|
void ex_class(exarg_T *eap);
|
||||||
type_T *class_member_type(class_T *cl, int is_object, char_u *name, char_u *name_end, int *member_idx, ocmember_T **m);
|
type_T *class_member_type(class_T *cl, int is_object, char_u *name, char_u *name_end, int *member_idx);
|
||||||
void ex_enum(exarg_T *eap);
|
void ex_enum(exarg_T *eap);
|
||||||
void ex_type(exarg_T *eap);
|
void ex_type(exarg_T *eap);
|
||||||
int class_object_index(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose);
|
int class_object_index(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose);
|
||||||
|
@@ -4,12 +4,14 @@ source check.vim
|
|||||||
import './vim9.vim' as v9
|
import './vim9.vim' as v9
|
||||||
|
|
||||||
def Test_class_basic()
|
def Test_class_basic()
|
||||||
|
# Class supported only in "vim9script"
|
||||||
var lines =<< trim END
|
var lines =<< trim END
|
||||||
class NotWorking
|
class NotWorking
|
||||||
endclass
|
endclass
|
||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E1316:')
|
v9.CheckSourceFailure(lines, 'E1316:')
|
||||||
|
|
||||||
|
# First character in a class name should be capitalized.
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
class notWorking
|
class notWorking
|
||||||
@@ -17,6 +19,7 @@ def Test_class_basic()
|
|||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E1314:')
|
v9.CheckSourceFailure(lines, 'E1314:')
|
||||||
|
|
||||||
|
# Only alphanumeric characters are supported in a class name
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
class Not@working
|
class Not@working
|
||||||
@@ -24,6 +27,7 @@ def Test_class_basic()
|
|||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E1315:')
|
v9.CheckSourceFailure(lines, 'E1315:')
|
||||||
|
|
||||||
|
# Unsupported keyword (instead of class)
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
abstract noclass Something
|
abstract noclass Something
|
||||||
@@ -31,6 +35,7 @@ def Test_class_basic()
|
|||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E475:')
|
v9.CheckSourceFailure(lines, 'E475:')
|
||||||
|
|
||||||
|
# Only the completed word "class" should be recognized
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
abstract classy Something
|
abstract classy Something
|
||||||
@@ -38,6 +43,7 @@ def Test_class_basic()
|
|||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E475:')
|
v9.CheckSourceFailure(lines, 'E475:')
|
||||||
|
|
||||||
|
# The complete "endclass" should be specified.
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
class Something
|
class Something
|
||||||
@@ -45,6 +51,7 @@ def Test_class_basic()
|
|||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E1065:')
|
v9.CheckSourceFailure(lines, 'E1065:')
|
||||||
|
|
||||||
|
# Additional words after "endclass"
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
class Something
|
class Something
|
||||||
@@ -52,6 +59,7 @@ def Test_class_basic()
|
|||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E488:')
|
v9.CheckSourceFailure(lines, 'E488:')
|
||||||
|
|
||||||
|
# Additional commands after "endclass"
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
class Something
|
class Something
|
||||||
@@ -59,6 +67,7 @@ def Test_class_basic()
|
|||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E488:')
|
v9.CheckSourceFailure(lines, 'E488:')
|
||||||
|
|
||||||
|
# Use "this" without any member variable name
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
class Something
|
class Something
|
||||||
@@ -67,6 +76,7 @@ def Test_class_basic()
|
|||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E1317:')
|
v9.CheckSourceFailure(lines, 'E1317:')
|
||||||
|
|
||||||
|
# Use "this." without any member variable name
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
class Something
|
class Something
|
||||||
@@ -75,6 +85,7 @@ def Test_class_basic()
|
|||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E1317:')
|
v9.CheckSourceFailure(lines, 'E1317:')
|
||||||
|
|
||||||
|
# Space between "this" and ".<variable>"
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
class Something
|
class Something
|
||||||
@@ -83,6 +94,7 @@ def Test_class_basic()
|
|||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E1317:')
|
v9.CheckSourceFailure(lines, 'E1317:')
|
||||||
|
|
||||||
|
# Space between "this." and the member variable name
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
class Something
|
class Something
|
||||||
@@ -91,6 +103,7 @@ def Test_class_basic()
|
|||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E1317:')
|
v9.CheckSourceFailure(lines, 'E1317:')
|
||||||
|
|
||||||
|
# Use "that" instead of "this"
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
class Something
|
class Something
|
||||||
@@ -100,6 +113,7 @@ def Test_class_basic()
|
|||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: that.count')
|
v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: that.count')
|
||||||
|
|
||||||
|
# Member variable without a type or initialization
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
class Something
|
class Something
|
||||||
@@ -108,6 +122,7 @@ def Test_class_basic()
|
|||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E1022:')
|
v9.CheckSourceFailure(lines, 'E1022:')
|
||||||
|
|
||||||
|
# Use a non-existing member variable in new()
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
class Something
|
class Something
|
||||||
@@ -117,8 +132,9 @@ def Test_class_basic()
|
|||||||
endclass
|
endclass
|
||||||
var obj = Something.new()
|
var obj = Something.new()
|
||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E1089:')
|
v9.CheckSourceFailure(lines, 'E1326: Member not found on object "Something": state')
|
||||||
|
|
||||||
|
# Space before ":" in a member variable declaration
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
class Something
|
class Something
|
||||||
@@ -127,6 +143,7 @@ def Test_class_basic()
|
|||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E1059:')
|
v9.CheckSourceFailure(lines, 'E1059:')
|
||||||
|
|
||||||
|
# No space after ":" in a member variable declaration
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
class Something
|
class Something
|
||||||
@@ -204,6 +221,8 @@ def Test_class_basic()
|
|||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E1324: Using an object as a String')
|
v9.CheckSourceFailure(lines, 'E1324: Using an object as a String')
|
||||||
|
|
||||||
|
# Test creating a class with member variables and methods, calling a object
|
||||||
|
# method. Check for using type() and typename() with a class and an object.
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
|
|
||||||
@@ -270,6 +289,7 @@ def Test_class_basic()
|
|||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E15:')
|
v9.CheckSourceFailure(lines, 'E15:')
|
||||||
|
|
||||||
|
# Use a multi-line initialization for a member variable
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
class A
|
class A
|
||||||
@@ -347,6 +367,7 @@ def Test_class_interface_wrong_end()
|
|||||||
enddef
|
enddef
|
||||||
|
|
||||||
def Test_object_not_set()
|
def Test_object_not_set()
|
||||||
|
# Use an uninitialized object in script context
|
||||||
var lines =<< trim END
|
var lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
|
|
||||||
@@ -360,6 +381,7 @@ def Test_object_not_set()
|
|||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E1360:')
|
v9.CheckSourceFailure(lines, 'E1360:')
|
||||||
|
|
||||||
|
# Use an uninitialized object from a def function
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
|
|
||||||
@@ -378,6 +400,8 @@ def Test_object_not_set()
|
|||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E1360:')
|
v9.CheckSourceFailure(lines, 'E1360:')
|
||||||
|
|
||||||
|
# Pass an uninitialized object variable to a "new" function and try to call an
|
||||||
|
# object method.
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
|
|
||||||
@@ -418,6 +442,7 @@ def Test_object_not_set()
|
|||||||
v9.CheckSourceFailure(lines, 'E1363:')
|
v9.CheckSourceFailure(lines, 'E1363:')
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
" Null object assignment and comparison
|
||||||
def Test_null_object_assign_compare()
|
def Test_null_object_assign_compare()
|
||||||
var lines =<< trim END
|
var lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
@@ -458,6 +483,7 @@ def Test_null_object_assign_compare()
|
|||||||
v9.CheckSourceSuccess(lines)
|
v9.CheckSourceSuccess(lines)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
" Test for object member initialization and disassembly
|
||||||
def Test_class_member_initializer()
|
def Test_class_member_initializer()
|
||||||
var lines =<< trim END
|
var lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
@@ -517,32 +543,6 @@ def Test_member_any_used_as_object()
|
|||||||
END
|
END
|
||||||
v9.CheckSourceSuccess(lines)
|
v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
lines =<< trim END
|
|
||||||
vim9script
|
|
||||||
|
|
||||||
class Inner
|
|
||||||
this.value: number = 0
|
|
||||||
endclass
|
|
||||||
|
|
||||||
class Outer
|
|
||||||
this.inner: Inner
|
|
||||||
endclass
|
|
||||||
|
|
||||||
def F(outer: Outer)
|
|
||||||
outer.inner.value = 1
|
|
||||||
enddef
|
|
||||||
|
|
||||||
def Test_assign_to_nested_typed_member()
|
|
||||||
var inner = Inner.new(0)
|
|
||||||
var outer = Outer.new(inner)
|
|
||||||
F(outer)
|
|
||||||
assert_equal(1, inner.value)
|
|
||||||
enddef
|
|
||||||
|
|
||||||
Test_assign_to_nested_typed_member()
|
|
||||||
END
|
|
||||||
v9.CheckSourceSuccess(lines)
|
|
||||||
|
|
||||||
# Try modifying a private variable using an "any" object
|
# Try modifying a private variable using an "any" object
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
@@ -588,7 +588,37 @@ def Test_member_any_used_as_object()
|
|||||||
v9.CheckSourceFailure(lines, 'E1326: Member not found on object "Inner": someval')
|
v9.CheckSourceFailure(lines, 'E1326: Member not found on object "Inner": someval')
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
" Nested assignment to a object variable which is of another class type
|
||||||
|
def Test_assignment_nested_type()
|
||||||
|
var lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
class Inner
|
||||||
|
public this.value: number = 0
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class Outer
|
||||||
|
this.inner: Inner
|
||||||
|
endclass
|
||||||
|
|
||||||
|
def F(outer: Outer)
|
||||||
|
outer.inner.value = 1
|
||||||
|
enddef
|
||||||
|
|
||||||
|
def Test_assign_to_nested_typed_member()
|
||||||
|
var inner = Inner.new(0)
|
||||||
|
var outer = Outer.new(inner)
|
||||||
|
F(outer)
|
||||||
|
assert_equal(1, inner.value)
|
||||||
|
enddef
|
||||||
|
|
||||||
|
Test_assign_to_nested_typed_member()
|
||||||
|
END
|
||||||
|
v9.CheckSourceSuccess(lines)
|
||||||
|
enddef
|
||||||
|
|
||||||
def Test_assignment_with_operator()
|
def Test_assignment_with_operator()
|
||||||
|
# Use "+=" to assign to a object variable
|
||||||
var lines =<< trim END
|
var lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
|
|
||||||
@@ -762,7 +792,6 @@ def Test_class_default_new()
|
|||||||
v9.CheckSourceFailure(lines, "E1328: Constructor default value must be v:none: = 'a'")
|
v9.CheckSourceFailure(lines, "E1328: Constructor default value must be v:none: = 'a'")
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
|
||||||
def Test_class_new_with_object_member()
|
def Test_class_new_with_object_member()
|
||||||
var lines =<< trim END
|
var lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
@@ -1677,7 +1706,7 @@ func Test_interface_garbagecollect()
|
|||||||
call v9.CheckSourceSuccess(lines)
|
call v9.CheckSourceSuccess(lines)
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
def Test_class_function()
|
def Test_class_method()
|
||||||
var lines =<< trim END
|
var lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
class Value
|
class Value
|
||||||
@@ -1713,6 +1742,29 @@ def Test_class_function()
|
|||||||
endclass
|
endclass
|
||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E1318:')
|
v9.CheckSourceFailure(lines, 'E1318:')
|
||||||
|
|
||||||
|
# Test for calling a class method from another class method without the class
|
||||||
|
# name prefix.
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
static myList: list<number> = [1]
|
||||||
|
static def Foo(n: number)
|
||||||
|
myList->add(n)
|
||||||
|
enddef
|
||||||
|
static def Bar()
|
||||||
|
Foo(2)
|
||||||
|
enddef
|
||||||
|
def Baz()
|
||||||
|
Foo(3)
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
A.Bar()
|
||||||
|
var a = A.new()
|
||||||
|
a.Baz()
|
||||||
|
assert_equal([1, 2, 3], A.myList)
|
||||||
|
END
|
||||||
|
v9.CheckSourceSuccess(lines)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
def Test_class_defcompile()
|
def Test_class_defcompile()
|
||||||
@@ -2343,7 +2395,6 @@ def Test_call_interface_method()
|
|||||||
END
|
END
|
||||||
v9.CheckSourceSuccess(lines)
|
v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
|
||||||
# No class that implements the interface.
|
# No class that implements the interface.
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
@@ -2685,7 +2736,6 @@ def Test_using_base_class()
|
|||||||
v9.CheckSourceSuccess(lines)
|
v9.CheckSourceSuccess(lines)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
|
||||||
def Test_class_import()
|
def Test_class_import()
|
||||||
var lines =<< trim END
|
var lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
@@ -3651,7 +3701,7 @@ def Test_private_class_method()
|
|||||||
return 1234
|
return 1234
|
||||||
enddef
|
enddef
|
||||||
def Bar()
|
def Bar()
|
||||||
assert_equal(1234, A._Foo())
|
assert_equal(1234, _Foo())
|
||||||
enddef
|
enddef
|
||||||
endclass
|
endclass
|
||||||
var a = A.new()
|
var a = A.new()
|
||||||
@@ -3659,7 +3709,8 @@ def Test_private_class_method()
|
|||||||
END
|
END
|
||||||
v9.CheckSourceSuccess(lines)
|
v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
# Use a class private method from another class private method
|
# Use a class private method from another class private method without the
|
||||||
|
# class name prefix.
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
|
|
||||||
@@ -3668,10 +3719,10 @@ def Test_private_class_method()
|
|||||||
return 1234
|
return 1234
|
||||||
enddef
|
enddef
|
||||||
static def _Foo2()
|
static def _Foo2()
|
||||||
assert_equal(1234, A._Foo1())
|
assert_equal(1234, _Foo1())
|
||||||
enddef
|
enddef
|
||||||
def Bar()
|
def Bar()
|
||||||
A._Foo2()
|
_Foo2()
|
||||||
enddef
|
enddef
|
||||||
endclass
|
endclass
|
||||||
var a = A.new()
|
var a = A.new()
|
||||||
@@ -4063,7 +4114,7 @@ def Test_private_member_access_outside_class()
|
|||||||
enddef
|
enddef
|
||||||
T()
|
T()
|
||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E1089: Unknown variable: _a')
|
v9.CheckSourceFailure(lines, 'E1326: Member not found on object "A": _a')
|
||||||
|
|
||||||
# private static member variable
|
# private static member variable
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
@@ -4091,7 +4142,7 @@ def Test_private_member_access_outside_class()
|
|||||||
enddef
|
enddef
|
||||||
T()
|
T()
|
||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E1374: Class member "_val" accessible only inside class "A"')
|
v9.CheckSourceFailure(lines, 'E1375: Class member "_val" accessible only using class "A"')
|
||||||
|
|
||||||
# private static class variable
|
# private static class variable
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
@@ -4265,7 +4316,7 @@ def Test_class_variable_access_using_object()
|
|||||||
enddef
|
enddef
|
||||||
T()
|
T()
|
||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E1374: Class member "svar2" accessible only inside class "A"')
|
v9.CheckSourceFailure(lines, 'E1375: Class member "svar2" accessible only using class "A"')
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
" Test for using a interface method using a child object
|
" Test for using a interface method using a child object
|
||||||
@@ -4711,7 +4762,7 @@ def Test_class_variable()
|
|||||||
enddef
|
enddef
|
||||||
T()
|
T()
|
||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E1374: Class member "val" accessible only inside class "A"')
|
v9.CheckSourceFailure(lines, 'E1375: Class member "val" accessible only using class "A"')
|
||||||
|
|
||||||
# Reading a class variable using an object at function level
|
# Reading a class variable using an object at function level
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
@@ -4977,7 +5028,7 @@ def Test_interface_with_unsupported_members()
|
|||||||
static num: number
|
static num: number
|
||||||
endinterface
|
endinterface
|
||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface')
|
v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface')
|
||||||
|
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
@@ -4985,7 +5036,7 @@ def Test_interface_with_unsupported_members()
|
|||||||
static _num: number
|
static _num: number
|
||||||
endinterface
|
endinterface
|
||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface')
|
v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface')
|
||||||
|
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
@@ -4993,7 +5044,7 @@ def Test_interface_with_unsupported_members()
|
|||||||
public static num: number
|
public static num: number
|
||||||
endinterface
|
endinterface
|
||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface')
|
v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface')
|
||||||
|
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
@@ -5001,7 +5052,7 @@ def Test_interface_with_unsupported_members()
|
|||||||
public static _num: number
|
public static _num: number
|
||||||
endinterface
|
endinterface
|
||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface')
|
v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface')
|
||||||
|
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
@@ -5009,7 +5060,7 @@ def Test_interface_with_unsupported_members()
|
|||||||
static def Foo(d: dict<any>): list<string>
|
static def Foo(d: dict<any>): list<string>
|
||||||
endinterface
|
endinterface
|
||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface')
|
v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface')
|
||||||
|
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
@@ -5017,7 +5068,7 @@ def Test_interface_with_unsupported_members()
|
|||||||
static def _Foo(d: dict<any>): list<string>
|
static def _Foo(d: dict<any>): list<string>
|
||||||
endinterface
|
endinterface
|
||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface')
|
v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface')
|
||||||
|
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
@@ -5402,4 +5453,35 @@ def Test_implements_using_var_type_any()
|
|||||||
v9.CheckSourceFailure(lines, 'E1382: Member "val": type mismatch, expected list<dict<string>> but got dict<number>')
|
v9.CheckSourceFailure(lines, 'E1382: Member "val": type mismatch, expected list<dict<string>> but got dict<number>')
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
" Test for assigning to a member variable in a nested class
|
||||||
|
def Test_nested_object_assignment()
|
||||||
|
var lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
class A
|
||||||
|
this.value: number
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class B
|
||||||
|
this.a: A = A.new()
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class C
|
||||||
|
this.b: B = B.new()
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class D
|
||||||
|
this.c: C = C.new()
|
||||||
|
endclass
|
||||||
|
|
||||||
|
def T(da: D)
|
||||||
|
da.c.b.a.value = 10
|
||||||
|
enddef
|
||||||
|
|
||||||
|
var d = D.new()
|
||||||
|
T(d)
|
||||||
|
END
|
||||||
|
v9.CheckSourceFailure(lines, 'E46: Cannot change read-only variable "value"')
|
||||||
|
enddef
|
||||||
|
|
||||||
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
|
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
|
||||||
|
@@ -699,6 +699,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
1909,
|
||||||
/**/
|
/**/
|
||||||
1908,
|
1908,
|
||||||
/**/
|
/**/
|
||||||
|
@@ -1592,7 +1592,7 @@ early_ret:
|
|||||||
|
|
||||||
if (!is_class)
|
if (!is_class)
|
||||||
{
|
{
|
||||||
emsg(_(e_static_cannot_be_used_in_interface));
|
emsg(_(e_static_member_not_supported_in_interface));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
has_static = TRUE;
|
has_static = TRUE;
|
||||||
@@ -1623,7 +1623,8 @@ early_ret:
|
|||||||
if (!is_class && *varname == '_')
|
if (!is_class && *varname == '_')
|
||||||
{
|
{
|
||||||
// private variables are not supported in an interface
|
// private variables are not supported in an interface
|
||||||
semsg(_(e_private_variable_str_in_interface), varname);
|
semsg(_(e_private_variable_not_supported_in_interface),
|
||||||
|
varname);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1689,7 +1690,8 @@ early_ret:
|
|||||||
if (!is_class && *name == '_')
|
if (!is_class && *name == '_')
|
||||||
{
|
{
|
||||||
// private variables are not supported in an interface
|
// private variables are not supported in an interface
|
||||||
semsg(_(e_private_method_str_in_interface), name);
|
semsg(_(e_private_method_not_supported_in_interface),
|
||||||
|
name);
|
||||||
func_clear_free(uf, FALSE);
|
func_clear_free(uf, FALSE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2010,8 +2012,7 @@ class_member_type(
|
|||||||
int is_object,
|
int is_object,
|
||||||
char_u *name,
|
char_u *name,
|
||||||
char_u *name_end,
|
char_u *name_end,
|
||||||
int *member_idx,
|
int *member_idx)
|
||||||
ocmember_T **p_m)
|
|
||||||
{
|
{
|
||||||
size_t len = name_end - name;
|
size_t len = name_end - name;
|
||||||
ocmember_T *m;
|
ocmember_T *m;
|
||||||
@@ -2022,27 +2023,11 @@ class_member_type(
|
|||||||
member_idx);
|
member_idx);
|
||||||
if (m == NULL)
|
if (m == NULL)
|
||||||
{
|
{
|
||||||
char_u *varname = vim_strnsave(name, len);
|
member_not_found_msg(cl, is_object ? VAR_OBJECT : VAR_CLASS, name,
|
||||||
if (varname != NULL)
|
len);
|
||||||
{
|
|
||||||
if (is_object && class_member_idx(cl, name, len) >= 0)
|
|
||||||
// A class variable with this name is present
|
|
||||||
semsg(_(e_class_member_str_accessible_only_inside_class_str),
|
|
||||||
varname, cl->class_name);
|
|
||||||
else if (!is_object && object_member_idx(cl, name, len) >= 0)
|
|
||||||
// An instance variable with this name is present
|
|
||||||
semsg(_(e_object_member_str_accessible_only_using_object_str),
|
|
||||||
varname, cl->class_name);
|
|
||||||
else
|
|
||||||
semsg(_(e_unknown_variable_str), varname);
|
|
||||||
}
|
|
||||||
vim_free(varname);
|
|
||||||
return &t_any;
|
return &t_any;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p_m != NULL)
|
|
||||||
*p_m = m;
|
|
||||||
|
|
||||||
return m->ocm_type;
|
return m->ocm_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2250,12 +2235,6 @@ class_object_index(
|
|||||||
semsg(_(e_cannot_access_private_member_str), m->ocm_name);
|
semsg(_(e_cannot_access_private_member_str), m->ocm_name);
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
if ((cl->class_flags & CLASS_INTERFACE) != 0)
|
|
||||||
{
|
|
||||||
semsg(_(e_interface_static_direct_access_str),
|
|
||||||
cl->class_name, m->ocm_name);
|
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
typval_T *tv = &cl->class_members_tv[m_idx];
|
typval_T *tv = &cl->class_members_tv[m_idx];
|
||||||
copy_tv(tv, rettv);
|
copy_tv(tv, rettv);
|
||||||
|
@@ -1578,6 +1578,47 @@ is_decl_command(cmdidx_T cmdidx)
|
|||||||
|| cmdidx == CMD_final || cmdidx == CMD_const;
|
|| cmdidx == CMD_final || cmdidx == CMD_const;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns TRUE if the class or object variable in "lhs" is modifiable.
|
||||||
|
* "var_start" points to the start of the variable name and "lhs->lhs_varlen"
|
||||||
|
* has the total length. Note that the "lhs" can be nested an object reference
|
||||||
|
* (e.g. a.b.c.d.var).
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
lhs_class_member_modifiable(lhs_T *lhs, char_u *var_start, cctx_T *cctx)
|
||||||
|
{
|
||||||
|
size_t varlen = lhs->lhs_varlen;
|
||||||
|
class_T *cl = lhs->lhs_type->tt_class;
|
||||||
|
int is_object = lhs->lhs_type->tt_type == VAR_OBJECT;
|
||||||
|
char_u *name = var_start + varlen + 1;
|
||||||
|
size_t namelen = lhs->lhs_end - var_start - varlen - 1;
|
||||||
|
ocmember_T *m;
|
||||||
|
|
||||||
|
m = member_lookup(cl, lhs->lhs_type->tt_type, name, namelen, NULL);
|
||||||
|
if (m == NULL)
|
||||||
|
{
|
||||||
|
member_not_found_msg(cl, lhs->lhs_type->tt_type, name, namelen);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it is private member variable, then accessing it outside the
|
||||||
|
// class is not allowed.
|
||||||
|
// If it is a read only class variable, then it can be modified
|
||||||
|
// only inside the class where it is defined.
|
||||||
|
if ((m->ocm_access != VIM_ACCESS_ALL) &&
|
||||||
|
((is_object && !inside_class(cctx, cl))
|
||||||
|
|| (!is_object && cctx->ctx_ufunc->uf_class != cl)))
|
||||||
|
{
|
||||||
|
char *msg = (m->ocm_access == VIM_ACCESS_PRIVATE)
|
||||||
|
? e_cannot_access_private_member_str
|
||||||
|
: e_cannot_change_readonly_variable_str;
|
||||||
|
semsg(_(msg), m->ocm_name);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Figure out the LHS type and other properties for an assignment or one item
|
* Figure out the LHS type and other properties for an assignment or one item
|
||||||
* of ":unlet" with an index.
|
* of ":unlet" with an index.
|
||||||
@@ -1691,9 +1732,9 @@ compile_lhs(
|
|||||||
{
|
{
|
||||||
if (is_decl)
|
if (is_decl)
|
||||||
{
|
{
|
||||||
// if we come here with what looks like an assignment like .=
|
// if we come here with what looks like an assignment like
|
||||||
// but which has been reject by assignment_len() from may_compile_assignment
|
// .= but which has been reject by assignment_len() from
|
||||||
// give a better error message
|
// may_compile_assignment give a better error message
|
||||||
char_u *p = skipwhite(lhs->lhs_end);
|
char_u *p = skipwhite(lhs->lhs_end);
|
||||||
if (p[0] == '.' && p[1] == '=')
|
if (p[0] == '.' && p[1] == '=')
|
||||||
emsg(_(e_dot_equal_not_supported_with_script_version_two));
|
emsg(_(e_dot_equal_not_supported_with_script_version_two));
|
||||||
@@ -1959,36 +2000,17 @@ compile_lhs(
|
|||||||
{
|
{
|
||||||
// for an object or class member get the type of the member
|
// for an object or class member get the type of the member
|
||||||
class_T *cl = lhs->lhs_type->tt_class;
|
class_T *cl = lhs->lhs_type->tt_class;
|
||||||
ocmember_T *m;
|
|
||||||
int is_object = lhs->lhs_type->tt_type == VAR_OBJECT;
|
int is_object = lhs->lhs_type->tt_type == VAR_OBJECT;
|
||||||
|
|
||||||
|
if (!lhs_class_member_modifiable(lhs, var_start, cctx))
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
lhs->lhs_member_type = class_member_type(cl,
|
lhs->lhs_member_type = class_member_type(cl,
|
||||||
is_object,
|
is_object,
|
||||||
after + 1, lhs->lhs_end,
|
after + 1, lhs->lhs_end,
|
||||||
&lhs->lhs_member_idx, &m);
|
&lhs->lhs_member_idx);
|
||||||
if (lhs->lhs_member_idx < 0)
|
if (lhs->lhs_member_idx < 0)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
if ((cl->class_flags & CLASS_INTERFACE) != 0
|
|
||||||
&& lhs->lhs_type->tt_type == VAR_CLASS)
|
|
||||||
{
|
|
||||||
semsg(_(e_interface_static_direct_access_str),
|
|
||||||
cl->class_name, m->ocm_name);
|
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
// If it is private member variable, then accessing it outside the
|
|
||||||
// class is not allowed.
|
|
||||||
// If it is a read only class variable, then it can be modified
|
|
||||||
// only inside the class where it is defined.
|
|
||||||
if ((m->ocm_access != VIM_ACCESS_ALL) &&
|
|
||||||
((is_object && !inside_class(cctx, cl))
|
|
||||||
|| (!is_object && cctx->ctx_ufunc->uf_class != cl)))
|
|
||||||
{
|
|
||||||
char *msg = (m->ocm_access == VIM_ACCESS_PRIVATE)
|
|
||||||
? e_cannot_access_private_member_str
|
|
||||||
: e_cannot_change_readonly_variable_str;
|
|
||||||
semsg(_(msg), m->ocm_name);
|
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -2163,6 +2185,14 @@ compile_load_lhs(
|
|||||||
|
|
||||||
lhs->lhs_type = cctx->ctx_type_stack.ga_len == 0 ? &t_void
|
lhs->lhs_type = cctx->ctx_type_stack.ga_len == 0 ? &t_void
|
||||||
: get_type_on_stack(cctx, 0);
|
: get_type_on_stack(cctx, 0);
|
||||||
|
|
||||||
|
if (lhs->lhs_type->tt_type == VAR_OBJECT)
|
||||||
|
{
|
||||||
|
// Check whether the object variable is modifiable
|
||||||
|
if (!lhs_class_member_modifiable(lhs, var_start, cctx))
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
// Now we can properly check the type. The variable is indexed, thus
|
// Now we can properly check the type. The variable is indexed, thus
|
||||||
// we need the member type. For a class or object we don't know the
|
// we need the member type. For a class or object we don't know the
|
||||||
// type yet, it depends on what member is used.
|
// type yet, it depends on what member is used.
|
||||||
@@ -2198,8 +2228,7 @@ compile_load_lhs_with_index(lhs_T *lhs, char_u *var_start, cctx_T *cctx)
|
|||||||
|
|
||||||
class_T *cl = lhs->lhs_type->tt_class;
|
class_T *cl = lhs->lhs_type->tt_class;
|
||||||
type_T *type = class_member_type(cl, TRUE, dot + 1,
|
type_T *type = class_member_type(cl, TRUE, dot + 1,
|
||||||
lhs->lhs_end, &lhs->lhs_member_idx,
|
lhs->lhs_end, &lhs->lhs_member_idx);
|
||||||
NULL);
|
|
||||||
if (lhs->lhs_member_idx < 0)
|
if (lhs->lhs_member_idx < 0)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
|
@@ -439,12 +439,6 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
|
|||||||
if (m != NULL)
|
if (m != NULL)
|
||||||
{
|
{
|
||||||
// Note: type->tt_type = VAR_CLASS
|
// Note: type->tt_type = VAR_CLASS
|
||||||
if ((cl->class_flags & CLASS_INTERFACE) != 0)
|
|
||||||
{
|
|
||||||
semsg(_(e_interface_static_direct_access_str),
|
|
||||||
cl->class_name, m->ocm_name);
|
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
// A private class variable can be accessed only in the class where
|
// A private class variable can be accessed only in the class where
|
||||||
// it is defined.
|
// it is defined.
|
||||||
if (*name == '_' && cctx->ctx_ufunc->uf_class != cl)
|
if (*name == '_' && cctx->ctx_ufunc->uf_class != cl)
|
||||||
@@ -1152,9 +1146,6 @@ compile_call(
|
|||||||
// the class where the function is defined.
|
// the class where the function is defined.
|
||||||
if (cctx->ctx_ufunc->uf_defclass == cl)
|
if (cctx->ctx_ufunc->uf_defclass == cl)
|
||||||
{
|
{
|
||||||
// The generate_CALL() function expects the class type at the
|
|
||||||
// top of the stack. So push the class type to the stack.
|
|
||||||
push_type_stack(cctx, &t_class);
|
|
||||||
res = generate_CALL(cctx, cl->class_class_functions[mi], NULL,
|
res = generate_CALL(cctx, cl->class_class_functions[mi], NULL,
|
||||||
0, type, argcount);
|
0, type, argcount);
|
||||||
}
|
}
|
||||||
|
@@ -1899,9 +1899,15 @@ generate_CALL(
|
|||||||
// drop the argument types
|
// drop the argument types
|
||||||
cctx->ctx_type_stack.ga_len -= argcount;
|
cctx->ctx_type_stack.ga_len -= argcount;
|
||||||
|
|
||||||
// For an object or class method call, drop the object/class type
|
// For an object or class method call, drop the object/class type.
|
||||||
if (ufunc->uf_class != NULL)
|
if (ufunc->uf_class != NULL)
|
||||||
cctx->ctx_type_stack.ga_len--;
|
{
|
||||||
|
// When a class method is called without the class name prefix, then
|
||||||
|
// the type will not be in the stack.
|
||||||
|
type_T *stype = get_type_on_stack(cctx, 0);
|
||||||
|
if (stype->tt_type == VAR_CLASS || stype->tt_type == VAR_OBJECT)
|
||||||
|
cctx->ctx_type_stack.ga_len--;
|
||||||
|
}
|
||||||
|
|
||||||
// add return type
|
// add return type
|
||||||
return push_type_stack(cctx, ufunc->uf_ret_type);
|
return push_type_stack(cctx, ufunc->uf_ret_type);
|
||||||
|
Reference in New Issue
Block a user