modelize: useless-visibility is an advice
[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 var mmodule = nclassdef.mclassdef.mmodule
86
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
97 mprop.is_init = true
98 nclassdef.mfree_init = mpropdef
99 self.toolcontext.info("{mclassdef} gets a free empty constructor {mpropdef}{msignature}", 3)
100 the_root_init_mmethod = mprop
101 return
102 end
103
104 # Is the class forbid constructors?
105 if not mclassdef.mclass.kind.need_init then return
106
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
115 else
116 # An explicit old-style init, so return
117 return
118 end
119 end
120
121 if not nclassdef isa AStdClassdef then return
122
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
127 var c = st.mclass
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)
136 continue
137 end
138 end
139 end
140 var inhc2 = c.inherit_init_from
141 if inhc2 == null then inhc2 = c
142 if inhc2 == inhc then continue
143 if inhc != null then
144 self.error(nclassdef, "Error: Cannot provide a defaut constructor: conflict for {inhc} and {c}")
145 else
146 inhc = inhc2
147 end
148 end
149
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)
158 if at != null then
159 npropdef.noinit = true
160 if npropdef.n_expr != null then
161 self.error(at, "Error: `noinit` attributes cannot have an initial value")
162 end
163 continue # Skip noinit attributes
164 end
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)
175 else
176 # Add the setter to the list
177 initializers.add(msetter.mproperty)
178 end
179 if anode == null then anode = npropdef
180 end
181 end
182 if anode == null then anode = nclassdef
183
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(", ")}")
187 return
188 end
189
190 # TODO: actively inherit the consturctor
191 self.toolcontext.info("{mclassdef} inherits all constructors from {inhc}", 3)
192 #mclassdef.mclass.inherit_init_from = inhc
193 #return
194 end
195
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}")
198 return
199 end
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)
206 return
207 end
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
213 mprop.is_init = true
214 nclassdef.mfree_init = mpropdef
215 self.toolcontext.info("{mclassdef} gets a free empty constructor {mpropdef}{msignature}", 3)
216 return
217 end
218
219 if the_root_init_mmethod == null then return
220
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`?")
225 end
226
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
235 end
236 # part 2. compare
237 for spd in spropdefs do
238 var i = 0
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(", ")})")
242 return
243 end
244 i += 1
245 end
246 end
247 end
248
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)
252 return
253 end
254
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
259 end
260
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)
267 return
268 end
269
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)
280 end
281
282 # Check the visibility of `mtype` as an element of the signature of `mpropdef`.
283 fun check_visibility(node: ANode, mtype: MType, mpropdef: MPropDef)
284 do
285 var mmodule = mpropdef.mclassdef.mmodule
286 var mproperty = mpropdef.mproperty
287
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
301 else
302 node.debug "Unexpected type {mtype}"
303 abort
304 end
305
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}`")
311 return
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}`")
314 return
315 end
316 end
317
318 # No error, try to go deeper in generic types
319 if node isa AType then
320 for a in node.n_types do
321 var t = a.mtype
322 if t == null then continue # Error, thus skipped
323 check_visibility(a, t, mpropdef)
324 end
325 else if mtype isa MGenericType then
326 for t in mtype.arguments do check_visibility(node, t, mpropdef)
327 end
328 end
329 end
330
331 redef class MClass
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
335 end
336
337 redef class MPropDef
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
341 end
342
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
348
349 # The free init (implicitely constructed by the class if required)
350 var mfree_init: nullable MMethodDef = null
351 end
352
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]
357 end
358
359 redef class Prod
360 # Join the text of all tokens
361 # Used to get the 'real name' of method definitions.
362 fun collect_text: String
363 do
364 var v = new TextCollectorVisitor
365 v.enter_visit(self)
366 assert v.text != ""
367 return v.text
368 end
369 end
370
371 private class TextCollectorVisitor
372 super Visitor
373 var text: String = ""
374 redef fun visit(n)
375 do
376 if n isa Token then text += n.text
377 n.visit_all(self)
378 end
379 end
380
381 redef class APropdef
382 # The associated main model entity
383 type MPROPDEF: MPropDef
384
385 # The associated propdef once build by a `ModelBuilder`
386 var mpropdef: nullable MPROPDEF is writable
387
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
392 do
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
399 end
400 end
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.")
408 end
409 mvisibility = private_visibility
410 end
411 return mvisibility
412 end
413
414 private fun set_doc(mpropdef: MPropDef, modelbuilder: ModelBuilder)
415 do
416 var ndoc = self.n_doc
417 if ndoc != null then
418 var mdoc = ndoc.to_mdoc
419 mpropdef.mdoc = mdoc
420 mdoc.original_mentity = mpropdef
421 end
422
423 var at_deprecated = get_single_annotation("deprecated", modelbuilder)
424 if at_deprecated != null then
425 if not mpropdef.is_intro then
426 modelbuilder.error(self, "Error: method redefinition cannot be deprecated.")
427 else
428 var info = new MDeprecationInfo
429 ndoc = at_deprecated.n_doc
430 if ndoc != null then info.mdoc = ndoc.to_mdoc
431 mpropdef.mproperty.deprecation = info
432 end
433 end
434 end
435
436 private fun check_redef_property_visibility(modelbuilder: ModelBuilder, nvisibility: nullable AVisibility, mprop: MProperty)
437 do
438 if nvisibility == null then return
439 var mvisibility = nvisibility.mvisibility
440 if mvisibility != mprop.visibility and mvisibility != public_visibility then
441 modelbuilder.error(nvisibility, "Error: redefinition changed the visibility from a {mprop.visibility} to a {mvisibility}")
442 end
443 end
444
445 private fun check_redef_keyword(modelbuilder: ModelBuilder, mclassdef: MClassDef, kwredef: nullable Token, need_redef: Bool, mprop: MProperty): Bool
446 do
447 if mclassdef.mprop2npropdef.has_key(mprop) then
448 modelbuilder.error(self, "Error: A property {mprop} is already defined in class {mclassdef.mclass} at line {mclassdef.mprop2npropdef[mprop].location.line_start}.")
449 return false
450 end
451 if mprop isa MMethod and mprop.is_toplevel != (parent isa ATopClassdef) then
452 if mprop.is_toplevel then
453 modelbuilder.error(self, "Error: {mprop} is a top level method.")
454 else
455 modelbuilder.error(self, "Error: {mprop} is not a top level method.")
456 end
457 return false
458
459 end
460 if kwredef == null then
461 if need_redef then
462 modelbuilder.error(self, "Redef error: {mclassdef.mclass}::{mprop.name} is an inherited property. To redefine it, add the redef keyword.")
463 return false
464 end
465 else
466 if not need_redef then
467 modelbuilder.error(self, "Error: No property {mclassdef.mclass}::{mprop.name} is inherited. Remove the redef keyword to define a new property.")
468 return false
469 end
470 end
471 return true
472 end
473
474 end
475
476 redef class ASignature
477 # Is the model builder has correctly visited the signature
478 var is_visited = false
479 # Names of parameters from the AST
480 # REQUIRE: is_visited
481 var param_names = new Array[String]
482 # Types of parameters from the AST
483 # REQUIRE: is_visited
484 var param_types = new Array[MType]
485 # Rank of the vararg (of -1 if none)
486 # REQUIRE: is_visited
487 var vararg_rank: Int = -1
488 # Return type
489 var ret_type: nullable MType = null
490
491 # Visit and fill information about a signature
492 private fun visit_signature(modelbuilder: ModelBuilder, mclassdef: MClassDef): Bool
493 do
494 var mmodule = mclassdef.mmodule
495 var param_names = self.param_names
496 var param_types = self.param_types
497 for np in self.n_params do
498 param_names.add(np.n_id.text)
499 var ntype = np.n_type
500 if ntype != null then
501 var mtype = modelbuilder.resolve_mtype(mmodule, mclassdef, ntype)
502 if mtype == null then return false # Skip error
503 for i in [0..param_names.length-param_types.length[ do
504 param_types.add(mtype)
505 end
506 if np.n_dotdotdot != null then
507 if self.vararg_rank != -1 then
508 modelbuilder.error(np, "Error: {param_names[self.vararg_rank]} is already a vararg")
509 return false
510 else
511 self.vararg_rank = param_names.length - 1
512 end
513 end
514 end
515 end
516 var ntype = self.n_type
517 if ntype != null then
518 self.ret_type = modelbuilder.resolve_mtype(mmodule, mclassdef, ntype)
519 if self.ret_type == null then return false # Skip errir
520 end
521
522 self.is_visited = true
523 return true
524 end
525
526 # Build a visited signature
527 fun build_signature(modelbuilder: ModelBuilder): nullable MSignature
528 do
529 if param_names.length != param_types.length then
530 # Some parameters are typed, other parameters are not typed.
531 modelbuilder.error(self.n_params[param_types.length], "Error: Untyped parameter `{param_names[param_types.length]}'.")
532 return null
533 end
534
535 var mparameters = new Array[MParameter]
536 for i in [0..param_names.length[ do
537 var mparameter = new MParameter(param_names[i], param_types[i], i == vararg_rank)
538 self.n_params[i].mparameter = mparameter
539 mparameters.add(mparameter)
540 end
541
542 var msignature = new MSignature(mparameters, ret_type)
543 return msignature
544 end
545 end
546
547 redef class AParam
548 # The associated mparameter if any
549 var mparameter: nullable MParameter = null
550 end
551
552 redef class AMethPropdef
553 redef type MPROPDEF: MMethodDef
554
555
556 # Can self be used as a root init?
557 private fun look_like_a_root_init(modelbuilder: ModelBuilder): Bool
558 do
559 # Need the `init` keyword
560 if n_kwinit == null then return false
561 # Need to by anonymous
562 if self.n_methid != null then return false
563 # No parameters
564 if self.n_signature.n_params.length > 0 then return false
565 # Cannot be private or something
566 if not self.n_visibility isa APublicVisibility then return false
567 # No annotation on itself
568 if get_single_annotation("old_style_init", modelbuilder) != null then return false
569 # Nor on its module
570 var amod = self.parent.parent.as(AModule)
571 var amoddecl = amod.n_moduledecl
572 if amoddecl != null then
573 var old = amoddecl.get_single_annotation("old_style_init", modelbuilder)
574 if old != null then return false
575 end
576
577 return true
578 end
579
580 redef fun build_property(modelbuilder, mclassdef)
581 do
582 var n_kwinit = n_kwinit
583 var n_kwnew = n_kwnew
584 var is_init = n_kwinit != null or n_kwnew != null
585 var name: String
586 var amethodid = self.n_methid
587 var name_node: ANode
588 if amethodid == null then
589 if not is_init then
590 name = "main"
591 name_node = self
592 else if n_kwinit != null then
593 name = "init"
594 name_node = n_kwinit
595 else if n_kwnew != null then
596 name = "init"
597 name_node = n_kwnew
598 else
599 abort
600 end
601 else if amethodid isa AIdMethid then
602 name = amethodid.n_id.text
603 name_node = amethodid
604 else
605 # operator, bracket or assign
606 name = amethodid.collect_text
607 name_node = amethodid
608
609 if name == "-" and self.n_signature.n_params.length == 0 then
610 name = "unary -"
611 end
612 end
613
614 var mprop: nullable MMethod = null
615 if not is_init or n_kwredef != null then mprop = modelbuilder.try_get_mproperty_by_name(name_node, mclassdef, name).as(nullable MMethod)
616 if mprop == null and look_like_a_root_init(modelbuilder) then
617 mprop = modelbuilder.the_root_init_mmethod
618 end
619 if mprop == null then
620 var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility)
621 mprop = new MMethod(mclassdef, name, mvisibility)
622 if look_like_a_root_init(modelbuilder) and modelbuilder.the_root_init_mmethod == null then
623 modelbuilder.the_root_init_mmethod = mprop
624 mprop.is_root_init = true
625 end
626 mprop.is_init = is_init
627 mprop.is_new = n_kwnew != null
628 if parent isa ATopClassdef then mprop.is_toplevel = true
629 if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mprop) then return
630 else
631 if not mprop.is_root_init and not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, not self isa AMainMethPropdef, mprop) then return
632 check_redef_property_visibility(modelbuilder, self.n_visibility, mprop)
633 end
634 mclassdef.mprop2npropdef[mprop] = self
635
636 var mpropdef = new MMethodDef(mclassdef, mprop, self.location)
637
638 set_doc(mpropdef, modelbuilder)
639
640 self.mpropdef = mpropdef
641 modelbuilder.mpropdef2npropdef[mpropdef] = self
642 if mpropdef.is_intro then
643 modelbuilder.toolcontext.info("{mpropdef} introduces new method {mprop.full_name}", 3)
644 else
645 modelbuilder.toolcontext.info("{mpropdef} redefines method {mprop.full_name}", 3)
646 end
647 end
648
649 redef fun build_signature(modelbuilder)
650 do
651 var mpropdef = self.mpropdef
652 if mpropdef == null then return # Error thus skiped
653 var mclassdef = mpropdef.mclassdef
654 var mmodule = mclassdef.mmodule
655 var nsig = self.n_signature
656
657 # Retrieve info from the signature AST
658 var param_names = new Array[String] # Names of parameters from the AST
659 var param_types = new Array[MType] # Types of parameters from the AST
660 var vararg_rank = -1
661 var ret_type: nullable MType = null # Return type from the AST
662 if nsig != null then
663 if not nsig.visit_signature(modelbuilder, mclassdef) then return
664 param_names = nsig.param_names
665 param_types = nsig.param_types
666 vararg_rank = nsig.vararg_rank
667 ret_type = nsig.ret_type
668 end
669
670 # Look for some signature to inherit
671 # FIXME: do not inherit from the intro, but from the most specific
672 var msignature: nullable MSignature = null
673 if not mpropdef.is_intro then
674 msignature = mpropdef.mproperty.intro.msignature
675 if msignature == null then return # Skip error
676
677 # Check inherited signature arity
678 if param_names.length != msignature.arity then
679 var node: ANode
680 if nsig != null then node = nsig else node = self
681 modelbuilder.error(node, "Redef error: {mpropdef} redefines {mpropdef.mproperty.intro} with {param_names.length} parameter(s), {msignature.arity} expected. Signature is {mpropdef}{msignature}")
682 return
683 end
684 else if mpropdef.mproperty.is_init then
685 # FIXME UGLY: inherit signature from a super-constructor
686 for msupertype in mclassdef.supertypes do
687 msupertype = msupertype.anchor_to(mmodule, mclassdef.bound_mtype)
688 var candidate = modelbuilder.try_get_mproperty_by_name2(self, mmodule, msupertype, mpropdef.mproperty.name)
689 if candidate != null then
690 if msignature == null then
691 msignature = candidate.intro.as(MMethodDef).msignature
692 end
693 end
694 end
695 end
696
697
698 # Inherit the signature
699 if msignature != null and param_names.length != param_types.length and param_names.length == msignature.arity and param_types.length == 0 then
700 # Parameters are untyped, thus inherit them
701 param_types = new Array[MType]
702 for mparameter in msignature.mparameters do
703 param_types.add(mparameter.mtype)
704 end
705 vararg_rank = msignature.vararg_rank
706 end
707 if msignature != null and ret_type == null then
708 ret_type = msignature.return_mtype
709 end
710
711 if param_names.length != param_types.length then
712 # Some parameters are typed, other parameters are not typed.
713 modelbuilder.error(nsig.n_params[param_types.length], "Error: Untyped parameter `{param_names[param_types.length]}'.")
714 return
715 end
716
717 var mparameters = new Array[MParameter]
718 for i in [0..param_names.length[ do
719 var mparameter = new MParameter(param_names[i], param_types[i], i == vararg_rank)
720 if nsig != null then nsig.n_params[i].mparameter = mparameter
721 mparameters.add(mparameter)
722 end
723
724 msignature = new MSignature(mparameters, ret_type)
725 mpropdef.msignature = msignature
726 mpropdef.is_abstract = self isa ADeferredMethPropdef or self.get_single_annotation("abstract", modelbuilder) != null
727 mpropdef.is_intern = self isa AInternMethPropdef or self.get_single_annotation("intern", modelbuilder) != null
728 mpropdef.is_extern = self isa AExternPropdef or self.n_extern_code_block != null or self.get_single_annotation("extern", modelbuilder) != null
729 end
730
731 redef fun check_signature(modelbuilder)
732 do
733 var mpropdef = self.mpropdef
734 if mpropdef == null then return # Error thus skiped
735 var mclassdef = mpropdef.mclassdef
736 var mmodule = mclassdef.mmodule
737 var nsig = self.n_signature
738 var mysignature = self.mpropdef.msignature
739 if mysignature == null then return # Error thus skiped
740
741 # Lookup for signature in the precursor
742 # FIXME all precursors should be considered
743 if not mpropdef.is_intro then
744 var msignature = mpropdef.mproperty.intro.msignature
745 if msignature == null then return
746
747 var precursor_ret_type = msignature.return_mtype
748 var ret_type = mysignature.return_mtype
749 if ret_type != null and precursor_ret_type == null then
750 modelbuilder.error(nsig.n_type.as(not null), "Redef Error: {mpropdef.mproperty} is a procedure, not a function.")
751 return
752 end
753
754 if mysignature.arity > 0 then
755 # Check parameters types
756 for i in [0..mysignature.arity[ do
757 var myt = mysignature.mparameters[i].mtype
758 var prt = msignature.mparameters[i].mtype
759 if not myt.is_subtype(mmodule, mclassdef.bound_mtype, prt) or
760 not prt.is_subtype(mmodule, mclassdef.bound_mtype, myt) then
761 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}.")
762 end
763 end
764 end
765 if precursor_ret_type != null then
766 if ret_type == null then
767 # Inherit the return type
768 ret_type = precursor_ret_type
769 else if not ret_type.is_subtype(mmodule, mclassdef.bound_mtype, precursor_ret_type) then
770 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}.")
771 end
772 end
773 end
774
775 if mysignature.arity > 0 then
776 # Check parameters visibility
777 for i in [0..mysignature.arity[ do
778 var nt = nsig.n_params[i].n_type
779 if nt != null then modelbuilder.check_visibility(nt, nt.mtype.as(not null), mpropdef)
780 end
781 var nt = nsig.n_type
782 if nt != null then modelbuilder.check_visibility(nt, nt.mtype.as(not null), mpropdef)
783 end
784 end
785 end
786
787 redef class AAttrPropdef
788 redef type MPROPDEF: MAttributeDef
789
790 # Is the node tagged `noinit`?
791 var noinit = false
792
793 # Is the node taggeg lazy?
794 var is_lazy = false
795
796 # The guard associated to a lasy attribute.
797 # Because some engines does not have a working `isset`,
798 # this additionnal attribute is used to guard the lazy initialization.
799 # TODO: to remove once isset is correctly implemented
800 var mlazypropdef: nullable MAttributeDef
801
802 # The associated getter (read accessor) if any
803 var mreadpropdef: nullable MMethodDef is writable
804 # The associated setter (write accessor) if any
805 var mwritepropdef: nullable MMethodDef is writable
806
807 redef fun build_property(modelbuilder, mclassdef)
808 do
809 var mclass = mclassdef.mclass
810
811 var name: String
812 if self.n_id != null then
813 name = self.n_id.text
814 else
815 name = self.n_id2.text
816 end
817
818 if mclass.kind == interface_kind or mclassdef.mclass.kind == enum_kind then
819 modelbuilder.error(self, "Error: Attempt to define attribute {name} in the interface {mclass}.")
820 else if mclass.kind == enum_kind then
821 modelbuilder.error(self, "Error: Attempt to define attribute {name} in the enum class {mclass}.")
822 else if mclass.kind == extern_kind then
823 modelbuilder.error(self, "Error: Attempt to define attribute {name} in the extern class {mclass}.")
824 end
825
826 var nid = self.n_id
827 if nid != null then
828 # Old attribute style
829 modelbuilder.error(nid, "Error: old-style attribute no more supported")
830 else
831 # New attribute style
832 var nid2 = self.n_id2.as(not null)
833 var mprop = new MAttribute(mclassdef, "_" + name, private_visibility)
834 var mpropdef = new MAttributeDef(mclassdef, mprop, self.location)
835 self.mpropdef = mpropdef
836 modelbuilder.mpropdef2npropdef[mpropdef] = self
837 set_doc(mpropdef, modelbuilder)
838
839 var readname = name
840 var mreadprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, readname).as(nullable MMethod)
841 if mreadprop == null then
842 var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility)
843 mreadprop = new MMethod(mclassdef, readname, mvisibility)
844 if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mreadprop) then return
845 mreadprop.deprecation = mprop.deprecation
846 else
847 if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, true, mreadprop) then return
848 check_redef_property_visibility(modelbuilder, self.n_visibility, mreadprop)
849 end
850 mclassdef.mprop2npropdef[mreadprop] = self
851
852 var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location)
853 self.mreadpropdef = mreadpropdef
854 modelbuilder.mpropdef2npropdef[mreadpropdef] = self
855 mreadpropdef.mdoc = mpropdef.mdoc
856
857 var atlazy = self.get_single_annotation("lazy", modelbuilder)
858 if atlazy != null then
859 if n_expr == null then
860 modelbuilder.error(atlazy, "Error: a lazy attribute needs a value")
861 end
862 is_lazy = true
863 var mlazyprop = new MAttribute(mclassdef, "lazy _" + name, none_visibility)
864 var mlazypropdef = new MAttributeDef(mclassdef, mlazyprop, self.location)
865 self.mlazypropdef = mlazypropdef
866 end
867
868 var atreadonly = self.get_single_annotation("readonly", modelbuilder)
869 if atreadonly != null then
870 if n_expr == null then
871 modelbuilder.error(atreadonly, "Error: a readonly attribute needs a value")
872 end
873 # No setter, so just leave
874 return
875 end
876
877 var writename = name + "="
878 var nwritable = self.n_writable
879 if nwritable != null then modelbuilder.error(nwritable, "Error: old-style setter no more supported")
880 var atwritable = self.get_single_annotation("writable", modelbuilder)
881 if atwritable != null then
882 if not atwritable.n_args.is_empty then
883 writename = atwritable.arg_as_id(modelbuilder) or else writename
884 end
885 end
886 var mwriteprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, writename).as(nullable MMethod)
887 var nwkwredef: nullable Token = null
888 if atwritable != null then nwkwredef = atwritable.n_kwredef
889 if mwriteprop == null then
890 var mvisibility
891 if atwritable != null then
892 mvisibility = new_property_visibility(modelbuilder, mclassdef, atwritable.n_visibility)
893 else
894 mvisibility = private_visibility
895 end
896 mwriteprop = new MMethod(mclassdef, writename, mvisibility)
897 if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef, false, mwriteprop) then return
898 mwriteprop.deprecation = mprop.deprecation
899 else
900 if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef or else n_kwredef, true, mwriteprop) then return
901 if atwritable != null then
902 check_redef_property_visibility(modelbuilder, atwritable.n_visibility, mwriteprop)
903 end
904 end
905 mclassdef.mprop2npropdef[mwriteprop] = self
906
907 var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location)
908 self.mwritepropdef = mwritepropdef
909 modelbuilder.mpropdef2npropdef[mwritepropdef] = self
910 mwritepropdef.mdoc = mpropdef.mdoc
911 end
912 end
913
914 redef fun build_signature(modelbuilder)
915 do
916 var mpropdef = self.mpropdef
917 if mpropdef == null then return # Error thus skiped
918 var mclassdef = mpropdef.mclassdef
919 var mmodule = mclassdef.mmodule
920 var mtype: nullable MType = null
921
922 var mreadpropdef = self.mreadpropdef
923
924 var ntype = self.n_type
925 if ntype != null then
926 mtype = modelbuilder.resolve_mtype(mmodule, mclassdef, ntype)
927 if mtype == null then return
928 end
929
930 # Inherit the type from the getter (usually an abstact getter)
931 if mtype == null and mreadpropdef != null and not mreadpropdef.is_intro then
932 var msignature = mreadpropdef.mproperty.intro.msignature
933 if msignature == null then return # Error, thus skiped
934 mtype = msignature.return_mtype
935 end
936
937 var nexpr = self.n_expr
938 if mtype == null then
939 if nexpr != null then
940 if nexpr isa ANewExpr then
941 mtype = modelbuilder.resolve_mtype(mmodule, mclassdef, nexpr.n_type)
942 else if nexpr isa AIntExpr then
943 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Int")
944 if cla != null then mtype = cla.mclass_type
945 else if nexpr isa AFloatExpr then
946 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Float")
947 if cla != null then mtype = cla.mclass_type
948 else if nexpr isa ACharExpr then
949 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Char")
950 if cla != null then mtype = cla.mclass_type
951 else if nexpr isa ABoolExpr then
952 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Bool")
953 if cla != null then mtype = cla.mclass_type
954 else if nexpr isa ASuperstringExpr then
955 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String")
956 if cla != null then mtype = cla.mclass_type
957 else if nexpr isa AStringFormExpr then
958 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String")
959 if cla != null then mtype = cla.mclass_type
960 else
961 modelbuilder.error(self, "Error: Untyped attribute {mpropdef}. Implicit typing allowed only for literals and new.")
962 end
963
964 if mtype == null then return
965 end
966 else if ntype != null then
967 if nexpr isa ANewExpr then
968 var xmtype = modelbuilder.resolve_mtype(mmodule, mclassdef, nexpr.n_type)
969 if xmtype == mtype and modelbuilder.toolcontext.opt_warn.value >= 2 then
970 modelbuilder.warning(ntype, "useless-type", "Warning: useless type definition")
971 end
972 end
973 end
974
975 if mtype == null then
976 modelbuilder.error(self, "Error: Untyped attribute {mpropdef}")
977 return
978 end
979
980 mpropdef.static_mtype = mtype
981
982 if mreadpropdef != null then
983 var msignature = new MSignature(new Array[MParameter], mtype)
984 mreadpropdef.msignature = msignature
985 end
986
987 var mwritepropdef = self.mwritepropdef
988 if mwritepropdef != null then
989 var name: String
990 if n_id != null then
991 name = n_id.text.substring_from(1)
992 else
993 name = n_id2.text
994 end
995 var mparameter = new MParameter(name, mtype, false)
996 var msignature = new MSignature([mparameter], null)
997 mwritepropdef.msignature = msignature
998 end
999
1000 var mlazypropdef = self.mlazypropdef
1001 if mlazypropdef != null then
1002 mlazypropdef.static_mtype = modelbuilder.model.get_mclasses_by_name("Bool").first.mclass_type
1003 end
1004 end
1005
1006 redef fun check_signature(modelbuilder)
1007 do
1008 var mpropdef = self.mpropdef
1009 if mpropdef == null then return # Error thus skiped
1010 var mclassdef = mpropdef.mclassdef
1011 var mmodule = mclassdef.mmodule
1012 var ntype = self.n_type
1013 var mtype = self.mpropdef.static_mtype
1014 if mtype == null then return # Error thus skiped
1015
1016 # Lookup for signature in the precursor
1017 # FIXME all precursors should be considered
1018 if not mpropdef.is_intro then
1019 var precursor_type = mpropdef.mproperty.intro.static_mtype
1020 if precursor_type == null then return
1021
1022 if mtype != precursor_type then
1023 modelbuilder.error(ntype.as(not null), "Redef Error: Wrong static type. found {mtype}, expected {precursor_type}.")
1024 return
1025 end
1026 end
1027
1028 # Check getter and setter
1029 var meth = self.mreadpropdef
1030 if meth != null then
1031 self.check_method_signature(modelbuilder, meth)
1032 var node: nullable ANode = ntype
1033 if node == null then node = self
1034 modelbuilder.check_visibility(node, mtype, meth)
1035 end
1036 meth = self.mwritepropdef
1037 if meth != null then
1038 self.check_method_signature(modelbuilder, meth)
1039 var node: nullable ANode = ntype
1040 if node == null then node = self
1041 modelbuilder.check_visibility(node, mtype, meth)
1042 end
1043 end
1044
1045 private fun check_method_signature(modelbuilder: ModelBuilder, mpropdef: MMethodDef)
1046 do
1047 var mclassdef = mpropdef.mclassdef
1048 var mmodule = mclassdef.mmodule
1049 var nsig = self.n_type
1050 var mysignature = mpropdef.msignature
1051 if mysignature == null then return # Error thus skiped
1052
1053 # Lookup for signature in the precursor
1054 # FIXME all precursors should be considered
1055 if not mpropdef.is_intro then
1056 var msignature = mpropdef.mproperty.intro.msignature
1057 if msignature == null then return
1058
1059 if mysignature.arity != msignature.arity then
1060 var node: ANode
1061 if nsig != null then node = nsig else node = self
1062 modelbuilder.error(node, "Redef Error: {mysignature.arity} parameters found, {msignature.arity} expected. Signature is {mpropdef}{msignature}")
1063 return
1064 end
1065 var precursor_ret_type = msignature.return_mtype
1066 var ret_type = mysignature.return_mtype
1067 if ret_type != null and precursor_ret_type == null then
1068 var node: ANode
1069 if nsig != null then node = nsig else node = self
1070 modelbuilder.error(node, "Redef Error: {mpropdef.mproperty} is a procedure, not a function.")
1071 return
1072 end
1073
1074 if mysignature.arity > 0 then
1075 # Check parameters types
1076 for i in [0..mysignature.arity[ do
1077 var myt = mysignature.mparameters[i].mtype
1078 var prt = msignature.mparameters[i].mtype
1079 if not myt.is_subtype(mmodule, mclassdef.bound_mtype, prt) or
1080 not prt.is_subtype(mmodule, mclassdef.bound_mtype, myt) then
1081 var node: ANode
1082 if nsig != null then node = nsig else node = self
1083 modelbuilder.error(node, "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt}.")
1084 end
1085 end
1086 end
1087 if precursor_ret_type != null then
1088 if ret_type == null then
1089 # Inherit the return type
1090 ret_type = precursor_ret_type
1091 else if not ret_type.is_subtype(mmodule, mclassdef.bound_mtype, precursor_ret_type) then
1092 var node: ANode
1093 if nsig != null then node = nsig else node = self
1094 modelbuilder.error(node, "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type}.")
1095 end
1096 end
1097 end
1098 end
1099 end
1100
1101 redef class ATypePropdef
1102 redef type MPROPDEF: MVirtualTypeDef
1103
1104 redef fun build_property(modelbuilder, mclassdef)
1105 do
1106 var name = self.n_id.text
1107 var mprop = modelbuilder.try_get_mproperty_by_name(self.n_id, mclassdef, name)
1108 if mprop == null then
1109 var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility)
1110 mprop = new MVirtualTypeProp(mclassdef, name, mvisibility)
1111 for c in name.chars do if c >= 'a' and c<= 'z' then
1112 modelbuilder.warning(n_id, "bad-type-name", "Warning: lowercase in the virtual type {name}")
1113 break
1114 end
1115 if not self.check_redef_keyword(modelbuilder, mclassdef, self.n_kwredef, false, mprop) then return
1116 else
1117 if not self.check_redef_keyword(modelbuilder, mclassdef, self.n_kwredef, true, mprop) then return
1118 assert mprop isa MVirtualTypeProp
1119 check_redef_property_visibility(modelbuilder, self.n_visibility, mprop)
1120 end
1121 mclassdef.mprop2npropdef[mprop] = self
1122
1123 var mpropdef = new MVirtualTypeDef(mclassdef, mprop, self.location)
1124 self.mpropdef = mpropdef
1125 modelbuilder.mpropdef2npropdef[mpropdef] = self
1126 set_doc(mpropdef, modelbuilder)
1127
1128 var atfixed = get_single_annotation("fixed", modelbuilder)
1129 if atfixed != null then
1130 mpropdef.is_fixed = true
1131 end
1132 end
1133
1134 redef fun build_signature(modelbuilder)
1135 do
1136 var mpropdef = self.mpropdef
1137 if mpropdef == null then return # Error thus skiped
1138 var mclassdef = mpropdef.mclassdef
1139 var mmodule = mclassdef.mmodule
1140 var mtype: nullable MType = null
1141
1142 var ntype = self.n_type
1143 mtype = modelbuilder.resolve_mtype(mmodule, mclassdef, ntype)
1144 if mtype == null then return
1145
1146 mpropdef.bound = mtype
1147 # print "{mpropdef}: {mtype}"
1148 end
1149
1150 redef fun check_signature(modelbuilder)
1151 do
1152 var mpropdef = self.mpropdef
1153 if mpropdef == null then return # Error thus skiped
1154
1155 var bound = self.mpropdef.bound
1156 if bound == null then return # Error thus skiped
1157
1158 modelbuilder.check_visibility(n_type, bound, mpropdef)
1159
1160 var mclassdef = mpropdef.mclassdef
1161 var mmodule = mclassdef.mmodule
1162 var anchor = mclassdef.bound_mtype
1163
1164 # Check circularity
1165 if bound isa MVirtualType then
1166 # Slow case: progress on each resolution until: (i) we loop, or (ii) we found a non formal type
1167 var seen = [self.mpropdef.mproperty.mvirtualtype]
1168 loop
1169 if seen.has(bound) then
1170 seen.add(bound)
1171 modelbuilder.error(self, "Error: circularity of virtual type definition: {seen.join(" -> ")}")
1172 return
1173 end
1174 seen.add(bound)
1175 var next = bound.lookup_bound(mmodule, anchor)
1176 if not next isa MVirtualType then break
1177 bound = next
1178 end
1179 end
1180
1181 # Check redefinitions
1182 bound = mpropdef.bound.as(not null)
1183 for p in mpropdef.mproperty.lookup_super_definitions(mmodule, anchor) do
1184 var supbound = p.bound.as(not null)
1185 if p.is_fixed then
1186 modelbuilder.error(self, "Redef Error: Virtual type {mpropdef.mproperty} is fixed in super-class {p.mclassdef.mclass}")
1187 break
1188 end
1189 if p.mclassdef.mclass == mclassdef.mclass then
1190 # Still a warning to pass existing bad code
1191 modelbuilder.warning(n_type, "refine-type", "Redef Error: a virtual type cannot be refined.")
1192 break
1193 end
1194 if not bound.is_subtype(mmodule, anchor, supbound) then
1195 modelbuilder.error(n_type, "Redef Error: Wrong bound type. Found {bound}, expected a subtype of {supbound}, as in {p}.")
1196 break
1197 end
1198 end
1199 end
1200 end