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