nitunit: use `--ignore-visibility` when executon tests
[nit.git] / src / 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
22 redef class ToolContext
23 var modelize_property_phase: Phase = new ModelizePropertyPhase(self, [modelize_class_phase])
24 end
25
26 private class ModelizePropertyPhase
27 super Phase
28 redef fun process_nmodule(nmodule)
29 do
30 for nclassdef in nmodule.n_classdefs do
31 toolcontext.modelbuilder.build_properties(nclassdef)
32 end
33 end
34 end
35
36 redef class ModelBuilder
37 # Register the npropdef associated to each mpropdef
38 # FIXME: why not refine the `MPropDef` class with a nullable attribute?
39 var mpropdef2npropdef: HashMap[MPropDef, APropdef] = new HashMap[MPropDef, APropdef]
40
41 # Build the properties of `nclassdef`.
42 # REQUIRE: all superclasses are built.
43 private fun build_properties(nclassdef: AClassdef)
44 do
45 # Force building recursively
46 if nclassdef.build_properties_is_done then return
47 nclassdef.build_properties_is_done = true
48 var mclassdef = nclassdef.mclassdef.as(not null)
49 if mclassdef.in_hierarchy == null then return # Skip error
50 for superclassdef in mclassdef.in_hierarchy.direct_greaters do
51 build_properties(mclassdef2nclassdef[superclassdef])
52 end
53
54 for npropdef in nclassdef.n_propdefs do
55 npropdef.build_property(self, nclassdef)
56 end
57 for npropdef in nclassdef.n_propdefs do
58 npropdef.build_signature(self, nclassdef)
59 end
60 for npropdef in nclassdef.n_propdefs do
61 npropdef.check_signature(self, nclassdef)
62 end
63 process_default_constructors(nclassdef)
64 end
65
66 # Introduce or inherit default constructor
67 # This is the last part of `build_properties`.
68 private fun process_default_constructors(nclassdef: AClassdef)
69 do
70 var mclassdef = nclassdef.mclassdef.as(not null)
71
72 # Are we a refinement
73 if not mclassdef.is_intro then return
74
75 # Is the class forbid constructors?
76 if not mclassdef.mclass.kind.need_init then return
77
78 # Is there already a constructor defined?
79 for mpropdef in mclassdef.mpropdefs do
80 if not mpropdef isa MMethodDef then continue
81 if mpropdef.mproperty.is_init then return
82 end
83
84 if not nclassdef isa AStdClassdef then return
85
86 var mmodule = nclassdef.mclassdef.mmodule
87 # Do we inherit for a constructor?
88 var combine = new Array[MMethod]
89 var inhc: nullable MClass = null
90 for st in mclassdef.supertypes do
91 var c = st.mclass
92 if not c.kind.need_init then continue
93 st = st.anchor_to(mmodule, nclassdef.mclassdef.bound_mtype)
94 var candidate = self.try_get_mproperty_by_name2(nclassdef, mmodule, st, "init").as(nullable MMethod)
95 if candidate != null and candidate.intro.msignature.arity == 0 then
96 combine.add(candidate)
97 continue
98 end
99 var inhc2 = c.inherit_init_from
100 if inhc2 == null then inhc2 = c
101 if inhc2 == inhc then continue
102 if inhc != null then
103 self.error(nclassdef, "Error: Cannot provide a defaut constructor: conflict for {inhc} and {c}")
104 else
105 inhc = inhc2
106 end
107 end
108 if combine.is_empty and inhc != null then
109 # TODO: actively inherit the consturctor
110 self.toolcontext.info("{mclassdef} inherits all constructors from {inhc}", 3)
111 mclassdef.mclass.inherit_init_from = inhc
112 return
113 end
114 if not combine.is_empty and inhc != null then
115 self.error(nclassdef, "Error: Cannot provide a defaut constructor: conflict for {combine.join(", ")} and {inhc}")
116 return
117 end
118
119 if not combine.is_empty then
120 nclassdef.super_inits = combine
121 var mprop = new MMethod(mclassdef, "init", mclassdef.mclass.visibility)
122 var mpropdef = new MMethodDef(mclassdef, mprop, nclassdef.location)
123 var mparameters = new Array[MParameter]
124 var msignature = new MSignature(mparameters, null)
125 mpropdef.msignature = msignature
126 mprop.is_init = true
127 nclassdef.mfree_init = mpropdef
128 self.toolcontext.info("{mclassdef} gets a free empty constructor {mpropdef}{msignature}", 3)
129 return
130 end
131
132 # Collect undefined attributes
133 var mparameters = new Array[MParameter]
134 for npropdef in nclassdef.n_propdefs do
135 if npropdef isa AAttrPropdef and npropdef.n_expr == null then
136 if npropdef.mpropdef == null then return # Skip broken attribute
137 var paramname = npropdef.mpropdef.mproperty.name.substring_from(1)
138 var ret_type = npropdef.mpropdef.static_mtype
139 if ret_type == null then return
140 var mparameter = new MParameter(paramname, ret_type, false)
141 mparameters.add(mparameter)
142 end
143 end
144
145 var mprop = new MMethod(mclassdef, "init", mclassdef.mclass.visibility)
146 var mpropdef = new MMethodDef(mclassdef, mprop, nclassdef.location)
147 var msignature = new MSignature(mparameters, null)
148 mpropdef.msignature = msignature
149 mprop.is_init = true
150 nclassdef.mfree_init = mpropdef
151 self.toolcontext.info("{mclassdef} gets a free constructor for attributes {mpropdef}{msignature}", 3)
152 end
153 end
154
155 redef class MClass
156 # The class whose self inherit all the constructors.
157 # 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
158 var inherit_init_from: nullable MClass = null
159 end
160
161 redef class MClassDef
162 private var propdef_names = new HashSet[String]
163 end
164
165 redef class MPropDef
166 # Does the MPropDef contains a call to super or a call of a super-constructor?
167 # Subsequent phases of the frontend (esp. typing) set it if required
168 var has_supercall: Bool writable = false
169 end
170
171 redef class AClassdef
172 var build_properties_is_done: Bool = false
173 # The list of super-constructor to call at the start of the free constructor
174 # 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
175 var super_inits: nullable Collection[MMethod] = null
176
177 # The free init (implicitely constructed by the class if required)
178 var mfree_init: nullable MMethodDef = null
179
180 # What is the `APropdef` associated to a `MProperty`?
181 # Used to check multiple definition of a property.
182 var mprop2npropdef: Map[MProperty, APropdef] = new HashMap[MProperty, APropdef]
183 end
184
185 redef class Prod
186 # Join the text of all tokens
187 # Used to get the 'real name' of method definitions.
188 fun collect_text: String
189 do
190 var v = new TextCollectorVisitor
191 v.enter_visit(self)
192 assert v.text != ""
193 return v.text
194 end
195 end
196
197 private class TextCollectorVisitor
198 super Visitor
199 var text: String = ""
200 redef fun visit(n)
201 do
202 if n isa Token then text += n.text
203 n.visit_all(self)
204 end
205 end
206
207 redef class APropdef
208 # The associated main model entity
209 type MPROPDEF: MPropDef
210
211 # The associated propdef once build by a `ModelBuilder`
212 var mpropdef: nullable MPROPDEF writable
213
214 private fun build_property(modelbuilder: ModelBuilder, nclassdef: AClassdef) is abstract
215 private fun build_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef) is abstract
216 private fun check_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef) is abstract
217 private fun new_property_visibility(modelbuilder: ModelBuilder, nclassdef: AClassdef, nvisibility: nullable AVisibility): MVisibility
218 do
219 var mvisibility = public_visibility
220 if nvisibility != null then
221 mvisibility = nvisibility.mvisibility
222 if mvisibility == intrude_visibility then
223 modelbuilder.error(nvisibility, "Error: intrude is not a legal visibility for properties.")
224 mvisibility = public_visibility
225 end
226 end
227 if nclassdef.mclassdef.mclass.visibility == private_visibility then
228 if mvisibility == protected_visibility then
229 assert nvisibility != null
230 modelbuilder.error(nvisibility, "Error: The only legal visibility for properties in a private class is private.")
231 else if mvisibility == private_visibility then
232 assert nvisibility != null
233 # Not yet
234 # modelbuilder.warning(nvisibility, "Warning: private is unrequired since the only legal visibility for properties in a private class is private.")
235 end
236 mvisibility = private_visibility
237 end
238 return mvisibility
239 end
240
241 private fun set_doc(mpropdef: MPropDef)
242 do
243 var ndoc = self.n_doc
244 if ndoc != null then mpropdef.mdoc = ndoc.to_mdoc
245 end
246
247 private fun check_redef_property_visibility(modelbuilder: ModelBuilder, nclassdef: AClassdef, nvisibility: nullable AVisibility, mprop: MProperty)
248 do
249 if nvisibility == null then return
250 var mvisibility = nvisibility.mvisibility
251 if mvisibility != mprop.visibility and mvisibility != public_visibility then
252 modelbuilder.error(nvisibility, "Error: redefinition changed the visibility from a {mprop.visibility} to a {mvisibility}")
253 end
254 end
255
256 private fun check_redef_keyword(modelbuilder: ModelBuilder, nclassdef: AClassdef, kwredef: nullable Token, need_redef: Bool, mprop: MProperty): Bool
257 do
258 if nclassdef.mprop2npropdef.has_key(mprop) then
259 modelbuilder.error(self, "Error: A property {mprop} is already defined in class {nclassdef.mclassdef.mclass}.")
260 return false
261 end
262 if kwredef == null then
263 if need_redef then
264 modelbuilder.error(self, "Redef error: {nclassdef.mclassdef.mclass}::{mprop.name} is an inherited property. To redefine it, add the redef keyword.")
265 return false
266 end
267 else
268 if not need_redef then
269 modelbuilder.error(self, "Error: No property {nclassdef.mclassdef.mclass}::{mprop.name} is inherited. Remove the redef keyword to define a new property.")
270 return false
271 end
272 end
273 return true
274 end
275
276 end
277
278 redef class ASignature
279 # Is the model builder has correctly visited the signature
280 var is_visited = false
281 # Names of parameters from the AST
282 # REQUIRE: is_visited
283 var param_names = new Array[String]
284 # Types of parameters from the AST
285 # REQUIRE: is_visited
286 var param_types = new Array[MType]
287 # Rank of the vararg (of -1 if none)
288 # REQUIRE: is_visited
289 var vararg_rank: Int = -1
290 # Return type
291 var ret_type: nullable MType = null
292
293 # Visit and fill information about a signature
294 private fun visit_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef): Bool
295 do
296 var param_names = self.param_names
297 var param_types = self.param_types
298 for np in self.n_params do
299 param_names.add(np.n_id.text)
300 var ntype = np.n_type
301 if ntype != null then
302 var mtype = modelbuilder.resolve_mtype(nclassdef, ntype)
303 if mtype == null then return false # Skip error
304 for i in [0..param_names.length-param_types.length[ do
305 param_types.add(mtype)
306 end
307 if np.n_dotdotdot != null then
308 if self.vararg_rank != -1 then
309 modelbuilder.error(np, "Error: {param_names[self.vararg_rank]} is already a vararg")
310 return false
311 else
312 self.vararg_rank = param_names.length - 1
313 end
314 end
315 end
316 end
317 var ntype = self.n_type
318 if ntype != null then
319 self.ret_type = modelbuilder.resolve_mtype(nclassdef, ntype)
320 if self.ret_type == null then return false # Skip errir
321 end
322
323 self.is_visited = true
324 return true
325 end
326
327 # Build a visited signature
328 fun build_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef): nullable MSignature
329 do
330 if param_names.length != param_types.length then
331 # Some parameters are typed, other parameters are not typed.
332 modelbuilder.error(self.n_params[param_types.length], "Error: Untyped parameter `{param_names[param_types.length]}'.")
333 return null
334 end
335
336 var mparameters = new Array[MParameter]
337 for i in [0..param_names.length[ do
338 var mparameter = new MParameter(param_names[i], param_types[i], i == vararg_rank)
339 self.n_params[i].mparameter = mparameter
340 mparameters.add(mparameter)
341 end
342
343 var msignature = new MSignature(mparameters, ret_type)
344 return msignature
345 end
346 end
347
348 redef class AParam
349 # The associated mparameter if any
350 var mparameter: nullable MParameter = null
351 end
352
353 redef class AMethPropdef
354 redef type MPROPDEF: MMethodDef
355
356 redef fun build_property(modelbuilder, nclassdef)
357 do
358 var is_init = self isa AInitPropdef
359 var mclassdef = nclassdef.mclassdef.as(not null)
360 var name: String
361 var amethodid = self.n_methid
362 var name_node: ANode
363 if amethodid == null then
364 if self isa AMainMethPropdef then
365 name = "main"
366 name_node = self
367 else if self isa AConcreteInitPropdef then
368 name = "init"
369 name_node = self.n_kwinit
370 else if self isa AExternInitPropdef then
371 name = "init"
372 name_node = self.n_kwnew
373 else
374 abort
375 end
376 else if amethodid isa AIdMethid then
377 name = amethodid.n_id.text
378 name_node = amethodid
379 else
380 # operator, bracket or assign
381 name = amethodid.collect_text
382 name_node = amethodid
383
384 if name == "-" and self.n_signature.n_params.length == 0 then
385 name = "unary -"
386 end
387 end
388
389 var mprop: nullable MMethod = null
390 if not is_init or n_kwredef != null then mprop = modelbuilder.try_get_mproperty_by_name(name_node, mclassdef, name).as(nullable MMethod)
391 if mprop == null then
392 var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility)
393 mprop = new MMethod(mclassdef, name, mvisibility)
394 mprop.is_init = is_init
395 mprop.is_new = self isa AExternInitPropdef
396 if not self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, false, mprop) then return
397 else
398 if n_kwredef == null then
399 if self isa AMainMethPropdef then
400 # no warning
401 else
402 if not self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, true, mprop) then return
403 end
404 end
405 check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mprop)
406 end
407 nclassdef.mprop2npropdef[mprop] = self
408
409 var mpropdef = new MMethodDef(mclassdef, mprop, self.location)
410
411 if mclassdef.propdef_names.has(mprop.name) then
412 var loc: nullable Location = null
413 for i in mclassdef.mpropdefs do
414 if i.mproperty.name == mprop.name then
415 loc = i.location
416 break
417 end
418 end
419 if loc == null then abort
420 modelbuilder.error(self, "Error: a property {mprop} is already defined in class {mclassdef.mclass} at {loc}")
421 end
422
423 mclassdef.propdef_names.add(mpropdef.mproperty.name)
424
425 set_doc(mpropdef)
426
427 self.mpropdef = mpropdef
428 modelbuilder.mpropdef2npropdef[mpropdef] = self
429 if mpropdef.is_intro then
430 modelbuilder.toolcontext.info("{mpropdef} introduces new method {mprop.full_name}", 3)
431 else
432 modelbuilder.toolcontext.info("{mpropdef} redefines method {mprop.full_name}", 3)
433 end
434 end
435
436 redef fun build_signature(modelbuilder, nclassdef)
437 do
438 var mpropdef = self.mpropdef
439 if mpropdef == null then return # Error thus skiped
440 var mmodule = mpropdef.mclassdef.mmodule
441 var nsig = self.n_signature
442
443 # Retrieve info from the signature AST
444 var param_names = new Array[String] # Names of parameters from the AST
445 var param_types = new Array[MType] # Types of parameters from the AST
446 var vararg_rank = -1
447 var ret_type: nullable MType = null # Return type from the AST
448 if nsig != null then
449 if not nsig.visit_signature(modelbuilder, nclassdef) then return
450 param_names = nsig.param_names
451 param_types = nsig.param_types
452 vararg_rank = nsig.vararg_rank
453 ret_type = nsig.ret_type
454 end
455
456 # Look for some signature to inherit
457 # FIXME: do not inherit from the intro, but from the most specific
458 var msignature: nullable MSignature = null
459 if not mpropdef.is_intro then
460 msignature = mpropdef.mproperty.intro.msignature
461 if msignature == null then return # Skip error
462
463 # Check inherited signature arity
464 if param_names.length != msignature.arity then
465 var node: ANode
466 if nsig != null then node = nsig else node = self
467 modelbuilder.error(node, "Redef error: {mpropdef} redefines {mpropdef.mproperty.intro} with {param_names.length} parameter(s), {msignature.arity} expected. Signature is {mpropdef}{msignature}")
468 return
469 end
470 else if mpropdef.mproperty.is_init then
471 # FIXME UGLY: inherit signature from a super-constructor
472 for msupertype in nclassdef.mclassdef.supertypes do
473 msupertype = msupertype.anchor_to(mmodule, nclassdef.mclassdef.bound_mtype)
474 var candidate = modelbuilder.try_get_mproperty_by_name2(self, mmodule, msupertype, mpropdef.mproperty.name)
475 if candidate != null then
476 if msignature == null then
477 msignature = candidate.intro.as(MMethodDef).msignature
478 end
479 end
480 end
481 end
482
483
484 # Inherit the signature
485 if msignature != null and param_names.length != param_types.length and param_names.length == msignature.arity and param_types.length == 0 then
486 # Parameters are untyped, thus inherit them
487 param_types = new Array[MType]
488 for mparameter in msignature.mparameters do
489 param_types.add(mparameter.mtype)
490 end
491 vararg_rank = msignature.vararg_rank
492 end
493 if msignature != null and ret_type == null then
494 ret_type = msignature.return_mtype
495 end
496
497 if param_names.length != param_types.length then
498 # Some parameters are typed, other parameters are not typed.
499 modelbuilder.error(nsig.n_params[param_types.length], "Error: Untyped parameter `{param_names[param_types.length]}'.")
500 return
501 end
502
503 var mparameters = new Array[MParameter]
504 for i in [0..param_names.length[ do
505 var mparameter = new MParameter(param_names[i], param_types[i], i == vararg_rank)
506 if nsig != null then nsig.n_params[i].mparameter = mparameter
507 mparameters.add(mparameter)
508 end
509
510 msignature = new MSignature(mparameters, ret_type)
511 mpropdef.msignature = msignature
512 mpropdef.is_abstract = self isa ADeferredMethPropdef
513 end
514
515 redef fun check_signature(modelbuilder, nclassdef)
516 do
517 var mpropdef = self.mpropdef
518 if mpropdef == null then return # Error thus skiped
519 var mmodule = mpropdef.mclassdef.mmodule
520 var nsig = self.n_signature
521 var mysignature = self.mpropdef.msignature
522 if mysignature == null then return # Error thus skiped
523
524 # Lookup for signature in the precursor
525 # FIXME all precursors should be considered
526 if not mpropdef.is_intro then
527 var msignature = mpropdef.mproperty.intro.msignature
528 if msignature == null then return
529
530 var precursor_ret_type = msignature.return_mtype
531 var ret_type = mysignature.return_mtype
532 if ret_type != null and precursor_ret_type == null then
533 modelbuilder.error(nsig.n_type.as(not null), "Redef Error: {mpropdef.mproperty} is a procedure, not a function.")
534 return
535 end
536
537 if mysignature.arity > 0 then
538 # Check parameters types
539 for i in [0..mysignature.arity[ do
540 var myt = mysignature.mparameters[i].mtype
541 var prt = msignature.mparameters[i].mtype
542 if not myt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, prt) or
543 not prt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, myt) then
544 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}.")
545 end
546 end
547 end
548 if precursor_ret_type != null then
549 if ret_type == null then
550 # Inherit the return type
551 ret_type = precursor_ret_type
552 else if not ret_type.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, precursor_ret_type) then
553 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}.")
554 end
555 end
556 end
557 end
558 end
559
560 redef class AAttrPropdef
561 redef type MPROPDEF: MAttributeDef
562
563 # The associated getter (read accessor) if any
564 var mreadpropdef: nullable MMethodDef writable
565 # The associated setter (write accessor) if any
566 var mwritepropdef: nullable MMethodDef writable
567 redef fun build_property(modelbuilder, nclassdef)
568 do
569 var mclassdef = nclassdef.mclassdef.as(not null)
570 var mclass = mclassdef.mclass
571
572 var name: String
573 if self.n_id != null then
574 name = self.n_id.text
575 else
576 name = self.n_id2.text
577 end
578
579 if mclass.kind == interface_kind or mclassdef.mclass.kind == enum_kind then
580 modelbuilder.error(self, "Error: Attempt to define attribute {name} in the interface {mclass}.")
581 else if mclass.kind == enum_kind then
582 modelbuilder.error(self, "Error: Attempt to define attribute {name} in the enum class {mclass}.")
583 else if mclass.kind == extern_kind then
584 modelbuilder.error(self, "Error: Attempt to define attribute {name} in the extern class {mclass}.")
585 end
586
587 var nid = self.n_id
588 if nid != null then
589 # Old attribute style
590 var mprop = modelbuilder.try_get_mproperty_by_name(nid, mclassdef, name)
591 if mprop == null then
592 var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility)
593 mprop = new MAttribute(mclassdef, name, mvisibility)
594 if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, false, mprop) then return
595 else
596 assert mprop isa MAttribute
597 check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mprop)
598 if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, true, mprop) then return
599 end
600 nclassdef.mprop2npropdef[mprop] = self
601
602 var mpropdef = new MAttributeDef(mclassdef, mprop, self.location)
603 self.mpropdef = mpropdef
604 modelbuilder.mpropdef2npropdef[mpropdef] = self
605 set_doc(mpropdef)
606
607 var nreadable = self.n_readable
608 if nreadable != null then
609 var readname = name.substring_from(1)
610 var mreadprop = modelbuilder.try_get_mproperty_by_name(nid, mclassdef, readname).as(nullable MMethod)
611 if mreadprop == null then
612 var mvisibility = new_property_visibility(modelbuilder, nclassdef, nreadable.n_visibility)
613 mreadprop = new MMethod(mclassdef, readname, mvisibility)
614 if not self.check_redef_keyword(modelbuilder, nclassdef, nreadable.n_kwredef, false, mreadprop) then return
615 else
616 if not self.check_redef_keyword(modelbuilder, nclassdef, nreadable.n_kwredef, true, mreadprop) then return
617 check_redef_property_visibility(modelbuilder, nclassdef, nreadable.n_visibility, mreadprop)
618 end
619 nclassdef.mprop2npropdef[mreadprop] = self
620
621 var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location)
622 self.mreadpropdef = mreadpropdef
623 modelbuilder.mpropdef2npropdef[mreadpropdef] = self
624 mreadpropdef.mdoc = mpropdef.mdoc
625 end
626
627 var nwritable = self.n_writable
628 if nwritable != null then
629 var writename = name.substring_from(1) + "="
630 var mwriteprop = modelbuilder.try_get_mproperty_by_name(nid, mclassdef, writename).as(nullable MMethod)
631 if mwriteprop == null then
632 var mvisibility = new_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility)
633 mwriteprop = new MMethod(mclassdef, writename, mvisibility)
634 if not self.check_redef_keyword(modelbuilder, nclassdef, nwritable.n_kwredef, false, mwriteprop) then return
635 else
636 if not self.check_redef_keyword(modelbuilder, nclassdef, nwritable.n_kwredef, true, mwriteprop) then return
637 check_redef_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility, mwriteprop)
638 end
639 nclassdef.mprop2npropdef[mwriteprop] = self
640
641 var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location)
642 self.mwritepropdef = mwritepropdef
643 modelbuilder.mpropdef2npropdef[mwritepropdef] = self
644 mwritepropdef.mdoc = mpropdef.mdoc
645 end
646 else
647 # New attribute style
648 var nid2 = self.n_id2.as(not null)
649 var mprop = new MAttribute(mclassdef, "@" + name, none_visibility)
650 var mpropdef = new MAttributeDef(mclassdef, mprop, self.location)
651 self.mpropdef = mpropdef
652 modelbuilder.mpropdef2npropdef[mpropdef] = self
653 set_doc(mpropdef)
654
655 var readname = name
656 var mreadprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, readname).as(nullable MMethod)
657 if mreadprop == null then
658 var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility)
659 mreadprop = new MMethod(mclassdef, readname, mvisibility)
660 if not self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, false, mreadprop) then return
661 else
662 if not self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, true, mreadprop) then return
663 check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mreadprop)
664 end
665 nclassdef.mprop2npropdef[mreadprop] = self
666
667 var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location)
668 self.mreadpropdef = mreadpropdef
669 modelbuilder.mpropdef2npropdef[mreadpropdef] = self
670 mreadpropdef.mdoc = mpropdef.mdoc
671
672 var writename = name + "="
673 var nwritable = self.n_writable
674 var mwriteprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, writename).as(nullable MMethod)
675 var nwkwredef: nullable Token = null
676 if nwritable != null then nwkwredef = nwritable.n_kwredef
677 if mwriteprop == null then
678 var mvisibility
679 if nwritable != null then
680 mvisibility = new_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility)
681 else
682 mvisibility = private_visibility
683 end
684 mwriteprop = new MMethod(mclassdef, writename, mvisibility)
685 if not self.check_redef_keyword(modelbuilder, nclassdef, nwkwredef, false, mwriteprop) then return
686 else
687 if not self.check_redef_keyword(modelbuilder, nclassdef, nwkwredef, true, mwriteprop) then return
688 if nwritable != null then
689 check_redef_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility, mwriteprop)
690 end
691 end
692 nclassdef.mprop2npropdef[mwriteprop] = self
693
694 var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location)
695 self.mwritepropdef = mwritepropdef
696 modelbuilder.mpropdef2npropdef[mwritepropdef] = self
697 mwritepropdef.mdoc = mpropdef.mdoc
698 end
699 end
700
701 redef fun build_signature(modelbuilder, nclassdef)
702 do
703 var mpropdef = self.mpropdef
704 if mpropdef == null then return # Error thus skiped
705 var mmodule = mpropdef.mclassdef.mmodule
706 var mtype: nullable MType = null
707
708 var ntype = self.n_type
709 if ntype != null then
710 mtype = modelbuilder.resolve_mtype(nclassdef, ntype)
711 if mtype == null then return
712 end
713
714 var nexpr = self.n_expr
715 if mtype == null then
716 if nexpr != null then
717 if nexpr isa ANewExpr then
718 mtype = modelbuilder.resolve_mtype(nclassdef, nexpr.n_type)
719 else if nexpr isa AIntExpr then
720 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Int")
721 if cla != null then mtype = cla.mclass_type
722 else if nexpr isa AFloatExpr then
723 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Float")
724 if cla != null then mtype = cla.mclass_type
725 else if nexpr isa ACharExpr then
726 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Char")
727 if cla != null then mtype = cla.mclass_type
728 else if nexpr isa ABoolExpr then
729 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Bool")
730 if cla != null then mtype = cla.mclass_type
731 else if nexpr isa ASuperstringExpr then
732 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String")
733 if cla != null then mtype = cla.mclass_type
734 else if nexpr isa AStringFormExpr then
735 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String")
736 if cla != null then mtype = cla.mclass_type
737 else
738 modelbuilder.error(self, "Error: Untyped attribute {mpropdef}. Implicit typing allowed only for literals and new.")
739 end
740
741 else
742 modelbuilder.error(self, "Error: Untyped attribute {mpropdef}")
743 end
744 else
745 assert ntype != null
746 if nexpr isa ANewExpr then
747 var xmtype = modelbuilder.resolve_mtype(nclassdef, nexpr.n_type)
748 if xmtype == mtype and modelbuilder.toolcontext.opt_warn.value >= 2 then
749 modelbuilder.warning(ntype, "Warning: useless type definition")
750 end
751 end
752 end
753
754 if mtype == null then return
755
756 mpropdef.static_mtype = mtype
757
758 var mreadpropdef = self.mreadpropdef
759 if mreadpropdef != null then
760 var msignature = new MSignature(new Array[MParameter], mtype)
761 mreadpropdef.msignature = msignature
762 end
763
764 var msritepropdef = self.mwritepropdef
765 if mwritepropdef != null then
766 var name: String
767 if n_id != null then
768 name = n_id.text.substring_from(1)
769 else
770 name = n_id2.text
771 end
772 var mparameter = new MParameter(name, mtype, false)
773 var msignature = new MSignature([mparameter], null)
774 mwritepropdef.msignature = msignature
775 end
776 end
777
778 redef fun check_signature(modelbuilder, nclassdef)
779 do
780 var mpropdef = self.mpropdef
781 if mpropdef == null then return # Error thus skiped
782 var mmodule = mpropdef.mclassdef.mmodule
783 var ntype = self.n_type
784 var mtype = self.mpropdef.static_mtype
785 if mtype == null then return # Error thus skiped
786
787 # Lookup for signature in the precursor
788 # FIXME all precursors should be considered
789 if not mpropdef.is_intro then
790 var precursor_type = mpropdef.mproperty.intro.static_mtype
791 if precursor_type == null then return
792
793 if mtype != precursor_type then
794 modelbuilder.error(ntype.as(not null), "Redef Error: Wrong static type. found {mtype}, expected {precursor_type}.")
795 return
796 end
797 end
798
799 # Check getter and setter
800 var meth = self.mreadpropdef
801 if meth != null then self.check_method_signature(modelbuilder, nclassdef, meth)
802 meth = self.mwritepropdef
803 if meth != null then self.check_method_signature(modelbuilder, nclassdef, meth)
804 end
805
806 private fun check_method_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef, mpropdef: MMethodDef)
807 do
808 var mmodule = mpropdef.mclassdef.mmodule
809 var nsig = self.n_type
810 var mysignature = mpropdef.msignature
811 if mysignature == null then return # Error thus skiped
812
813 # Lookup for signature in the precursor
814 # FIXME all precursors should be considered
815 if not mpropdef.is_intro then
816 var msignature = mpropdef.mproperty.intro.msignature
817 if msignature == null then return
818
819 if mysignature.arity != msignature.arity then
820 var node: ANode
821 if nsig != null then node = nsig else node = self
822 modelbuilder.error(node, "Redef Error: {mysignature.arity} parameters found, {msignature.arity} expected. Signature is {mpropdef}{msignature}")
823 return
824 end
825 var precursor_ret_type = msignature.return_mtype
826 var ret_type = mysignature.return_mtype
827 if ret_type != null and precursor_ret_type == null then
828 var node: ANode
829 if nsig != null then node = nsig else node = self
830 modelbuilder.error(node, "Redef Error: {mpropdef.mproperty} is a procedure, not a function.")
831 return
832 end
833
834 if mysignature.arity > 0 then
835 # Check parameters types
836 for i in [0..mysignature.arity[ do
837 var myt = mysignature.mparameters[i].mtype
838 var prt = msignature.mparameters[i].mtype
839 if not myt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, prt) and
840 not prt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, myt) then
841 var node: ANode
842 if nsig != null then node = nsig else node = self
843 modelbuilder.error(node, "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt}.")
844 end
845 end
846 end
847 if precursor_ret_type != null then
848 if ret_type == null then
849 # Inherit the return type
850 ret_type = precursor_ret_type
851 else if not ret_type.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, precursor_ret_type) then
852 var node: ANode
853 if nsig != null then node = nsig else node = self
854 modelbuilder.error(node, "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type}.")
855 end
856 end
857 end
858 end
859 end
860
861 redef class ATypePropdef
862 redef type MPROPDEF: MVirtualTypeDef
863
864 redef fun build_property(modelbuilder, nclassdef)
865 do
866 var mclassdef = nclassdef.mclassdef.as(not null)
867 var name = self.n_id.text
868 var mprop = modelbuilder.try_get_mproperty_by_name(self.n_id, mclassdef, name)
869 if mprop == null then
870 var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility)
871 mprop = new MVirtualTypeProp(mclassdef, name, mvisibility)
872 for c in name do if c >= 'a' and c<= 'z' then
873 modelbuilder.warning(n_id, "Warning: lowercase in the virtual type {name}")
874 break
875 end
876 if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, false, mprop) then return
877 else
878 if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, true, mprop) then return
879 assert mprop isa MVirtualTypeProp
880 check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mprop)
881 end
882 nclassdef.mprop2npropdef[mprop] = self
883
884 var mpropdef = new MVirtualTypeDef(mclassdef, mprop, self.location)
885 self.mpropdef = mpropdef
886 set_doc(mpropdef)
887 end
888
889 redef fun build_signature(modelbuilder, nclassdef)
890 do
891 var mpropdef = self.mpropdef
892 if mpropdef == null then return # Error thus skiped
893 var mmodule = mpropdef.mclassdef.mmodule
894 var mtype: nullable MType = null
895
896 var ntype = self.n_type
897 mtype = modelbuilder.resolve_mtype(nclassdef, ntype)
898 if mtype == null then return
899
900 mpropdef.bound = mtype
901 # print "{mpropdef}: {mtype}"
902 end
903
904 redef fun check_signature(modelbuilder, nclassdef)
905 do
906 var mpropdef = self.mpropdef
907 if mpropdef == null then return # Error thus skiped
908
909 var bound = self.mpropdef.bound
910
911 # Fast case: the bound is not a formal type
912 if not bound isa MVirtualType then return
913
914 var mmodule = nclassdef.mclassdef.mmodule
915 var anchor = nclassdef.mclassdef.bound_mtype
916
917 # Slow case: progress on each resolution until: (i) we loop, or (ii) we found a non formal type
918 var seen = [self.mpropdef.mproperty.mvirtualtype]
919 loop
920 if seen.has(bound) then
921 seen.add(bound)
922 modelbuilder.error(self, "Error: circularity of virtual type definition: {seen.join(" -> ")}")
923 return
924 end
925 seen.add(bound)
926 var next = bound.lookup_bound(mmodule, anchor)
927 if not next isa MVirtualType then return
928 bound = next
929 end
930 end
931 end