modelize: remove a useless local variable
[nit.git] / src / modelize / modelize_property.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2012 Jean Privat <jean@pryen.org>
4 #
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
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
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.
16
17 # Analysis and verification of property definitions to instantiate model element
18 module modelize_property
19
20 import modelize_class
21 private import annotation
22
23 redef class ToolContext
24 var modelize_property_phase: Phase = new ModelizePropertyPhase(self, [modelize_class_phase])
25 end
26
27 private class ModelizePropertyPhase
28 super Phase
29 redef fun process_nmodule(nmodule)
30 do
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)
34 end
35 end
36 end
37
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]
42
43 # Build the properties of `nclassdef`.
44 # REQUIRE: all superclasses are built.
45 private fun build_properties(nclassdef: AClassdef)
46 do
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])
55 end
56
57 for nclassdef2 in nclassdef.all_defs do
58 for npropdef in nclassdef2.n_propdefs do
59 npropdef.build_property(self, mclassdef)
60 end
61 for npropdef in nclassdef2.n_propdefs do
62 npropdef.build_signature(self)
63 end
64 for npropdef in nclassdef2.n_propdefs do
65 npropdef.check_signature(self)
66 end
67 end
68 process_default_constructors(nclassdef)
69 end
70
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
75
76 # Introduce or inherit default constructor
77 # This is the last part of `build_properties`.
78 private fun process_default_constructors(nclassdef: AClassdef)
79 do
80 var mclassdef = nclassdef.mclassdef.as(not null)
81
82 # Are we a refinement
83 if not mclassdef.is_intro then return
84
85 # Look for the init in Object, or create it
86 if mclassdef.mclass.name == "Object" and the_root_init_mmethod == null then
87 # Create the implicit root-init method
88 var mprop = new MMethod(mclassdef, "init", mclassdef.mclass.visibility)
89 mprop.is_root_init = true
90 var mpropdef = new MMethodDef(mclassdef, mprop, nclassdef.location)
91 var mparameters = new Array[MParameter]
92 var msignature = new MSignature(mparameters, null)
93 mpropdef.msignature = msignature
94 mpropdef.new_msignature = msignature
95 mprop.is_init = true
96 nclassdef.mfree_init = mpropdef
97 self.toolcontext.info("{mclassdef} gets a free empty constructor {mpropdef}{msignature}", 3)
98 the_root_init_mmethod = mprop
99 return
100 end
101
102 # Is the class forbid constructors?
103 if not mclassdef.mclass.kind.need_init then return
104
105 # Is there already a constructor defined?
106 var defined_init: nullable MMethodDef = null
107 for mpropdef in mclassdef.mpropdefs do
108 if not mpropdef isa MMethodDef then continue
109 if not mpropdef.mproperty.is_init then continue
110 if mpropdef.mproperty.is_root_init then
111 assert defined_init == null
112 defined_init = mpropdef
113 else if mpropdef.mproperty.name == "init" then
114 # An explicit old-style init named "init", so return
115 return
116 end
117 end
118
119 if not nclassdef isa AStdClassdef then return
120
121 # Collect undefined attributes
122 var mparameters = new Array[MParameter]
123 var initializers = new Array[MProperty]
124 for npropdef in nclassdef.n_propdefs do
125 if npropdef isa AAttrPropdef then
126 if npropdef.mpropdef == null then return # Skip broken attribute
127 var at = npropdef.get_single_annotation("noinit", self)
128 if at != null then
129 npropdef.noinit = true
130 if npropdef.n_expr != null then
131 self.error(at, "Error: `noinit` attributes cannot have an initial value")
132 end
133 continue # Skip noinit attributes
134 end
135 if npropdef.n_expr != null then continue
136 var paramname = npropdef.mpropdef.mproperty.name.substring_from(1)
137 var ret_type = npropdef.mpropdef.static_mtype
138 if ret_type == null then return
139 var mparameter = new MParameter(paramname, ret_type, false)
140 mparameters.add(mparameter)
141 var msetter = npropdef.mwritepropdef
142 if msetter == null then
143 # No setter, it is a old-style attribute, so just add it
144 initializers.add(npropdef.mpropdef.mproperty)
145 else
146 # Add the setter to the list
147 initializers.add(msetter.mproperty)
148 end
149 end
150 end
151
152 if the_root_init_mmethod == null then return
153
154 # Look for most-specific new-stype init definitions
155 var spropdefs = the_root_init_mmethod.lookup_super_definitions(mclassdef.mmodule, mclassdef.bound_mtype)
156 if spropdefs.is_empty then
157 toolcontext.fatal_error(nclassdef.location, "Fatal error: {mclassdef} does not specialize {the_root_init_mmethod.intro_mclassdef}. Possible duplication of the root class `Object`?")
158 end
159
160 # Search the longest-one and checks for conflict
161 var longest = spropdefs.first
162 if spropdefs.length > 1 then
163 # Check for conflict in the order of initializers
164 # Each initializer list must me a prefix of the longest list
165 # part 1. find the longest list
166 for spd in spropdefs do
167 if spd.initializers.length > longest.initializers.length then longest = spd
168 end
169 # part 2. compare
170 for spd in spropdefs do
171 var i = 0
172 for p in spd.initializers do
173 if p != longest.initializers[i] then
174 self.error(nclassdef, "Error: conflict for inherited inits {spd}({spd.initializers.join(", ")}) and {longest}({longest.initializers.join(", ")})")
175 return
176 end
177 i += 1
178 end
179 end
180 end
181
182 # Can we just inherit?
183 if spropdefs.length == 1 and mparameters.is_empty and defined_init == null then
184 self.toolcontext.info("{mclassdef} inherits the basic constructor {longest}", 3)
185 return
186 end
187
188 # Combine the inherited list to what is collected
189 if longest.initializers.length > 0 then
190 mparameters.prepend longest.new_msignature.mparameters
191 initializers.prepend longest.initializers
192 end
193
194 # If we already have a basic init definition, then setup its initializers
195 if defined_init != null then
196 defined_init.initializers.add_all(initializers)
197 var msignature = new MSignature(mparameters, null)
198 defined_init.new_msignature = msignature
199 self.toolcontext.info("{mclassdef} extends its basic constructor signature to {defined_init}{msignature}", 3)
200 return
201 end
202
203 # Else create the local implicit basic init definition
204 var mprop = the_root_init_mmethod.as(not null)
205 var mpropdef = new MMethodDef(mclassdef, mprop, nclassdef.location)
206 mpropdef.has_supercall = true
207 mpropdef.initializers.add_all(initializers)
208 var msignature = new MSignature(mparameters, null)
209 mpropdef.new_msignature = msignature
210 mpropdef.msignature = new MSignature(new Array[MParameter], null) # always an empty real signature
211 nclassdef.mfree_init = mpropdef
212 self.toolcontext.info("{mclassdef} gets a free constructor for attributes {mpropdef}{msignature}", 3)
213 end
214
215 # Check the visibility of `mtype` as an element of the signature of `mpropdef`.
216 fun check_visibility(node: ANode, mtype: MType, mpropdef: MPropDef)
217 do
218 var mmodule = mpropdef.mclassdef.mmodule
219 var mproperty = mpropdef.mproperty
220
221 # Extract visibility information of the main part of `mtype`
222 # It is a case-by case
223 var vis_type: nullable MVisibility = null # The own visibility of the type
224 var mmodule_type: nullable MModule = null # The origial module of the type
225 mtype = mtype.as_notnullable
226 if mtype isa MClassType then
227 vis_type = mtype.mclass.visibility
228 mmodule_type = mtype.mclass.intro.mmodule
229 else if mtype isa MVirtualType then
230 vis_type = mtype.mproperty.visibility
231 mmodule_type = mtype.mproperty.intro_mclassdef.mmodule
232 else if mtype isa MParameterType then
233 # nothing, always visible
234 else
235 node.debug "Unexpected type {mtype}"
236 abort
237 end
238
239 if vis_type != null then
240 assert mmodule_type != null
241 var vis_module_type = mmodule.visibility_for(mmodule_type) # the visibility of the original module
242 if mproperty.visibility > vis_type then
243 error(node, "Error: The {mproperty.visibility} property `{mproperty}` cannot contain the {vis_type} type `{mtype}`")
244 return
245 else if mproperty.visibility > vis_module_type then
246 error(node, "Error: The {mproperty.visibility} property `{mproperty}` cannot contain the type `{mtype}` from the {vis_module_type} module `{mmodule_type}`")
247 return
248 end
249 end
250
251 # No error, try to go deeper in generic types
252 if node isa AType then
253 for a in node.n_types do
254 var t = a.mtype
255 if t == null then continue # Error, thus skipped
256 check_visibility(a, t, mpropdef)
257 end
258 else if mtype isa MGenericType then
259 for t in mtype.arguments do check_visibility(node, t, mpropdef)
260 end
261 end
262 end
263
264 redef class MPropDef
265 # Does the MPropDef contains a call to super or a call of a super-constructor?
266 # Subsequent phases of the frontend (esp. typing) set it if required
267 var has_supercall: Bool = false is writable
268 end
269
270 redef class AClassdef
271 var build_properties_is_done: Bool = false
272 # The list of super-constructor to call at the start of the free constructor
273 # 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
274 var super_inits: nullable Collection[MMethod] = null
275
276 # The free init (implicitely constructed by the class if required)
277 var mfree_init: nullable MMethodDef = null
278 end
279
280 redef class MClassDef
281 # What is the `APropdef` associated to a `MProperty`?
282 # Used to check multiple definition of a property.
283 var mprop2npropdef: Map[MProperty, APropdef] = new HashMap[MProperty, APropdef]
284 end
285
286 redef class Prod
287 # Join the text of all tokens
288 # Used to get the 'real name' of method definitions.
289 fun collect_text: String
290 do
291 var v = new TextCollectorVisitor
292 v.enter_visit(self)
293 assert v.text != ""
294 return v.text
295 end
296 end
297
298 private class TextCollectorVisitor
299 super Visitor
300 var text: String = ""
301 redef fun visit(n)
302 do
303 if n isa Token then text += n.text
304 n.visit_all(self)
305 end
306 end
307
308 redef class APropdef
309 # The associated main model entity
310 type MPROPDEF: MPropDef
311
312 # The associated propdef once build by a `ModelBuilder`
313 var mpropdef: nullable MPROPDEF is writable
314
315 private fun build_property(modelbuilder: ModelBuilder, mclassdef: MClassDef) is abstract
316 private fun build_signature(modelbuilder: ModelBuilder) is abstract
317 private fun check_signature(modelbuilder: ModelBuilder) is abstract
318 private fun new_property_visibility(modelbuilder: ModelBuilder, mclassdef: MClassDef, nvisibility: nullable AVisibility): MVisibility
319 do
320 var mvisibility = public_visibility
321 if nvisibility != null then
322 mvisibility = nvisibility.mvisibility
323 if mvisibility == intrude_visibility then
324 modelbuilder.error(nvisibility, "Error: intrude is not a legal visibility for properties.")
325 mvisibility = public_visibility
326 end
327 end
328 if mclassdef.mclass.visibility == private_visibility then
329 if mvisibility == protected_visibility then
330 assert nvisibility != null
331 modelbuilder.error(nvisibility, "Error: The only legal visibility for properties in a private class is private.")
332 else if mvisibility == private_visibility then
333 assert nvisibility != null
334 modelbuilder.advice(nvisibility, "useless-visibility", "Warning: private is superfluous since the only legal visibility for properties in a private class is private.")
335 end
336 mvisibility = private_visibility
337 end
338 return mvisibility
339 end
340
341 private fun set_doc(mpropdef: MPropDef, modelbuilder: ModelBuilder)
342 do
343 var ndoc = self.n_doc
344 if ndoc != null then
345 var mdoc = ndoc.to_mdoc
346 mpropdef.mdoc = mdoc
347 mdoc.original_mentity = mpropdef
348 else if mpropdef.is_intro and mpropdef.mproperty.visibility >= protected_visibility then
349 modelbuilder.advice(self, "missing-doc", "Documentation warning: Undocumented property `{mpropdef.mproperty}`")
350 end
351
352 var at_deprecated = get_single_annotation("deprecated", modelbuilder)
353 if at_deprecated != null then
354 if not mpropdef.is_intro then
355 modelbuilder.error(self, "Error: method redefinition cannot be deprecated.")
356 else
357 var info = new MDeprecationInfo
358 ndoc = at_deprecated.n_doc
359 if ndoc != null then info.mdoc = ndoc.to_mdoc
360 mpropdef.mproperty.deprecation = info
361 end
362 end
363 end
364
365 private fun check_redef_property_visibility(modelbuilder: ModelBuilder, nvisibility: nullable AVisibility, mprop: MProperty)
366 do
367 if nvisibility == null then return
368 var mvisibility = nvisibility.mvisibility
369 if mvisibility != mprop.visibility and mvisibility != public_visibility then
370 modelbuilder.error(nvisibility, "Error: redefinition changed the visibility from a {mprop.visibility} to a {mvisibility}")
371 end
372 end
373
374 private fun check_redef_keyword(modelbuilder: ModelBuilder, mclassdef: MClassDef, kwredef: nullable Token, need_redef: Bool, mprop: MProperty): Bool
375 do
376 if mclassdef.mprop2npropdef.has_key(mprop) then
377 modelbuilder.error(self, "Error: A property {mprop} is already defined in class {mclassdef.mclass} at line {mclassdef.mprop2npropdef[mprop].location.line_start}.")
378 return false
379 end
380 if mprop isa MMethod and mprop.is_toplevel != (parent isa ATopClassdef) then
381 if mprop.is_toplevel then
382 modelbuilder.error(self, "Error: {mprop} is a top level method.")
383 else
384 modelbuilder.error(self, "Error: {mprop} is not a top level method.")
385 end
386 return false
387
388 end
389 if kwredef == null then
390 if need_redef then
391 modelbuilder.error(self, "Redef error: {mclassdef.mclass}::{mprop.name} is an inherited property. To redefine it, add the redef keyword.")
392 return false
393 end
394 else
395 if not need_redef then
396 modelbuilder.error(self, "Error: No property {mclassdef.mclass}::{mprop.name} is inherited. Remove the redef keyword to define a new property.")
397 return false
398 end
399 end
400 return true
401 end
402
403 end
404
405 redef class ASignature
406 # Is the model builder has correctly visited the signature
407 var is_visited = false
408 # Names of parameters from the AST
409 # REQUIRE: is_visited
410 var param_names = new Array[String]
411 # Types of parameters from the AST
412 # REQUIRE: is_visited
413 var param_types = new Array[MType]
414 # Rank of the vararg (of -1 if none)
415 # REQUIRE: is_visited
416 var vararg_rank: Int = -1
417 # Return type
418 var ret_type: nullable MType = null
419
420 # Visit and fill information about a signature
421 private fun visit_signature(modelbuilder: ModelBuilder, mclassdef: MClassDef): Bool
422 do
423 var mmodule = mclassdef.mmodule
424 var param_names = self.param_names
425 var param_types = self.param_types
426 for np in self.n_params do
427 param_names.add(np.n_id.text)
428 var ntype = np.n_type
429 if ntype != null then
430 var mtype = modelbuilder.resolve_mtype(mmodule, mclassdef, ntype)
431 if mtype == null then return false # Skip error
432 for i in [0..param_names.length-param_types.length[ do
433 param_types.add(mtype)
434 end
435 if np.n_dotdotdot != null then
436 if self.vararg_rank != -1 then
437 modelbuilder.error(np, "Error: {param_names[self.vararg_rank]} is already a vararg")
438 return false
439 else
440 self.vararg_rank = param_names.length - 1
441 end
442 end
443 end
444 end
445 var ntype = self.n_type
446 if ntype != null then
447 self.ret_type = modelbuilder.resolve_mtype(mmodule, mclassdef, ntype)
448 if self.ret_type == null then return false # Skip errir
449 end
450
451 self.is_visited = true
452 return true
453 end
454
455 # Build a visited signature
456 fun build_signature(modelbuilder: ModelBuilder): nullable MSignature
457 do
458 if param_names.length != param_types.length then
459 # Some parameters are typed, other parameters are not typed.
460 modelbuilder.error(self.n_params[param_types.length], "Error: Untyped parameter `{param_names[param_types.length]}'.")
461 return null
462 end
463
464 var mparameters = new Array[MParameter]
465 for i in [0..param_names.length[ do
466 var mparameter = new MParameter(param_names[i], param_types[i], i == vararg_rank)
467 self.n_params[i].mparameter = mparameter
468 mparameters.add(mparameter)
469 end
470
471 var msignature = new MSignature(mparameters, ret_type)
472 return msignature
473 end
474 end
475
476 redef class AParam
477 # The associated mparameter if any
478 var mparameter: nullable MParameter = null
479 end
480
481 redef class AMethPropdef
482 redef type MPROPDEF: MMethodDef
483
484
485 # Can self be used as a root init?
486 private fun look_like_a_root_init(modelbuilder: ModelBuilder): Bool
487 do
488 # Need the `init` keyword
489 if n_kwinit == null then return false
490 # Need to by anonymous
491 if self.n_methid != null then return false
492 # No parameters
493 if self.n_signature.n_params.length > 0 then return false
494 # Cannot be private or something
495 if not self.n_visibility isa APublicVisibility then return false
496 # No annotation on itself
497 if get_single_annotation("old_style_init", modelbuilder) != null then return false
498 # Nor on its module
499 var amod = self.parent.parent.as(AModule)
500 var amoddecl = amod.n_moduledecl
501 if amoddecl != null then
502 var old = amoddecl.get_single_annotation("old_style_init", modelbuilder)
503 if old != null then return false
504 end
505
506 return true
507 end
508
509 redef fun build_property(modelbuilder, mclassdef)
510 do
511 var n_kwinit = n_kwinit
512 var n_kwnew = n_kwnew
513 var is_init = n_kwinit != null or n_kwnew != null
514 var name: String
515 var amethodid = self.n_methid
516 var name_node: ANode
517 if amethodid == null then
518 if not is_init then
519 name = "main"
520 name_node = self
521 else if n_kwinit != null then
522 name = "init"
523 name_node = n_kwinit
524 else if n_kwnew != null then
525 name = "init"
526 name_node = n_kwnew
527 else
528 abort
529 end
530 else if amethodid isa AIdMethid then
531 name = amethodid.n_id.text
532 name_node = amethodid
533 else
534 # operator, bracket or assign
535 name = amethodid.collect_text
536 name_node = amethodid
537
538 if name == "-" and self.n_signature.n_params.length == 0 then
539 name = "unary -"
540 end
541 end
542
543 var mprop: nullable MMethod = null
544 if not is_init or n_kwredef != null then mprop = modelbuilder.try_get_mproperty_by_name(name_node, mclassdef, name).as(nullable MMethod)
545 if mprop == null and look_like_a_root_init(modelbuilder) then
546 mprop = modelbuilder.the_root_init_mmethod
547 var nb = n_block
548 if nb isa ABlockExpr and nb.n_expr.is_empty and n_doc == null then
549 modelbuilder.advice(self, "useless-init", "Warning: useless empty init in {mclassdef}")
550 end
551 end
552 if mprop == null then
553 var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility)
554 mprop = new MMethod(mclassdef, name, mvisibility)
555 if look_like_a_root_init(modelbuilder) and modelbuilder.the_root_init_mmethod == null then
556 modelbuilder.the_root_init_mmethod = mprop
557 mprop.is_root_init = true
558 end
559 mprop.is_init = is_init
560 mprop.is_new = n_kwnew != null
561 if parent isa ATopClassdef then mprop.is_toplevel = true
562 if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mprop) then return
563 else
564 if not mprop.is_root_init and not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, not self isa AMainMethPropdef, mprop) then return
565 check_redef_property_visibility(modelbuilder, self.n_visibility, mprop)
566 end
567 mclassdef.mprop2npropdef[mprop] = self
568
569 var mpropdef = new MMethodDef(mclassdef, mprop, self.location)
570
571 set_doc(mpropdef, modelbuilder)
572
573 self.mpropdef = mpropdef
574 modelbuilder.mpropdef2npropdef[mpropdef] = self
575 if mpropdef.is_intro then
576 modelbuilder.toolcontext.info("{mpropdef} introduces new method {mprop.full_name}", 3)
577 else
578 modelbuilder.toolcontext.info("{mpropdef} redefines method {mprop.full_name}", 3)
579 end
580 end
581
582 redef fun build_signature(modelbuilder)
583 do
584 var mpropdef = self.mpropdef
585 if mpropdef == null then return # Error thus skiped
586 var mclassdef = mpropdef.mclassdef
587 var mmodule = mclassdef.mmodule
588 var nsig = self.n_signature
589
590 # Retrieve info from the signature AST
591 var param_names = new Array[String] # Names of parameters from the AST
592 var param_types = new Array[MType] # Types of parameters from the AST
593 var vararg_rank = -1
594 var ret_type: nullable MType = null # Return type from the AST
595 if nsig != null then
596 if not nsig.visit_signature(modelbuilder, mclassdef) then return
597 param_names = nsig.param_names
598 param_types = nsig.param_types
599 vararg_rank = nsig.vararg_rank
600 ret_type = nsig.ret_type
601 end
602
603 # Look for some signature to inherit
604 # FIXME: do not inherit from the intro, but from the most specific
605 var msignature: nullable MSignature = null
606 if not mpropdef.is_intro then
607 msignature = mpropdef.mproperty.intro.msignature
608 if msignature == null then return # Skip error
609
610 # Check inherited signature arity
611 if param_names.length != msignature.arity then
612 var node: ANode
613 if nsig != null then node = nsig else node = self
614 modelbuilder.error(node, "Redef error: {mpropdef} redefines {mpropdef.mproperty.intro} with {param_names.length} parameter(s), {msignature.arity} expected. Signature is {mpropdef}{msignature}")
615 return
616 end
617 else if mpropdef.mproperty.is_init then
618 # FIXME UGLY: inherit signature from a super-constructor
619 for msupertype in mclassdef.supertypes do
620 msupertype = msupertype.anchor_to(mmodule, mclassdef.bound_mtype)
621 var candidate = modelbuilder.try_get_mproperty_by_name2(self, mmodule, msupertype, mpropdef.mproperty.name)
622 if candidate != null then
623 if msignature == null then
624 msignature = candidate.intro.as(MMethodDef).msignature
625 end
626 end
627 end
628 end
629
630
631 # Inherit the signature
632 if msignature != null and param_names.length != param_types.length and param_names.length == msignature.arity and param_types.length == 0 then
633 # Parameters are untyped, thus inherit them
634 param_types = new Array[MType]
635 for mparameter in msignature.mparameters do
636 param_types.add(mparameter.mtype)
637 end
638 vararg_rank = msignature.vararg_rank
639 end
640 if msignature != null and ret_type == null then
641 ret_type = msignature.return_mtype
642 end
643
644 if param_names.length != param_types.length then
645 # Some parameters are typed, other parameters are not typed.
646 modelbuilder.error(nsig.n_params[param_types.length], "Error: Untyped parameter `{param_names[param_types.length]}'.")
647 return
648 end
649
650 var mparameters = new Array[MParameter]
651 for i in [0..param_names.length[ do
652 var mparameter = new MParameter(param_names[i], param_types[i], i == vararg_rank)
653 if nsig != null then nsig.n_params[i].mparameter = mparameter
654 mparameters.add(mparameter)
655 end
656
657 msignature = new MSignature(mparameters, ret_type)
658 mpropdef.msignature = msignature
659 mpropdef.is_abstract = self.get_single_annotation("abstract", modelbuilder) != null
660 mpropdef.is_intern = self.get_single_annotation("intern", modelbuilder) != null
661 mpropdef.is_extern = self.n_extern_code_block != null or self.get_single_annotation("extern", modelbuilder) != null
662 end
663
664 redef fun check_signature(modelbuilder)
665 do
666 var mpropdef = self.mpropdef
667 if mpropdef == null then return # Error thus skiped
668 var mclassdef = mpropdef.mclassdef
669 var mmodule = mclassdef.mmodule
670 var nsig = self.n_signature
671 var mysignature = self.mpropdef.msignature
672 if mysignature == null then return # Error thus skiped
673
674 # Lookup for signature in the precursor
675 # FIXME all precursors should be considered
676 if not mpropdef.is_intro then
677 var msignature = mpropdef.mproperty.intro.msignature
678 if msignature == null then return
679
680 var precursor_ret_type = msignature.return_mtype
681 var ret_type = mysignature.return_mtype
682 if ret_type != null and precursor_ret_type == null then
683 modelbuilder.error(nsig.n_type.as(not null), "Redef Error: {mpropdef.mproperty} is a procedure, not a function.")
684 return
685 end
686
687 if mysignature.arity > 0 then
688 # Check parameters types
689 for i in [0..mysignature.arity[ do
690 var myt = mysignature.mparameters[i].mtype
691 var prt = msignature.mparameters[i].mtype
692 if not myt.is_subtype(mmodule, mclassdef.bound_mtype, prt) or
693 not prt.is_subtype(mmodule, mclassdef.bound_mtype, myt) then
694 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}.")
695 end
696 end
697 end
698 if precursor_ret_type != null then
699 if ret_type == null then
700 # Inherit the return type
701 ret_type = precursor_ret_type
702 else if not ret_type.is_subtype(mmodule, mclassdef.bound_mtype, precursor_ret_type) then
703 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}.")
704 end
705 end
706 end
707
708 if mysignature.arity > 0 then
709 # Check parameters visibility
710 for i in [0..mysignature.arity[ do
711 var nt = nsig.n_params[i].n_type
712 if nt != null then modelbuilder.check_visibility(nt, nt.mtype.as(not null), mpropdef)
713 end
714 var nt = nsig.n_type
715 if nt != null then modelbuilder.check_visibility(nt, nt.mtype.as(not null), mpropdef)
716 end
717 end
718 end
719
720 redef class AAttrPropdef
721 redef type MPROPDEF: MAttributeDef
722
723 # Is the node tagged `noinit`?
724 var noinit = false
725
726 # Is the node taggeg lazy?
727 var is_lazy = false
728
729 # The guard associated to a lasy attribute.
730 # Because some engines does not have a working `isset`,
731 # this additionnal attribute is used to guard the lazy initialization.
732 # TODO: to remove once isset is correctly implemented
733 var mlazypropdef: nullable MAttributeDef
734
735 # The associated getter (read accessor) if any
736 var mreadpropdef: nullable MMethodDef is writable
737 # The associated setter (write accessor) if any
738 var mwritepropdef: nullable MMethodDef is writable
739
740 redef fun build_property(modelbuilder, mclassdef)
741 do
742 var mclass = mclassdef.mclass
743
744 var name: String
745 name = self.n_id2.text
746
747 if mclass.kind == interface_kind or mclassdef.mclass.kind == enum_kind then
748 modelbuilder.error(self, "Error: Attempt to define attribute {name} in the interface {mclass}.")
749 else if mclass.kind == enum_kind then
750 modelbuilder.error(self, "Error: Attempt to define attribute {name} in the enum class {mclass}.")
751 else if mclass.kind == extern_kind then
752 modelbuilder.error(self, "Error: Attempt to define attribute {name} in the extern class {mclass}.")
753 end
754
755 # New attribute style
756 var nid2 = self.n_id2
757 var mprop = new MAttribute(mclassdef, "_" + name, private_visibility)
758 var mpropdef = new MAttributeDef(mclassdef, mprop, self.location)
759 self.mpropdef = mpropdef
760 modelbuilder.mpropdef2npropdef[mpropdef] = self
761 set_doc(mpropdef, modelbuilder)
762
763 var readname = name
764 var mreadprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, readname).as(nullable MMethod)
765 if mreadprop == null then
766 var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility)
767 mreadprop = new MMethod(mclassdef, readname, mvisibility)
768 if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mreadprop) then return
769 mreadprop.deprecation = mprop.deprecation
770 else
771 if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, true, mreadprop) then return
772 check_redef_property_visibility(modelbuilder, self.n_visibility, mreadprop)
773 end
774 mclassdef.mprop2npropdef[mreadprop] = self
775
776 var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location)
777 self.mreadpropdef = mreadpropdef
778 modelbuilder.mpropdef2npropdef[mreadpropdef] = self
779 mreadpropdef.mdoc = mpropdef.mdoc
780
781 var atlazy = self.get_single_annotation("lazy", modelbuilder)
782 if atlazy != null then
783 if n_expr == null then
784 modelbuilder.error(atlazy, "Error: a lazy attribute needs a value")
785 end
786 is_lazy = true
787 var mlazyprop = new MAttribute(mclassdef, "lazy _" + name, none_visibility)
788 var mlazypropdef = new MAttributeDef(mclassdef, mlazyprop, self.location)
789 self.mlazypropdef = mlazypropdef
790 end
791
792 var atreadonly = self.get_single_annotation("readonly", modelbuilder)
793 if atreadonly != null then
794 if n_expr == null then
795 modelbuilder.error(atreadonly, "Error: a readonly attribute needs a value")
796 end
797 # No setter, so just leave
798 return
799 end
800
801 var writename = name + "="
802 var atwritable = self.get_single_annotation("writable", modelbuilder)
803 if atwritable != null then
804 if not atwritable.n_args.is_empty then
805 writename = atwritable.arg_as_id(modelbuilder) or else writename
806 end
807 end
808 var mwriteprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, writename).as(nullable MMethod)
809 var nwkwredef: nullable Token = null
810 if atwritable != null then nwkwredef = atwritable.n_kwredef
811 if mwriteprop == null then
812 var mvisibility
813 if atwritable != null then
814 mvisibility = new_property_visibility(modelbuilder, mclassdef, atwritable.n_visibility)
815 else
816 mvisibility = private_visibility
817 end
818 mwriteprop = new MMethod(mclassdef, writename, mvisibility)
819 if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef, false, mwriteprop) then return
820 mwriteprop.deprecation = mprop.deprecation
821 else
822 if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef or else n_kwredef, true, mwriteprop) then return
823 if atwritable != null then
824 check_redef_property_visibility(modelbuilder, atwritable.n_visibility, mwriteprop)
825 end
826 end
827 mclassdef.mprop2npropdef[mwriteprop] = self
828
829 var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location)
830 self.mwritepropdef = mwritepropdef
831 modelbuilder.mpropdef2npropdef[mwritepropdef] = self
832 mwritepropdef.mdoc = mpropdef.mdoc
833 end
834
835 redef fun build_signature(modelbuilder)
836 do
837 var mpropdef = self.mpropdef
838 if mpropdef == null then return # Error thus skiped
839 var mclassdef = mpropdef.mclassdef
840 var mmodule = mclassdef.mmodule
841 var mtype: nullable MType = null
842
843 var mreadpropdef = self.mreadpropdef
844
845 var ntype = self.n_type
846 if ntype != null then
847 mtype = modelbuilder.resolve_mtype(mmodule, mclassdef, ntype)
848 if mtype == null then return
849 end
850
851 # Inherit the type from the getter (usually an abstact getter)
852 if mtype == null and mreadpropdef != null and not mreadpropdef.is_intro then
853 var msignature = mreadpropdef.mproperty.intro.msignature
854 if msignature == null then return # Error, thus skiped
855 mtype = msignature.return_mtype
856 end
857
858 var nexpr = self.n_expr
859 if mtype == null then
860 if nexpr != null then
861 if nexpr isa ANewExpr then
862 mtype = modelbuilder.resolve_mtype(mmodule, mclassdef, nexpr.n_type)
863 else if nexpr isa AIntExpr then
864 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Int")
865 if cla != null then mtype = cla.mclass_type
866 else if nexpr isa AFloatExpr then
867 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Float")
868 if cla != null then mtype = cla.mclass_type
869 else if nexpr isa ACharExpr then
870 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Char")
871 if cla != null then mtype = cla.mclass_type
872 else if nexpr isa ABoolExpr then
873 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Bool")
874 if cla != null then mtype = cla.mclass_type
875 else if nexpr isa ASuperstringExpr then
876 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String")
877 if cla != null then mtype = cla.mclass_type
878 else if nexpr isa AStringFormExpr then
879 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String")
880 if cla != null then mtype = cla.mclass_type
881 else
882 modelbuilder.error(self, "Error: Untyped attribute {mpropdef}. Implicit typing allowed only for literals and new.")
883 end
884
885 if mtype == null then return
886 end
887 else if ntype != null then
888 if nexpr isa ANewExpr then
889 var xmtype = modelbuilder.resolve_mtype(mmodule, mclassdef, nexpr.n_type)
890 if xmtype == mtype then
891 modelbuilder.advice(ntype, "useless-type", "Warning: useless type definition")
892 end
893 end
894 end
895
896 if mtype == null then
897 modelbuilder.error(self, "Error: Untyped attribute {mpropdef}")
898 return
899 end
900
901 mpropdef.static_mtype = mtype
902
903 if mreadpropdef != null then
904 var msignature = new MSignature(new Array[MParameter], mtype)
905 mreadpropdef.msignature = msignature
906 end
907
908 var mwritepropdef = self.mwritepropdef
909 if mwritepropdef != null then
910 var name: String
911 name = n_id2.text
912 var mparameter = new MParameter(name, mtype, false)
913 var msignature = new MSignature([mparameter], null)
914 mwritepropdef.msignature = msignature
915 end
916
917 var mlazypropdef = self.mlazypropdef
918 if mlazypropdef != null then
919 mlazypropdef.static_mtype = modelbuilder.model.get_mclasses_by_name("Bool").first.mclass_type
920 end
921 end
922
923 redef fun check_signature(modelbuilder)
924 do
925 var mpropdef = self.mpropdef
926 if mpropdef == null then return # Error thus skiped
927 var mclassdef = mpropdef.mclassdef
928 var mmodule = mclassdef.mmodule
929 var ntype = self.n_type
930 var mtype = self.mpropdef.static_mtype
931 if mtype == null then return # Error thus skiped
932
933 # Lookup for signature in the precursor
934 # FIXME all precursors should be considered
935 if not mpropdef.is_intro then
936 var precursor_type = mpropdef.mproperty.intro.static_mtype
937 if precursor_type == null then return
938
939 if mtype != precursor_type then
940 modelbuilder.error(ntype.as(not null), "Redef Error: Wrong static type. found {mtype}, expected {precursor_type}.")
941 return
942 end
943 end
944
945 # Check getter and setter
946 var meth = self.mreadpropdef
947 if meth != null then
948 self.check_method_signature(modelbuilder, meth)
949 var node: nullable ANode = ntype
950 if node == null then node = self
951 modelbuilder.check_visibility(node, mtype, meth)
952 end
953 meth = self.mwritepropdef
954 if meth != null then
955 self.check_method_signature(modelbuilder, meth)
956 var node: nullable ANode = ntype
957 if node == null then node = self
958 modelbuilder.check_visibility(node, mtype, meth)
959 end
960 end
961
962 private fun check_method_signature(modelbuilder: ModelBuilder, mpropdef: MMethodDef)
963 do
964 var mclassdef = mpropdef.mclassdef
965 var mmodule = mclassdef.mmodule
966 var nsig = self.n_type
967 var mysignature = mpropdef.msignature
968 if mysignature == null then return # Error thus skiped
969
970 # Lookup for signature in the precursor
971 # FIXME all precursors should be considered
972 if not mpropdef.is_intro then
973 var msignature = mpropdef.mproperty.intro.msignature
974 if msignature == null then return
975
976 if mysignature.arity != msignature.arity then
977 var node: ANode
978 if nsig != null then node = nsig else node = self
979 modelbuilder.error(node, "Redef Error: {mysignature.arity} parameters found, {msignature.arity} expected. Signature is {mpropdef}{msignature}")
980 return
981 end
982 var precursor_ret_type = msignature.return_mtype
983 var ret_type = mysignature.return_mtype
984 if ret_type != null and precursor_ret_type == null then
985 var node: ANode
986 if nsig != null then node = nsig else node = self
987 modelbuilder.error(node, "Redef Error: {mpropdef.mproperty} is a procedure, not a function.")
988 return
989 end
990
991 if mysignature.arity > 0 then
992 # Check parameters types
993 for i in [0..mysignature.arity[ do
994 var myt = mysignature.mparameters[i].mtype
995 var prt = msignature.mparameters[i].mtype
996 if not myt.is_subtype(mmodule, mclassdef.bound_mtype, prt) or
997 not prt.is_subtype(mmodule, mclassdef.bound_mtype, myt) then
998 var node: ANode
999 if nsig != null then node = nsig else node = self
1000 modelbuilder.error(node, "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt}.")
1001 end
1002 end
1003 end
1004 if precursor_ret_type != null then
1005 if ret_type == null then
1006 # Inherit the return type
1007 ret_type = precursor_ret_type
1008 else if not ret_type.is_subtype(mmodule, mclassdef.bound_mtype, precursor_ret_type) then
1009 var node: ANode
1010 if nsig != null then node = nsig else node = self
1011 modelbuilder.error(node, "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type}.")
1012 end
1013 end
1014 end
1015 end
1016 end
1017
1018 redef class ATypePropdef
1019 redef type MPROPDEF: MVirtualTypeDef
1020
1021 redef fun build_property(modelbuilder, mclassdef)
1022 do
1023 var name = self.n_id.text
1024 var mprop = modelbuilder.try_get_mproperty_by_name(self.n_id, mclassdef, name)
1025 if mprop == null then
1026 var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility)
1027 mprop = new MVirtualTypeProp(mclassdef, name, mvisibility)
1028 for c in name.chars do if c >= 'a' and c<= 'z' then
1029 modelbuilder.warning(n_id, "bad-type-name", "Warning: lowercase in the virtual type {name}")
1030 break
1031 end
1032 if not self.check_redef_keyword(modelbuilder, mclassdef, self.n_kwredef, false, mprop) then return
1033 else
1034 if not self.check_redef_keyword(modelbuilder, mclassdef, self.n_kwredef, true, mprop) then return
1035 assert mprop isa MVirtualTypeProp
1036 check_redef_property_visibility(modelbuilder, self.n_visibility, mprop)
1037 end
1038 mclassdef.mprop2npropdef[mprop] = self
1039
1040 var mpropdef = new MVirtualTypeDef(mclassdef, mprop, self.location)
1041 self.mpropdef = mpropdef
1042 modelbuilder.mpropdef2npropdef[mpropdef] = self
1043 set_doc(mpropdef, modelbuilder)
1044
1045 var atfixed = get_single_annotation("fixed", modelbuilder)
1046 if atfixed != null then
1047 mpropdef.is_fixed = true
1048 end
1049 end
1050
1051 redef fun build_signature(modelbuilder)
1052 do
1053 var mpropdef = self.mpropdef
1054 if mpropdef == null then return # Error thus skiped
1055 var mclassdef = mpropdef.mclassdef
1056 var mmodule = mclassdef.mmodule
1057 var mtype: nullable MType = null
1058
1059 var ntype = self.n_type
1060 mtype = modelbuilder.resolve_mtype(mmodule, mclassdef, ntype)
1061 if mtype == null then return
1062
1063 mpropdef.bound = mtype
1064 # print "{mpropdef}: {mtype}"
1065 end
1066
1067 redef fun check_signature(modelbuilder)
1068 do
1069 var mpropdef = self.mpropdef
1070 if mpropdef == null then return # Error thus skiped
1071
1072 var bound = self.mpropdef.bound
1073 if bound == null then return # Error thus skiped
1074
1075 modelbuilder.check_visibility(n_type, bound, mpropdef)
1076
1077 var mclassdef = mpropdef.mclassdef
1078 var mmodule = mclassdef.mmodule
1079 var anchor = mclassdef.bound_mtype
1080
1081 # Check circularity
1082 if bound isa MVirtualType then
1083 # Slow case: progress on each resolution until: (i) we loop, or (ii) we found a non formal type
1084 var seen = [self.mpropdef.mproperty.mvirtualtype]
1085 loop
1086 if seen.has(bound) then
1087 seen.add(bound)
1088 modelbuilder.error(self, "Error: circularity of virtual type definition: {seen.join(" -> ")}")
1089 return
1090 end
1091 seen.add(bound)
1092 var next = bound.lookup_bound(mmodule, anchor)
1093 if not next isa MVirtualType then break
1094 bound = next
1095 end
1096 end
1097
1098 # Check redefinitions
1099 bound = mpropdef.bound.as(not null)
1100 for p in mpropdef.mproperty.lookup_super_definitions(mmodule, anchor) do
1101 var supbound = p.bound.as(not null)
1102 if p.is_fixed then
1103 modelbuilder.error(self, "Redef Error: Virtual type {mpropdef.mproperty} is fixed in super-class {p.mclassdef.mclass}")
1104 break
1105 end
1106 if p.mclassdef.mclass == mclassdef.mclass then
1107 # Still a warning to pass existing bad code
1108 modelbuilder.warning(n_type, "refine-type", "Redef Error: a virtual type cannot be refined.")
1109 break
1110 end
1111 if not bound.is_subtype(mmodule, anchor, supbound) then
1112 modelbuilder.error(n_type, "Redef Error: Wrong bound type. Found {bound}, expected a subtype of {supbound}, as in {p}.")
1113 break
1114 end
1115 end
1116 end
1117 end