1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2012 Jean Privat <jean@pryen.org>
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 # Analysis and verification of property definitions to instantiate model element
18 module modelize_property
21 private import annotation
23 redef class ToolContext
24 var modelize_property_phase
: Phase = new ModelizePropertyPhase(self, [modelize_class_phase
])
27 private class ModelizePropertyPhase
29 redef fun process_nmodule
(nmodule
)
31 for nclassdef
in nmodule
.n_classdefs
do
32 if nclassdef
.all_defs
== null then continue # skip non principal classdef
33 toolcontext
.modelbuilder
.build_properties
(nclassdef
)
38 redef class ModelBuilder
39 # Register the npropdef associated to each mpropdef
40 # FIXME: why not refine the `MPropDef` class with a nullable attribute?
41 var mpropdef2npropdef
: HashMap[MPropDef, APropdef] = new HashMap[MPropDef, APropdef]
43 # Build the properties of `nclassdef`.
44 # REQUIRE: all superclasses are built.
45 private fun build_properties
(nclassdef
: AClassdef)
47 # Force building recursively
48 if nclassdef
.build_properties_is_done
then return
49 nclassdef
.build_properties_is_done
= true
50 var mclassdef
= nclassdef
.mclassdef
.as(not null)
51 if mclassdef
.in_hierarchy
== null then return # Skip error
52 for superclassdef
in mclassdef
.in_hierarchy
.direct_greaters
do
53 if not mclassdef2nclassdef
.has_key
(superclassdef
) then continue
54 build_properties
(mclassdef2nclassdef
[superclassdef
])
57 for nclassdef2
in nclassdef
.all_defs
do
58 for npropdef
in nclassdef2
.n_propdefs
do
59 npropdef
.build_property
(self, mclassdef
)
61 for npropdef
in nclassdef2
.n_propdefs
do
62 npropdef
.build_signature
(self)
64 for npropdef
in nclassdef2
.n_propdefs
do
65 npropdef
.check_signature
(self)
68 process_default_constructors
(nclassdef
)
71 # the root init of the Object class
72 # Is usually implicitly defined
73 # Then explicit or implicit definitions of root-init are attached to it
74 var the_root_init_mmethod
: nullable MMethod
76 # Introduce or inherit default constructor
77 # This is the last part of `build_properties`.
78 private fun process_default_constructors
(nclassdef
: AClassdef)
80 var mclassdef
= nclassdef
.mclassdef
.as(not null)
83 if not mclassdef
.is_intro
then return
85 var mmodule
= nclassdef
.mclassdef
.mmodule
87 # Look for the init in Object, or create it
88 if mclassdef
.mclass
.name
== "Object" and the_root_init_mmethod
== null then
89 # Create the implicit root-init method
90 var mprop
= new MMethod(mclassdef
, "init", mclassdef
.mclass
.visibility
)
91 mprop
.is_root_init
= true
92 var mpropdef
= new MMethodDef(mclassdef
, mprop
, nclassdef
.location
)
93 var mparameters
= new Array[MParameter]
94 var msignature
= new MSignature(mparameters
, null)
95 mpropdef
.msignature
= msignature
96 mpropdef
.new_msignature
= msignature
98 nclassdef
.mfree_init
= mpropdef
99 self.toolcontext
.info
("{mclassdef} gets a free empty constructor {mpropdef}{msignature}", 3)
100 the_root_init_mmethod
= mprop
104 # Is the class forbid constructors?
105 if not mclassdef
.mclass
.kind
.need_init
then return
107 # Is there already a constructor defined?
108 var defined_init
: nullable MMethodDef = null
109 for mpropdef
in mclassdef
.mpropdefs
do
110 if not mpropdef
isa MMethodDef then continue
111 if not mpropdef
.mproperty
.is_init
then continue
112 if mpropdef
.mproperty
.is_root_init
then
113 assert defined_init
== null
114 defined_init
= mpropdef
116 # An explicit old-style init, so return
121 if not nclassdef
isa AStdClassdef then return
123 # Do we inherit a old-style constructor?
124 var combine
= new Array[MMethod] # old-style constructors without arguments
125 var inhc
: nullable MClass = null # single super-class with a constructor with arguments
126 if defined_init
== null then for st
in mclassdef
.supertypes
do
128 if not c
.kind
.need_init
then continue
129 st
= st
.anchor_to
(mmodule
, mclassdef
.bound_mtype
)
130 var candidate
= self.try_get_mproperty_by_name2
(nclassdef
, mmodule
, st
, "init").as(nullable MMethod)
131 if candidate
!= null then
132 if candidate
.is_root_init
then continue
133 if candidate
.intro
.msignature
!= null then
134 if candidate
.intro
.msignature
.arity
== 0 then
135 combine
.add
(candidate
)
140 var inhc2
= c
.inherit_init_from
141 if inhc2
== null then inhc2
= c
142 if inhc2
== inhc
then continue
144 self.error
(nclassdef
, "Error: Cannot provide a defaut constructor: conflict for {inhc} and {c}")
150 # Collect undefined attributes
151 var mparameters
= new Array[MParameter]
152 var initializers
= new Array[MProperty]
153 var anode
: nullable ANode = null
154 for npropdef
in nclassdef
.n_propdefs
do
155 if npropdef
isa AAttrPropdef then
156 if npropdef
.mpropdef
== null then return # Skip broken attribute
157 var at
= npropdef
.get_single_annotation
("noinit", self)
159 npropdef
.noinit
= true
160 if npropdef
.n_expr
!= null then
161 self.error
(at
, "Error: `noinit` attributes cannot have an initial value")
163 continue # Skip noinit attributes
165 if npropdef
.n_expr
!= null then continue
166 var paramname
= npropdef
.mpropdef
.mproperty
.name
.substring_from
(1)
167 var ret_type
= npropdef
.mpropdef
.static_mtype
168 if ret_type
== null then return
169 var mparameter
= new MParameter(paramname
, ret_type
, false)
170 mparameters
.add
(mparameter
)
171 var msetter
= npropdef
.mwritepropdef
172 if msetter
== null then
173 # No setter, it is a old-style attribute, so just add it
174 initializers
.add
(npropdef
.mpropdef
.mproperty
)
176 # Add the setter to the list
177 initializers
.add
(msetter
.mproperty
)
179 if anode
== null then anode
= npropdef
182 if anode
== null then anode
= nclassdef
184 if combine
.is_empty
and inhc
!= null then
185 if not mparameters
.is_empty
then
186 self.error
(anode
,"Error: {mclassdef} cannot inherit constructors from {inhc} because there is attributes without initial values: {mparameters.join(", ")}")
190 # TODO: actively inherit the consturctor
191 self.toolcontext
.info
("{mclassdef} inherits all constructors from {inhc}", 3)
192 #mclassdef.mclass.inherit_init_from = inhc
196 if not combine
.is_empty
and inhc
!= null then
197 self.error
(nclassdef
, "Error: Cannot provide a defaut constructor: conflict for {combine.join(", ")} and {inhc}")
200 if not combine
.is_empty
then
201 if mparameters
.is_empty
and combine
.length
== 1 then
202 # No need to create a local init, the inherited one is enough
203 inhc
= combine
.first
.intro_mclassdef
.mclass
204 mclassdef
.mclass
.inherit_init_from
= inhc
205 self.toolcontext
.info
("{mclassdef} inherits all constructors from {inhc}", 3)
208 nclassdef
.super_inits
= combine
209 var mprop
= new MMethod(mclassdef
, "init", mclassdef
.mclass
.visibility
)
210 var mpropdef
= new MMethodDef(mclassdef
, mprop
, nclassdef
.location
)
211 var msignature
= new MSignature(mparameters
, null)
212 mpropdef
.msignature
= msignature
214 nclassdef
.mfree_init
= mpropdef
215 self.toolcontext
.info
("{mclassdef} gets a free empty constructor {mpropdef}{msignature}", 3)
219 if the_root_init_mmethod
== null then return
221 # Look for nost-specific new-stype init definitions
222 var spropdefs
= the_root_init_mmethod
.lookup_super_definitions
(mclassdef
.mmodule
, mclassdef
.bound_mtype
)
223 if spropdefs
.is_empty
then
224 toolcontext
.fatal_error
(nclassdef
.location
, "Fatal error: {mclassdef} does not specialize {the_root_init_mmethod.intro_mclassdef}. Possible duplication of the root class `Object`?")
227 # Search the longest-one and checks for conflict
228 var longest
= spropdefs
.first
229 if spropdefs
.length
> 1 then
230 # Check for conflict in the order of initializers
231 # Each initializer list must me a prefix of the longest list
232 # part 1. find the longest list
233 for spd
in spropdefs
do
234 if spd
.initializers
.length
> longest
.initializers
.length
then longest
= spd
237 for spd
in spropdefs
do
239 for p
in spd
.initializers
do
240 if p
!= longest
.initializers
[i
] then
241 self.error
(nclassdef
, "Error: conflict for inherited inits {spd}({spd.initializers.join(", ")}) and {longest}({longest.initializers.join(", ")})")
249 # Can we just inherit?
250 if spropdefs
.length
== 1 and mparameters
.is_empty
and defined_init
== null then
251 self.toolcontext
.info
("{mclassdef} inherits the basic constructor {longest}", 3)
255 # Combine the inherited list to what is collected
256 if longest
.initializers
.length
> 0 then
257 mparameters
.prepend longest
.new_msignature
.mparameters
258 initializers
.prepend longest
.initializers
261 # If we already have a basic init definition, then setup its initializers
262 if defined_init
!= null then
263 defined_init
.initializers
.add_all
(initializers
)
264 var msignature
= new MSignature(mparameters
, null)
265 defined_init
.new_msignature
= msignature
266 self.toolcontext
.info
("{mclassdef} extends its basic constructor signature to {defined_init}{msignature}", 3)
270 # Else create the local implicit basic init definition
271 var mprop
= the_root_init_mmethod
.as(not null)
272 var mpropdef
= new MMethodDef(mclassdef
, mprop
, nclassdef
.location
)
273 mpropdef
.has_supercall
= true
274 mpropdef
.initializers
.add_all
(initializers
)
275 var msignature
= new MSignature(mparameters
, null)
276 mpropdef
.new_msignature
= msignature
277 mpropdef
.msignature
= new MSignature(new Array[MParameter], null) # always an empty real signature
278 nclassdef
.mfree_init
= mpropdef
279 self.toolcontext
.info
("{mclassdef} gets a free constructor for attributes {mpropdef}{msignature}", 3)
282 # Check the visibility of `mtype` as an element of the signature of `mpropdef`.
283 fun check_visibility
(node
: ANode, mtype
: MType, mpropdef
: MPropDef)
285 var mmodule
= mpropdef
.mclassdef
.mmodule
286 var mproperty
= mpropdef
.mproperty
288 # Extract visibility information of the main part of `mtype`
289 # It is a case-by case
290 var vis_type
: nullable MVisibility = null # The own visibility of the type
291 var mmodule_type
: nullable MModule = null # The origial module of the type
292 mtype
= mtype
.as_notnullable
293 if mtype
isa MClassType then
294 vis_type
= mtype
.mclass
.visibility
295 mmodule_type
= mtype
.mclass
.intro
.mmodule
296 else if mtype
isa MVirtualType then
297 vis_type
= mtype
.mproperty
.visibility
298 mmodule_type
= mtype
.mproperty
.intro_mclassdef
.mmodule
299 else if mtype
isa MParameterType then
300 # nothing, always visible
302 node
.debug
"Unexpected type {mtype}"
306 if vis_type
!= null then
307 assert mmodule_type
!= null
308 var vis_module_type
= mmodule
.visibility_for
(mmodule_type
) # the visibility of the original module
309 if mproperty
.visibility
> vis_type
then
310 error
(node
, "Error: The {mproperty.visibility} property `{mproperty}` cannot contain the {vis_type} type `{mtype}`")
312 else if mproperty
.visibility
> vis_module_type
then
313 error
(node
, "Error: The {mproperty.visibility} property `{mproperty}` cannot contain the type `{mtype}` from the {vis_module_type} module `{mmodule_type}`")
318 # No error, try to go deeper in generic types
319 if node
isa AType then
320 for a
in node
.n_types
do
322 if t
== null then continue # Error, thus skipped
323 check_visibility
(a
, t
, mpropdef
)
325 else if mtype
isa MGenericType then
326 for t
in mtype
.arguments
do check_visibility
(node
, t
, mpropdef
)
332 # The class whose self inherit all the constructors.
333 # FIXME: this is needed to implement the crazy constructor mixin thing of the of old compiler. We need to think what to do with since this cannot stay in the modelbuilder
334 var inherit_init_from
: nullable MClass = null
338 # Does the MPropDef contains a call to super or a call of a super-constructor?
339 # Subsequent phases of the frontend (esp. typing) set it if required
340 var has_supercall
: Bool = false is writable
343 redef class AClassdef
344 var build_properties_is_done
: Bool = false
345 # The list of super-constructor to call at the start of the free constructor
346 # FIXME: this is needed to implement the crazy constructor thing of the of old compiler. We need to think what to do with since this cannot stay in the modelbuilder
347 var super_inits
: nullable Collection[MMethod] = null
349 # The free init (implicitely constructed by the class if required)
350 var mfree_init
: nullable MMethodDef = null
353 redef class MClassDef
354 # What is the `APropdef` associated to a `MProperty`?
355 # Used to check multiple definition of a property.
356 var mprop2npropdef
: Map[MProperty, APropdef] = new HashMap[MProperty, APropdef]
360 # Join the text of all tokens
361 # Used to get the 'real name' of method definitions.
362 fun collect_text
: String
364 var v
= new TextCollectorVisitor
371 private class TextCollectorVisitor
373 var text
: String = ""
376 if n
isa Token then text
+= n
.text
382 # The associated main model entity
383 type MPROPDEF: MPropDef
385 # The associated propdef once build by a `ModelBuilder`
386 var mpropdef
: nullable MPROPDEF is writable
388 private fun build_property
(modelbuilder
: ModelBuilder, mclassdef
: MClassDef) is abstract
389 private fun build_signature
(modelbuilder
: ModelBuilder) is abstract
390 private fun check_signature
(modelbuilder
: ModelBuilder) is abstract
391 private fun new_property_visibility
(modelbuilder
: ModelBuilder, mclassdef
: MClassDef, nvisibility
: nullable AVisibility): MVisibility
393 var mvisibility
= public_visibility
394 if nvisibility
!= null then
395 mvisibility
= nvisibility
.mvisibility
396 if mvisibility
== intrude_visibility
then
397 modelbuilder
.error
(nvisibility
, "Error: intrude is not a legal visibility for properties.")
398 mvisibility
= public_visibility
401 if mclassdef
.mclass
.visibility
== private_visibility
then
402 if mvisibility
== protected_visibility
then
403 assert nvisibility
!= null
404 modelbuilder
.error
(nvisibility
, "Error: The only legal visibility for properties in a private class is private.")
405 else if mvisibility
== private_visibility
then
406 assert nvisibility
!= null
407 modelbuilder
.advice
(nvisibility
, "useless-visibility", "Warning: private is superfluous since the only legal visibility for properties in a private class is private.")
409 mvisibility
= private_visibility
414 private fun set_doc
(mpropdef
: MPropDef, modelbuilder
: ModelBuilder)
416 var ndoc
= self.n_doc
418 var mdoc
= ndoc
.to_mdoc
420 mdoc
.original_mentity
= mpropdef
421 else if mpropdef
.is_intro
and mpropdef
.mproperty
.visibility
>= protected_visibility
then
422 modelbuilder
.advice
(self, "missing-doc", "Documentation warning: Undocumented property `{mpropdef.mproperty}`")
425 var at_deprecated
= get_single_annotation
("deprecated", modelbuilder
)
426 if at_deprecated
!= null then
427 if not mpropdef
.is_intro
then
428 modelbuilder
.error
(self, "Error: method redefinition cannot be deprecated.")
430 var info
= new MDeprecationInfo
431 ndoc
= at_deprecated
.n_doc
432 if ndoc
!= null then info
.mdoc
= ndoc
.to_mdoc
433 mpropdef
.mproperty
.deprecation
= info
438 private fun check_redef_property_visibility
(modelbuilder
: ModelBuilder, nvisibility
: nullable AVisibility, mprop
: MProperty)
440 if nvisibility
== null then return
441 var mvisibility
= nvisibility
.mvisibility
442 if mvisibility
!= mprop
.visibility
and mvisibility
!= public_visibility
then
443 modelbuilder
.error
(nvisibility
, "Error: redefinition changed the visibility from a {mprop.visibility} to a {mvisibility}")
447 private fun check_redef_keyword
(modelbuilder
: ModelBuilder, mclassdef
: MClassDef, kwredef
: nullable Token, need_redef
: Bool, mprop
: MProperty): Bool
449 if mclassdef
.mprop2npropdef
.has_key
(mprop
) then
450 modelbuilder
.error
(self, "Error: A property {mprop} is already defined in class {mclassdef.mclass} at line {mclassdef.mprop2npropdef[mprop].location.line_start}.")
453 if mprop
isa MMethod and mprop
.is_toplevel
!= (parent
isa ATopClassdef) then
454 if mprop
.is_toplevel
then
455 modelbuilder
.error
(self, "Error: {mprop} is a top level method.")
457 modelbuilder
.error
(self, "Error: {mprop} is not a top level method.")
462 if kwredef
== null then
464 modelbuilder
.error
(self, "Redef error: {mclassdef.mclass}::{mprop.name} is an inherited property. To redefine it, add the redef keyword.")
468 if not need_redef
then
469 modelbuilder
.error
(self, "Error: No property {mclassdef.mclass}::{mprop.name} is inherited. Remove the redef keyword to define a new property.")
478 redef class ASignature
479 # Is the model builder has correctly visited the signature
480 var is_visited
= false
481 # Names of parameters from the AST
482 # REQUIRE: is_visited
483 var param_names
= new Array[String]
484 # Types of parameters from the AST
485 # REQUIRE: is_visited
486 var param_types
= new Array[MType]
487 # Rank of the vararg (of -1 if none)
488 # REQUIRE: is_visited
489 var vararg_rank
: Int = -1
491 var ret_type
: nullable MType = null
493 # Visit and fill information about a signature
494 private fun visit_signature
(modelbuilder
: ModelBuilder, mclassdef
: MClassDef): Bool
496 var mmodule
= mclassdef
.mmodule
497 var param_names
= self.param_names
498 var param_types
= self.param_types
499 for np
in self.n_params
do
500 param_names
.add
(np
.n_id
.text
)
501 var ntype
= np
.n_type
502 if ntype
!= null then
503 var mtype
= modelbuilder
.resolve_mtype
(mmodule
, mclassdef
, ntype
)
504 if mtype
== null then return false # Skip error
505 for i
in [0..param_names
.length-param_types
.length
[ do
506 param_types
.add
(mtype
)
508 if np
.n_dotdotdot
!= null then
509 if self.vararg_rank
!= -1 then
510 modelbuilder
.error
(np
, "Error: {param_names[self.vararg_rank]} is already a vararg")
513 self.vararg_rank
= param_names
.length
- 1
518 var ntype
= self.n_type
519 if ntype
!= null then
520 self.ret_type
= modelbuilder
.resolve_mtype
(mmodule
, mclassdef
, ntype
)
521 if self.ret_type
== null then return false # Skip errir
524 self.is_visited
= true
528 # Build a visited signature
529 fun build_signature
(modelbuilder
: ModelBuilder): nullable MSignature
531 if param_names
.length
!= param_types
.length
then
532 # Some parameters are typed, other parameters are not typed.
533 modelbuilder
.error
(self.n_params
[param_types
.length
], "Error: Untyped parameter `{param_names[param_types.length]}'.")
537 var mparameters
= new Array[MParameter]
538 for i
in [0..param_names
.length
[ do
539 var mparameter
= new MParameter(param_names
[i
], param_types
[i
], i
== vararg_rank
)
540 self.n_params
[i
].mparameter
= mparameter
541 mparameters
.add
(mparameter
)
544 var msignature
= new MSignature(mparameters
, ret_type
)
550 # The associated mparameter if any
551 var mparameter
: nullable MParameter = null
554 redef class AMethPropdef
555 redef type MPROPDEF: MMethodDef
558 # Can self be used as a root init?
559 private fun look_like_a_root_init
(modelbuilder
: ModelBuilder): Bool
561 # Need the `init` keyword
562 if n_kwinit
== null then return false
563 # Need to by anonymous
564 if self.n_methid
!= null then return false
566 if self.n_signature
.n_params
.length
> 0 then return false
567 # Cannot be private or something
568 if not self.n_visibility
isa APublicVisibility then return false
569 # No annotation on itself
570 if get_single_annotation
("old_style_init", modelbuilder
) != null then return false
572 var amod
= self.parent
.parent
.as(AModule)
573 var amoddecl
= amod
.n_moduledecl
574 if amoddecl
!= null then
575 var old
= amoddecl
.get_single_annotation
("old_style_init", modelbuilder
)
576 if old
!= null then return false
582 redef fun build_property
(modelbuilder
, mclassdef
)
584 var n_kwinit
= n_kwinit
585 var n_kwnew
= n_kwnew
586 var is_init
= n_kwinit
!= null or n_kwnew
!= null
588 var amethodid
= self.n_methid
590 if amethodid
== null then
594 else if n_kwinit
!= null then
597 else if n_kwnew
!= null then
603 else if amethodid
isa AIdMethid then
604 name
= amethodid
.n_id
.text
605 name_node
= amethodid
607 # operator, bracket or assign
608 name
= amethodid
.collect_text
609 name_node
= amethodid
611 if name
== "-" and self.n_signature
.n_params
.length
== 0 then
616 var mprop
: nullable MMethod = null
617 if not is_init
or n_kwredef
!= null then mprop
= modelbuilder
.try_get_mproperty_by_name
(name_node
, mclassdef
, name
).as(nullable MMethod)
618 if mprop
== null and look_like_a_root_init
(modelbuilder
) then
619 mprop
= modelbuilder
.the_root_init_mmethod
621 if nb
isa ABlockExpr and nb
.n_expr
.is_empty
and n_doc
== null then
622 modelbuilder
.advice
(self, "useless-init", "Warning: useless empty init in {mclassdef}")
625 if mprop
== null then
626 var mvisibility
= new_property_visibility
(modelbuilder
, mclassdef
, self.n_visibility
)
627 mprop
= new MMethod(mclassdef
, name
, mvisibility
)
628 if look_like_a_root_init
(modelbuilder
) and modelbuilder
.the_root_init_mmethod
== null then
629 modelbuilder
.the_root_init_mmethod
= mprop
630 mprop
.is_root_init
= true
632 mprop
.is_init
= is_init
633 mprop
.is_new
= n_kwnew
!= null
634 if parent
isa ATopClassdef then mprop
.is_toplevel
= true
635 if not self.check_redef_keyword
(modelbuilder
, mclassdef
, n_kwredef
, false, mprop
) then return
637 if not mprop
.is_root_init
and not self.check_redef_keyword
(modelbuilder
, mclassdef
, n_kwredef
, not self isa AMainMethPropdef, mprop
) then return
638 check_redef_property_visibility
(modelbuilder
, self.n_visibility
, mprop
)
640 mclassdef
.mprop2npropdef
[mprop
] = self
642 var mpropdef
= new MMethodDef(mclassdef
, mprop
, self.location
)
644 set_doc
(mpropdef
, modelbuilder
)
646 self.mpropdef
= mpropdef
647 modelbuilder
.mpropdef2npropdef
[mpropdef
] = self
648 if mpropdef
.is_intro
then
649 modelbuilder
.toolcontext
.info
("{mpropdef} introduces new method {mprop.full_name}", 3)
651 modelbuilder
.toolcontext
.info
("{mpropdef} redefines method {mprop.full_name}", 3)
655 redef fun build_signature
(modelbuilder
)
657 var mpropdef
= self.mpropdef
658 if mpropdef
== null then return # Error thus skiped
659 var mclassdef
= mpropdef
.mclassdef
660 var mmodule
= mclassdef
.mmodule
661 var nsig
= self.n_signature
663 # Retrieve info from the signature AST
664 var param_names
= new Array[String] # Names of parameters from the AST
665 var param_types
= new Array[MType] # Types of parameters from the AST
667 var ret_type
: nullable MType = null # Return type from the AST
669 if not nsig
.visit_signature
(modelbuilder
, mclassdef
) then return
670 param_names
= nsig
.param_names
671 param_types
= nsig
.param_types
672 vararg_rank
= nsig
.vararg_rank
673 ret_type
= nsig
.ret_type
676 # Look for some signature to inherit
677 # FIXME: do not inherit from the intro, but from the most specific
678 var msignature
: nullable MSignature = null
679 if not mpropdef
.is_intro
then
680 msignature
= mpropdef
.mproperty
.intro
.msignature
681 if msignature
== null then return # Skip error
683 # Check inherited signature arity
684 if param_names
.length
!= msignature
.arity
then
686 if nsig
!= null then node
= nsig
else node
= self
687 modelbuilder
.error
(node
, "Redef error: {mpropdef} redefines {mpropdef.mproperty.intro} with {param_names.length} parameter(s), {msignature.arity} expected. Signature is {mpropdef}{msignature}")
690 else if mpropdef
.mproperty
.is_init
then
691 # FIXME UGLY: inherit signature from a super-constructor
692 for msupertype
in mclassdef
.supertypes
do
693 msupertype
= msupertype
.anchor_to
(mmodule
, mclassdef
.bound_mtype
)
694 var candidate
= modelbuilder
.try_get_mproperty_by_name2
(self, mmodule
, msupertype
, mpropdef
.mproperty
.name
)
695 if candidate
!= null then
696 if msignature
== null then
697 msignature
= candidate
.intro
.as(MMethodDef).msignature
704 # Inherit the signature
705 if msignature
!= null and param_names
.length
!= param_types
.length
and param_names
.length
== msignature
.arity
and param_types
.length
== 0 then
706 # Parameters are untyped, thus inherit them
707 param_types
= new Array[MType]
708 for mparameter
in msignature
.mparameters
do
709 param_types
.add
(mparameter
.mtype
)
711 vararg_rank
= msignature
.vararg_rank
713 if msignature
!= null and ret_type
== null then
714 ret_type
= msignature
.return_mtype
717 if param_names
.length
!= param_types
.length
then
718 # Some parameters are typed, other parameters are not typed.
719 modelbuilder
.error
(nsig
.n_params
[param_types
.length
], "Error: Untyped parameter `{param_names[param_types.length]}'.")
723 var mparameters
= new Array[MParameter]
724 for i
in [0..param_names
.length
[ do
725 var mparameter
= new MParameter(param_names
[i
], param_types
[i
], i
== vararg_rank
)
726 if nsig
!= null then nsig
.n_params
[i
].mparameter
= mparameter
727 mparameters
.add
(mparameter
)
730 msignature
= new MSignature(mparameters
, ret_type
)
731 mpropdef
.msignature
= msignature
732 mpropdef
.is_abstract
= self.get_single_annotation
("abstract", modelbuilder
) != null
733 mpropdef
.is_intern
= self.get_single_annotation
("intern", modelbuilder
) != null
734 mpropdef
.is_extern
= self.n_extern_code_block
!= null or self.get_single_annotation
("extern", modelbuilder
) != null
737 redef fun check_signature
(modelbuilder
)
739 var mpropdef
= self.mpropdef
740 if mpropdef
== null then return # Error thus skiped
741 var mclassdef
= mpropdef
.mclassdef
742 var mmodule
= mclassdef
.mmodule
743 var nsig
= self.n_signature
744 var mysignature
= self.mpropdef
.msignature
745 if mysignature
== null then return # Error thus skiped
747 # Lookup for signature in the precursor
748 # FIXME all precursors should be considered
749 if not mpropdef
.is_intro
then
750 var msignature
= mpropdef
.mproperty
.intro
.msignature
751 if msignature
== null then return
753 var precursor_ret_type
= msignature
.return_mtype
754 var ret_type
= mysignature
.return_mtype
755 if ret_type
!= null and precursor_ret_type
== null then
756 modelbuilder
.error
(nsig
.n_type
.as(not null), "Redef Error: {mpropdef.mproperty} is a procedure, not a function.")
760 if mysignature
.arity
> 0 then
761 # Check parameters types
762 for i
in [0..mysignature
.arity
[ do
763 var myt
= mysignature
.mparameters
[i
].mtype
764 var prt
= msignature
.mparameters
[i
].mtype
765 if not myt
.is_subtype
(mmodule
, mclassdef
.bound_mtype
, prt
) or
766 not prt
.is_subtype
(mmodule
, mclassdef
.bound_mtype
, myt
) then
767 modelbuilder
.error
(nsig
.n_params
[i
], "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt} as in {mpropdef.mproperty.intro}.")
771 if precursor_ret_type
!= null then
772 if ret_type
== null then
773 # Inherit the return type
774 ret_type
= precursor_ret_type
775 else if not ret_type
.is_subtype
(mmodule
, mclassdef
.bound_mtype
, precursor_ret_type
) then
776 modelbuilder
.error
(nsig
.n_type
.as(not null), "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type} as in {mpropdef.mproperty.intro}.")
781 if mysignature
.arity
> 0 then
782 # Check parameters visibility
783 for i
in [0..mysignature
.arity
[ do
784 var nt
= nsig
.n_params
[i
].n_type
785 if nt
!= null then modelbuilder
.check_visibility
(nt
, nt
.mtype
.as(not null), mpropdef
)
788 if nt
!= null then modelbuilder
.check_visibility
(nt
, nt
.mtype
.as(not null), mpropdef
)
793 redef class AAttrPropdef
794 redef type MPROPDEF: MAttributeDef
796 # Is the node tagged `noinit`?
799 # Is the node taggeg lazy?
802 # The guard associated to a lasy attribute.
803 # Because some engines does not have a working `isset`,
804 # this additionnal attribute is used to guard the lazy initialization.
805 # TODO: to remove once isset is correctly implemented
806 var mlazypropdef
: nullable MAttributeDef
808 # The associated getter (read accessor) if any
809 var mreadpropdef
: nullable MMethodDef is writable
810 # The associated setter (write accessor) if any
811 var mwritepropdef
: nullable MMethodDef is writable
813 redef fun build_property
(modelbuilder
, mclassdef
)
815 var mclass
= mclassdef
.mclass
818 name
= self.n_id2
.text
820 if mclass
.kind
== interface_kind
or mclassdef
.mclass
.kind
== enum_kind
then
821 modelbuilder
.error
(self, "Error: Attempt to define attribute {name} in the interface {mclass}.")
822 else if mclass
.kind
== enum_kind
then
823 modelbuilder
.error
(self, "Error: Attempt to define attribute {name} in the enum class {mclass}.")
824 else if mclass
.kind
== extern_kind
then
825 modelbuilder
.error
(self, "Error: Attempt to define attribute {name} in the extern class {mclass}.")
828 # New attribute style
829 var nid2
= self.n_id2
830 var mprop
= new MAttribute(mclassdef
, "_" + name
, private_visibility
)
831 var mpropdef
= new MAttributeDef(mclassdef
, mprop
, self.location
)
832 self.mpropdef
= mpropdef
833 modelbuilder
.mpropdef2npropdef
[mpropdef
] = self
834 set_doc
(mpropdef
, modelbuilder
)
837 var mreadprop
= modelbuilder
.try_get_mproperty_by_name
(nid2
, mclassdef
, readname
).as(nullable MMethod)
838 if mreadprop
== null then
839 var mvisibility
= new_property_visibility
(modelbuilder
, mclassdef
, self.n_visibility
)
840 mreadprop
= new MMethod(mclassdef
, readname
, mvisibility
)
841 if not self.check_redef_keyword
(modelbuilder
, mclassdef
, n_kwredef
, false, mreadprop
) then return
842 mreadprop
.deprecation
= mprop
.deprecation
844 if not self.check_redef_keyword
(modelbuilder
, mclassdef
, n_kwredef
, true, mreadprop
) then return
845 check_redef_property_visibility
(modelbuilder
, self.n_visibility
, mreadprop
)
847 mclassdef
.mprop2npropdef
[mreadprop
] = self
849 var mreadpropdef
= new MMethodDef(mclassdef
, mreadprop
, self.location
)
850 self.mreadpropdef
= mreadpropdef
851 modelbuilder
.mpropdef2npropdef
[mreadpropdef
] = self
852 mreadpropdef
.mdoc
= mpropdef
.mdoc
854 var atlazy
= self.get_single_annotation
("lazy", modelbuilder
)
855 if atlazy
!= null then
856 if n_expr
== null then
857 modelbuilder
.error
(atlazy
, "Error: a lazy attribute needs a value")
860 var mlazyprop
= new MAttribute(mclassdef
, "lazy _" + name
, none_visibility
)
861 var mlazypropdef
= new MAttributeDef(mclassdef
, mlazyprop
, self.location
)
862 self.mlazypropdef
= mlazypropdef
865 var atreadonly
= self.get_single_annotation
("readonly", modelbuilder
)
866 if atreadonly
!= null then
867 if n_expr
== null then
868 modelbuilder
.error
(atreadonly
, "Error: a readonly attribute needs a value")
870 # No setter, so just leave
874 var writename
= name
+ "="
875 var atwritable
= self.get_single_annotation
("writable", modelbuilder
)
876 if atwritable
!= null then
877 if not atwritable
.n_args
.is_empty
then
878 writename
= atwritable
.arg_as_id
(modelbuilder
) or else writename
881 var mwriteprop
= modelbuilder
.try_get_mproperty_by_name
(nid2
, mclassdef
, writename
).as(nullable MMethod)
882 var nwkwredef
: nullable Token = null
883 if atwritable
!= null then nwkwredef
= atwritable
.n_kwredef
884 if mwriteprop
== null then
886 if atwritable
!= null then
887 mvisibility
= new_property_visibility
(modelbuilder
, mclassdef
, atwritable
.n_visibility
)
889 mvisibility
= private_visibility
891 mwriteprop
= new MMethod(mclassdef
, writename
, mvisibility
)
892 if not self.check_redef_keyword
(modelbuilder
, mclassdef
, nwkwredef
, false, mwriteprop
) then return
893 mwriteprop
.deprecation
= mprop
.deprecation
895 if not self.check_redef_keyword
(modelbuilder
, mclassdef
, nwkwredef
or else n_kwredef
, true, mwriteprop
) then return
896 if atwritable
!= null then
897 check_redef_property_visibility
(modelbuilder
, atwritable
.n_visibility
, mwriteprop
)
900 mclassdef
.mprop2npropdef
[mwriteprop
] = self
902 var mwritepropdef
= new MMethodDef(mclassdef
, mwriteprop
, self.location
)
903 self.mwritepropdef
= mwritepropdef
904 modelbuilder
.mpropdef2npropdef
[mwritepropdef
] = self
905 mwritepropdef
.mdoc
= mpropdef
.mdoc
908 redef fun build_signature
(modelbuilder
)
910 var mpropdef
= self.mpropdef
911 if mpropdef
== null then return # Error thus skiped
912 var mclassdef
= mpropdef
.mclassdef
913 var mmodule
= mclassdef
.mmodule
914 var mtype
: nullable MType = null
916 var mreadpropdef
= self.mreadpropdef
918 var ntype
= self.n_type
919 if ntype
!= null then
920 mtype
= modelbuilder
.resolve_mtype
(mmodule
, mclassdef
, ntype
)
921 if mtype
== null then return
924 # Inherit the type from the getter (usually an abstact getter)
925 if mtype
== null and mreadpropdef
!= null and not mreadpropdef
.is_intro
then
926 var msignature
= mreadpropdef
.mproperty
.intro
.msignature
927 if msignature
== null then return # Error, thus skiped
928 mtype
= msignature
.return_mtype
931 var nexpr
= self.n_expr
932 if mtype
== null then
933 if nexpr
!= null then
934 if nexpr
isa ANewExpr then
935 mtype
= modelbuilder
.resolve_mtype
(mmodule
, mclassdef
, nexpr
.n_type
)
936 else if nexpr
isa AIntExpr then
937 var cla
= modelbuilder
.try_get_mclass_by_name
(nexpr
, mmodule
, "Int")
938 if cla
!= null then mtype
= cla
.mclass_type
939 else if nexpr
isa AFloatExpr then
940 var cla
= modelbuilder
.try_get_mclass_by_name
(nexpr
, mmodule
, "Float")
941 if cla
!= null then mtype
= cla
.mclass_type
942 else if nexpr
isa ACharExpr then
943 var cla
= modelbuilder
.try_get_mclass_by_name
(nexpr
, mmodule
, "Char")
944 if cla
!= null then mtype
= cla
.mclass_type
945 else if nexpr
isa ABoolExpr then
946 var cla
= modelbuilder
.try_get_mclass_by_name
(nexpr
, mmodule
, "Bool")
947 if cla
!= null then mtype
= cla
.mclass_type
948 else if nexpr
isa ASuperstringExpr then
949 var cla
= modelbuilder
.try_get_mclass_by_name
(nexpr
, mmodule
, "String")
950 if cla
!= null then mtype
= cla
.mclass_type
951 else if nexpr
isa AStringFormExpr then
952 var cla
= modelbuilder
.try_get_mclass_by_name
(nexpr
, mmodule
, "String")
953 if cla
!= null then mtype
= cla
.mclass_type
955 modelbuilder
.error
(self, "Error: Untyped attribute {mpropdef}. Implicit typing allowed only for literals and new.")
958 if mtype
== null then return
960 else if ntype
!= null then
961 if nexpr
isa ANewExpr then
962 var xmtype
= modelbuilder
.resolve_mtype
(mmodule
, mclassdef
, nexpr
.n_type
)
963 if xmtype
== mtype
then
964 modelbuilder
.advice
(ntype
, "useless-type", "Warning: useless type definition")
969 if mtype
== null then
970 modelbuilder
.error
(self, "Error: Untyped attribute {mpropdef}")
974 mpropdef
.static_mtype
= mtype
976 if mreadpropdef
!= null then
977 var msignature
= new MSignature(new Array[MParameter], mtype
)
978 mreadpropdef
.msignature
= msignature
981 var mwritepropdef
= self.mwritepropdef
982 if mwritepropdef
!= null then
985 var mparameter
= new MParameter(name
, mtype
, false)
986 var msignature
= new MSignature([mparameter
], null)
987 mwritepropdef
.msignature
= msignature
990 var mlazypropdef
= self.mlazypropdef
991 if mlazypropdef
!= null then
992 mlazypropdef
.static_mtype
= modelbuilder
.model
.get_mclasses_by_name
("Bool").first
.mclass_type
996 redef fun check_signature
(modelbuilder
)
998 var mpropdef
= self.mpropdef
999 if mpropdef
== null then return # Error thus skiped
1000 var mclassdef
= mpropdef
.mclassdef
1001 var mmodule
= mclassdef
.mmodule
1002 var ntype
= self.n_type
1003 var mtype
= self.mpropdef
.static_mtype
1004 if mtype
== null then return # Error thus skiped
1006 # Lookup for signature in the precursor
1007 # FIXME all precursors should be considered
1008 if not mpropdef
.is_intro
then
1009 var precursor_type
= mpropdef
.mproperty
.intro
.static_mtype
1010 if precursor_type
== null then return
1012 if mtype
!= precursor_type
then
1013 modelbuilder
.error
(ntype
.as(not null), "Redef Error: Wrong static type. found {mtype}, expected {precursor_type}.")
1018 # Check getter and setter
1019 var meth
= self.mreadpropdef
1020 if meth
!= null then
1021 self.check_method_signature
(modelbuilder
, meth
)
1022 var node
: nullable ANode = ntype
1023 if node
== null then node
= self
1024 modelbuilder
.check_visibility
(node
, mtype
, meth
)
1026 meth
= self.mwritepropdef
1027 if meth
!= null then
1028 self.check_method_signature
(modelbuilder
, meth
)
1029 var node
: nullable ANode = ntype
1030 if node
== null then node
= self
1031 modelbuilder
.check_visibility
(node
, mtype
, meth
)
1035 private fun check_method_signature
(modelbuilder
: ModelBuilder, mpropdef
: MMethodDef)
1037 var mclassdef
= mpropdef
.mclassdef
1038 var mmodule
= mclassdef
.mmodule
1039 var nsig
= self.n_type
1040 var mysignature
= mpropdef
.msignature
1041 if mysignature
== null then return # Error thus skiped
1043 # Lookup for signature in the precursor
1044 # FIXME all precursors should be considered
1045 if not mpropdef
.is_intro
then
1046 var msignature
= mpropdef
.mproperty
.intro
.msignature
1047 if msignature
== null then return
1049 if mysignature
.arity
!= msignature
.arity
then
1051 if nsig
!= null then node
= nsig
else node
= self
1052 modelbuilder
.error
(node
, "Redef Error: {mysignature.arity} parameters found, {msignature.arity} expected. Signature is {mpropdef}{msignature}")
1055 var precursor_ret_type
= msignature
.return_mtype
1056 var ret_type
= mysignature
.return_mtype
1057 if ret_type
!= null and precursor_ret_type
== null then
1059 if nsig
!= null then node
= nsig
else node
= self
1060 modelbuilder
.error
(node
, "Redef Error: {mpropdef.mproperty} is a procedure, not a function.")
1064 if mysignature
.arity
> 0 then
1065 # Check parameters types
1066 for i
in [0..mysignature
.arity
[ do
1067 var myt
= mysignature
.mparameters
[i
].mtype
1068 var prt
= msignature
.mparameters
[i
].mtype
1069 if not myt
.is_subtype
(mmodule
, mclassdef
.bound_mtype
, prt
) or
1070 not prt
.is_subtype
(mmodule
, mclassdef
.bound_mtype
, myt
) then
1072 if nsig
!= null then node
= nsig
else node
= self
1073 modelbuilder
.error
(node
, "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt}.")
1077 if precursor_ret_type
!= null then
1078 if ret_type
== null then
1079 # Inherit the return type
1080 ret_type
= precursor_ret_type
1081 else if not ret_type
.is_subtype
(mmodule
, mclassdef
.bound_mtype
, precursor_ret_type
) then
1083 if nsig
!= null then node
= nsig
else node
= self
1084 modelbuilder
.error
(node
, "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type}.")
1091 redef class ATypePropdef
1092 redef type MPROPDEF: MVirtualTypeDef
1094 redef fun build_property
(modelbuilder
, mclassdef
)
1096 var name
= self.n_id
.text
1097 var mprop
= modelbuilder
.try_get_mproperty_by_name
(self.n_id
, mclassdef
, name
)
1098 if mprop
== null then
1099 var mvisibility
= new_property_visibility
(modelbuilder
, mclassdef
, self.n_visibility
)
1100 mprop
= new MVirtualTypeProp(mclassdef
, name
, mvisibility
)
1101 for c
in name
.chars
do if c
>= 'a' and c
<= 'z' then
1102 modelbuilder
.warning
(n_id
, "bad-type-name", "Warning: lowercase in the virtual type {name}")
1105 if not self.check_redef_keyword
(modelbuilder
, mclassdef
, self.n_kwredef
, false, mprop
) then return
1107 if not self.check_redef_keyword
(modelbuilder
, mclassdef
, self.n_kwredef
, true, mprop
) then return
1108 assert mprop
isa MVirtualTypeProp
1109 check_redef_property_visibility
(modelbuilder
, self.n_visibility
, mprop
)
1111 mclassdef
.mprop2npropdef
[mprop
] = self
1113 var mpropdef
= new MVirtualTypeDef(mclassdef
, mprop
, self.location
)
1114 self.mpropdef
= mpropdef
1115 modelbuilder
.mpropdef2npropdef
[mpropdef
] = self
1116 set_doc
(mpropdef
, modelbuilder
)
1118 var atfixed
= get_single_annotation
("fixed", modelbuilder
)
1119 if atfixed
!= null then
1120 mpropdef
.is_fixed
= true
1124 redef fun build_signature
(modelbuilder
)
1126 var mpropdef
= self.mpropdef
1127 if mpropdef
== null then return # Error thus skiped
1128 var mclassdef
= mpropdef
.mclassdef
1129 var mmodule
= mclassdef
.mmodule
1130 var mtype
: nullable MType = null
1132 var ntype
= self.n_type
1133 mtype
= modelbuilder
.resolve_mtype
(mmodule
, mclassdef
, ntype
)
1134 if mtype
== null then return
1136 mpropdef
.bound
= mtype
1137 # print "{mpropdef}: {mtype}"
1140 redef fun check_signature
(modelbuilder
)
1142 var mpropdef
= self.mpropdef
1143 if mpropdef
== null then return # Error thus skiped
1145 var bound
= self.mpropdef
.bound
1146 if bound
== null then return # Error thus skiped
1148 modelbuilder
.check_visibility
(n_type
, bound
, mpropdef
)
1150 var mclassdef
= mpropdef
.mclassdef
1151 var mmodule
= mclassdef
.mmodule
1152 var anchor
= mclassdef
.bound_mtype
1155 if bound
isa MVirtualType then
1156 # Slow case: progress on each resolution until: (i) we loop, or (ii) we found a non formal type
1157 var seen
= [self.mpropdef
.mproperty
.mvirtualtype
]
1159 if seen
.has
(bound
) then
1161 modelbuilder
.error
(self, "Error: circularity of virtual type definition: {seen.join(" -> ")}")
1165 var next
= bound
.lookup_bound
(mmodule
, anchor
)
1166 if not next
isa MVirtualType then break
1171 # Check redefinitions
1172 bound
= mpropdef
.bound
.as(not null)
1173 for p
in mpropdef
.mproperty
.lookup_super_definitions
(mmodule
, anchor
) do
1174 var supbound
= p
.bound
.as(not null)
1176 modelbuilder
.error
(self, "Redef Error: Virtual type {mpropdef.mproperty} is fixed in super-class {p.mclassdef.mclass}")
1179 if p
.mclassdef
.mclass
== mclassdef
.mclass
then
1180 # Still a warning to pass existing bad code
1181 modelbuilder
.warning
(n_type
, "refine-type", "Redef Error: a virtual type cannot be refined.")
1184 if not bound
.is_subtype
(mmodule
, anchor
, supbound
) then
1185 modelbuilder
.error
(n_type
, "Redef Error: Wrong bound type. Found {bound}, expected a subtype of {supbound}, as in {p}.")