bootstrap: active cleaning of remaining useless files
[nit.git] / src / metrics / nullables_metrics.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 # Statistics about the usage of nullables
18 module nullables_metrics
19
20 import metrics_base
21 import mclasses_metrics
22 import semantize
23
24 redef class ToolContext
25 var nullables_metrics_phase: Phase = new NullablesMetricsPhase(self, null)
26 end
27
28 private class NullablesMetricsPhase
29 super Phase
30 redef fun process_mainmodule(mainmodule, given_mmodules)
31 do
32 if not toolcontext.opt_nullables.value and not toolcontext.opt_all.value then return
33
34 var csv = toolcontext.opt_csv.value
35 var out = "{toolcontext.opt_dir.value or else "metrics"}/nullables"
36 out.mkdir
37
38 print toolcontext.format_h1("\n# Nullable metrics")
39
40 var metrics = new MetricSet
41 var min_vis = private_visibility
42 metrics.register(new CNBA(mainmodule, min_vis))
43 metrics.register(new CNBNA(mainmodule, min_vis))
44
45 var model = toolcontext.modelbuilder.model
46 var mclasses = new HashSet[MClass]
47 for mproject in model.mprojects do
48
49 print toolcontext.format_h2("\n ## project {mproject}")
50
51 for mgroup in mproject.mgroups do
52 if mgroup.mmodules.is_empty then continue
53 metrics.clear
54
55 # Scalar metrics
56 print toolcontext.format_h3(" `- group {mgroup.full_name}")
57 var mod_mclasses = new HashSet[MClass]
58 for mmodule in mgroup.mmodules do mod_mclasses.add_all(mmodule.intro_mclasses)
59 if mod_mclasses.is_empty then continue
60 mclasses.add_all(mod_mclasses)
61 metrics.collect(new HashSet[MClass].from(mod_mclasses))
62 metrics.to_console(1, not toolcontext.opt_nocolors.value)
63 if csv then metrics.to_csv.save("{out}/{mgroup}.csv")
64 end
65 end
66 if not mclasses.is_empty then
67 metrics.clear
68 # Global metrics
69 print toolcontext.format_h2("\n ## global metrics")
70 metrics.collect(mclasses)
71 metrics.to_console(1, not toolcontext.opt_nocolors.value)
72 if csv then metrics.to_csv.save("{out}/summary.csv")
73 end
74
75 compute_nullables_metrics(toolcontext.modelbuilder)
76 end
77 end
78
79 # Class Metric: Number of nullables MAttributes
80 class CNBNA
81 super MClassMetric
82 super IntMetric
83 redef fun name do return "cnbna"
84 redef fun desc do return "number of accessible nullable attributes (inherited + local)"
85
86 var mainmodule: MModule
87 var min_visibility: MVisibility
88
89 init(mainmodule: MModule, min_visibility: MVisibility) do
90 self.mainmodule = mainmodule
91 self.min_visibility = min_visibility
92 end
93
94 redef fun collect(mclasses) do
95 for mclass in mclasses do
96 var all = mclass.all_mattributes(mainmodule, min_visibility)
97 for mattr in all do
98 if mattr.is_nullable then values.inc(mclass)
99 end
100 end
101 end
102 end
103
104
105 private class NullableSends
106 super Visitor
107 var modelbuilder: ModelBuilder
108 var nclassdef: AClassdef
109
110 var total_sends: Int = 0
111 var nullable_sends: Int = 0
112 var nullable_eq_sends: Int = 0
113 var buggy_sends: Int = 0
114
115 # Get a new visitor on a classef to add type count in `typecount`.
116 init(modelbuilder: ModelBuilder, nclassdef: AClassdef)
117 do
118 self.modelbuilder = modelbuilder
119 self.nclassdef = nclassdef
120 end
121
122 redef fun visit(n)
123 do
124 n.visit_all(self)
125 if n isa ASendExpr then
126 self.total_sends += 1
127 var t = n.n_expr.mtype
128 if t == null then
129 self.buggy_sends += 1
130 return
131 end
132 t = t.anchor_to(self.nclassdef.mclassdef.mmodule, self.nclassdef.mclassdef.bound_mtype)
133 if t isa MNullableType then
134 var name = n.callsite.mproperty.name
135 if name == "==" or name == "!=" or name == "is_same_instance" then
136 self.nullable_eq_sends += 1
137 else
138 self.nullable_sends += 1
139 end
140 else if t isa MClassType then
141 # Nothing
142 else
143 n.debug("Problem: strange receiver type found: {t} ({t.class_name})")
144 end
145 end
146 end
147 end
148
149 # Visit the AST and print metrics about the usage of send on nullable reciever.
150 fun compute_nullables_metrics(modelbuilder: ModelBuilder)
151 do
152 print "--- Sends on Nullable Receiver ---"
153 var total_sends = 0
154 var nullable_sends = 0
155 var nullable_eq_sends = 0
156 var buggy_sends = 0
157
158 # Visit all the source code to collect data
159 for nmodule in modelbuilder.nmodules do
160 for nclassdef in nmodule.n_classdefs do
161 var visitor = new NullableSends(modelbuilder, nclassdef)
162 visitor.enter_visit(nclassdef)
163 total_sends += visitor.total_sends
164 nullable_sends += visitor.nullable_sends
165 nullable_eq_sends += visitor.nullable_eq_sends
166 buggy_sends += visitor.buggy_sends
167 end
168 end
169 print "Total number of sends: {total_sends}"
170 print "Number of sends on a unsafe nullable receiver: {nullable_sends} ({div(nullable_sends*100,total_sends)}%)"
171 print "Number of sends on a safe nullable receiver: {nullable_eq_sends} ({div(nullable_eq_sends*100,total_sends)}%)"
172 print "Number of buggy sends (cannot determine the type of the receiver): {buggy_sends} ({div(buggy_sends*100,total_sends)}%)"
173 end