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