contracts: change the contract syntax
[nit.git] / src / frontend / check_annotation.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Check that annotation present in the AST are either primitive or user-declared
16 # There is no verification on the syntax or the semantic of the annotation: just the name is checked
17 #
18 # Specific phases or tools have to process their annotation and check them correclty.
19 module check_annotation
20
21 import phase
22 private import annotation
23
24 redef class ToolContext
25 # Check for unknown annotation in each module
26 var check_annotation_phase: Phase = new CheckAnnotationPhase(self, null)
27 end
28
29 private class CheckAnnotationPhase
30 super Phase
31
32 # The map of all declared user annotation
33 # note: lazy because toolcontex is set in the init
34 var declared_annotations = new MModuleMultiData[String](toolcontext.modelbuilder.model) is lazy
35
36 # The current module.
37 var mmodule: nullable MModule = null
38
39 redef fun process_nmodule(nmodule)
40 do
41 # Get the mmodule
42 var mmodule = nmodule.mmodule
43 if mmodule == null then return
44 self.mmodule = mmodule
45
46 # If no decl block then quit
47 var nmoduledecl = nmodule.n_moduledecl
48 if nmoduledecl == null then return
49
50 var modelbuilder = toolcontext.modelbuilder
51
52 # Get all the new annotations
53 var annots = nmoduledecl.get_annotations("new_annotation")
54
55 var super_mmodules = declared_annotations.lookup_all_modules(mmodule, private_visibility)
56
57 # Add each new annotations in the map
58 for annot in annots do
59 var name = annot.arg_as_id(modelbuilder)
60 if name == null then continue
61
62 for m in super_mmodules do
63 if declared_annotations[m].has(name) then
64 modelbuilder.warning(annot, "multiple-annotation-declarations", "Warning: an annotation `{name}` is already declared in module `{m}`.")
65 break label
66 end
67 end
68
69 declared_annotations[mmodule].add(name)
70 #annot.debug "add {mmodule}: {name}"
71 end label
72 end
73
74 # Raw new-line separated list of primitive annotation
75 # Note: empty-lines will be ignored since there is no annotation named by the empty string.
76 var primtives_annotations_list = """
77 new_annotation
78
79 conditional
80
81 deprecated
82 fixed
83 lazy
84 noinit
85 readonly
86 writable
87 optional
88 autoinit
89 noautoinit
90 lateinit
91 nosuper
92 old_style_init
93 abstract
94 intern
95 extern
96 no_warning
97 generated
98
99 auto_inspect
100
101 pkgconfig
102 cflags
103 ldflags
104 light_ffi
105
106 platform
107
108 test
109 before
110 before_all
111 after
112 after_all
113 example
114
115 expect
116 ensure
117 no_contract
118 """
119
120 # Efficient set build from `primtives_annotations_list`
121 var primtives_annotations = new HashSet[String].from(primtives_annotations_list.split("\n"))
122
123 # All user-declared annotations for each mmodule
124 var user_annotations = new HashMap[MModule, HashSet[String]]
125
126 redef fun process_annotated_node(node, nat)
127 do
128 var name = nat.name
129 if primtives_annotations.has(name) then return
130
131 var mmodule = self.mmodule
132 if mmodule == null then return
133
134 # Lazily build the full user-list
135 var annots = user_annotations.get_or_null(mmodule)
136 if annots == null then
137 annots = new HashSet[String]
138 annots.add_all(declared_annotations.lookup_joined_values(mmodule, private_visibility))
139 user_annotations[mmodule] = annots
140 end
141 #nat.debug "for {mmodule}: {annots.join(" ")}"
142
143 if annots.has(name) then return
144
145 toolcontext.modelbuilder.warning(nat, "unknown-annotation", "Warning: unknown annotation `{name}`.")
146
147 annots.add(name) # to avoid multiple errors on the same name
148 end
149 end