Rename REAMDE to README.md
[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 buggy_sends: Int = 0
113
114 # Get a new visitor on a classef to add type count in `typecount`.
115 init(modelbuilder: ModelBuilder, nclassdef: AClassdef)
116 do
117 self.modelbuilder = modelbuilder
118 self.nclassdef = nclassdef
119 end
120
121 redef fun visit(n)
122 do
123 n.visit_all(self)
124 if n isa ASendExpr then
125 self.total_sends += 1
126 var t = n.n_expr.mtype
127 if t == null then
128 self.buggy_sends += 1
129 return
130 end
131 t = t.anchor_to(self.nclassdef.mclassdef.mmodule, self.nclassdef.mclassdef.bound_mtype)
132 if t isa MNullableType then
133 self.nullable_sends += 1
134 else if t isa MClassType then
135 # Nothing
136 else
137 n.debug("Problem: strange receiver type found: {t} ({t.class_name})")
138 end
139 end
140 end
141 end
142
143 # Visit the AST and print metrics about the usage of send on nullable reciever.
144 fun compute_nullables_metrics(modelbuilder: ModelBuilder)
145 do
146 print "--- Sends on Nullable Receiver ---"
147 var total_sends = 0
148 var nullable_sends = 0
149 var buggy_sends = 0
150
151 # Visit all the source code to collect data
152 for nmodule in modelbuilder.nmodules do
153 for nclassdef in nmodule.n_classdefs do
154 var visitor = new NullableSends(modelbuilder, nclassdef)
155 visitor.enter_visit(nclassdef)
156 total_sends += visitor.total_sends
157 nullable_sends += visitor.nullable_sends
158 buggy_sends += visitor.buggy_sends
159 end
160 end
161 print "Total number of sends: {total_sends}"
162 print "Number of sends on a nullable receiver: {nullable_sends} ({div(nullable_sends*100,total_sends)}%)"
163 print "Number of buggy sends (cannot determine the type of the receiver): {buggy_sends} ({div(buggy_sends*100,total_sends)}%)"
164 end