tests: updates for user-declated annotations
[nit.git] / src / 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 import annotation
23
24 redef class ToolContext
25 var check_annotation_phase: Phase = new CheckAnnotationPhase(self, null)
26 end
27
28 private class CheckAnnotationPhase
29 super Phase
30
31 # The map of all declared user annotation
32 # note: lazy because toolcontex is set in the init
33 var declared_annotations = new MModuleMultiData[String](toolcontext.modelbuilder.model) is lazy
34
35 # The current module.
36 var mmodule: nullable MModule = null
37
38 redef fun process_nmodule(nmodule)
39 do
40 # Get the mmodule
41 var mmodule = nmodule.mmodule
42 assert mmodule != null
43 self.mmodule = mmodule
44
45 # If no decl block then quit
46 var nmoduledecl = nmodule.n_moduledecl
47 if nmoduledecl == null then return
48
49 var modelbuilder = toolcontext.modelbuilder
50
51 # Get all the new annotations
52 var annots = nmoduledecl.get_annotations("new_annotation")
53
54 # Add each new annotations in the map
55 for annot in annots do
56 var name = annot.arg_as_id(modelbuilder)
57 if name == null then continue
58
59 declared_annotations[mmodule].add(name)
60 #annot.debug "add {mmodule}: {name}"
61 end
62 end
63
64 # Raw new-line separated list of primitive annotation
65 # Note: empty-lines will be ignored since there is no annotation named by the empty string.
66 var primtives_annotations_list = """
67 new_annotation
68
69 fixed
70 lazy
71 noinit
72 readonly
73 writable
74 cached
75
76 pkgconfig
77 c_compiler_option
78 c_linker_option
79
80 platform
81 """
82
83 # Efficient set build from `primtives_annotations_list`
84 var primtives_annotations = new HashSet[String].from(primtives_annotations_list.split("\n"))
85
86 # All user-declared annotations for each mmodule
87 var user_annotations = new HashMap[MModule, HashSet[String]]
88
89 redef fun process_annotated_node(node, nat)
90 do
91 var name = nat.name
92 if primtives_annotations.has(name) then return
93
94 var mmodule = self.mmodule
95 assert mmodule != null
96
97 # Lazily build the full user-list
98 var annots = user_annotations.get_or_null(mmodule)
99 if annots == null then
100 annots = new HashSet[String]
101 annots.add_all(declared_annotations.lookup_joined_values(mmodule, private_visibility))
102 user_annotations[mmodule] = annots
103 end
104 #nat.debug "for {mmodule}: {annots.join(" ")}"
105
106 if annots.has(name) then return
107
108 toolcontext.modelbuilder.warning(nat, "Warning: unknown annotation `{name}`")
109
110 annots.add(name) # to avoid multiple errors on the same name
111 end
112 end