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