1 //          Copyright Basile Burg 2017.
2 // Distributed under the Boost Software License, Version 1.0.
3 //    (See accompanying file LICENSE_1_0.txt or copy at
4 //          http://www.boost.org/LICENSE_1_0.txt)
5 
6 module analysis.final_attribute;
7 
8 import analysis.base;
9 import analysis.helpers;
10 import dparse.ast;
11 import dparse.lexer;
12 
13 /**
14  * Checks for useless usage of the final attribute.
15  *
16  * There are several cases where the compiler allows them even if it's a noop.
17  */
18 final class FinalAttributeChecker : BaseAnalyzer
19 {
20 
21 private:
22 
23     enum string KEY = "dscanner.useless.final";
24     enum string MSGB = "Useless final attribute, %s";
25 
26     static struct MESSAGE
27     {
28         static immutable struct_i    = "structs cannot be subclassed";
29         static immutable union_i     = "unions cannot be subclassed";
30         static immutable class_t     = "templated functions declared within a class are never virtual";
31         static immutable class_p     = "private functions declared within a class are never virtual";
32         static immutable class_f     = "functions declared within a final class are never virtual";
33         static immutable interface_t = "templated functions declared within an interface are never virtual";
34         static immutable struct_f    = "functions declared within a struct are never virtual";
35         static immutable union_f     = "functions declared within an union are never virtual";
36         static immutable func_n      = "nested functions are never virtual";
37         static immutable func_g      = "global functions are never virtual";
38     }
39 
40     enum Parent
41     {
42         module_,
43         struct_,
44         union_,
45         class_,
46         function_,
47         interface_
48     }
49 
50     bool[] _private;
51     bool _finalAggregate;
52     Parent _parent = Parent.module_;
53 
54     void addError(T)(T t, string msg)
55     {
56         import std.format : format;
57         const size_t lne = t.name.line;
58         const size_t col = t.name.column;
59         addErrorMessage(lne, col, KEY, MSGB.format(msg));
60     }
61 
62 public:
63 
64     alias visit = BaseAnalyzer.visit;
65 
66     ///
67     this(string fileName, bool skipTests = false)
68     {
69         super(fileName, null, skipTests);
70         _private.length = 1;
71     }
72 
73     override void visit(const(StructDeclaration) sd)
74     {
75         const Parent saved = _parent;
76         _parent = Parent.struct_;
77         _private.length += 1;
78         sd.accept(this);
79         _private.length -= 1;
80         _parent = saved;
81     }
82 
83     override void visit(const(InterfaceDeclaration) id)
84     {
85         const Parent saved = _parent;
86         _parent = Parent.interface_;
87         _private.length += 1;
88         id.accept(this);
89         _private.length -= 1;
90         _parent = saved;
91     }
92 
93     override void visit(const(UnionDeclaration) ud)
94     {
95         const Parent saved = _parent;
96         _parent = Parent.union_;
97         _private.length += 1;
98         ud.accept(this);
99         _private.length -= 1;
100         _parent = saved;
101     }
102 
103     override void visit(const(ClassDeclaration) cd)
104     {
105         const Parent saved = _parent;
106         _parent = Parent.class_;
107         _private.length += 1;
108         cd.accept(this);
109         _private.length -= 1;
110         _parent = saved;
111     }
112 
113     override void visit(const(Declaration) d)
114     {
115         const Parent savedParent = _parent;
116 
117         scope(exit)
118         {
119             d.accept(this);
120             _parent = savedParent;
121         }
122 
123         if (!d.attributeDeclaration &&
124             !d.classDeclaration &&
125             !d.structDeclaration &&
126             !d.unionDeclaration &&
127             !d.interfaceDeclaration &&
128             !d.functionDeclaration)
129                 return;
130 
131         import std.algorithm.iteration : filter;
132         import std.algorithm.searching : find;
133         import std.range.primitives : empty;
134 
135         if (d.attributeDeclaration && d.attributeDeclaration.attribute)
136         {
137             const tp = d.attributeDeclaration.attribute.attribute.type;
138             _private[$-1] = isProtection(tp) & (tp == tok!"private");
139         }
140 
141         const bool isFinal = !d.attributes
142             .find!(a => a.attribute.type == tok!"final")
143             .empty;
144 
145         // determine if private
146         const bool changeProtectionOnce = !d.attributes
147             .filter!(a => a.attribute.type.isProtection)
148             .empty;
149 
150         const bool isPrivateOnce = !d.attributes
151             .find!(a => a.attribute.type == tok!"private")
152             .empty;
153 
154         bool isPrivate;
155         if (isPrivateOnce)
156             isPrivate = true;
157         else if (_private[$-1] && !changeProtectionOnce)
158             isPrivate = true;
159 
160         // check final aggregate type
161         if (d.classDeclaration || d.structDeclaration || d.unionDeclaration)
162         {
163             _finalAggregate = isFinal;
164             if (_finalAggregate && savedParent == Parent.module_)
165             {
166                 if (d.structDeclaration)
167                     addError(d.structDeclaration, MESSAGE.struct_i);
168                 else if (d.unionDeclaration)
169                     addError(d.unionDeclaration, MESSAGE.union_i);
170             }
171         }
172 
173         if (!d.functionDeclaration)
174             return;
175 
176         // check final functions
177         _parent = Parent.function_;
178         const(FunctionDeclaration) fd = d.functionDeclaration;
179 
180         if (isFinal) final switch(savedParent)
181         {
182         case Parent.class_:
183             if (fd.templateParameters)
184                 addError(fd, MESSAGE.class_t);
185             if (isPrivate)
186                 addError(fd, MESSAGE.class_p);
187             else if (_finalAggregate)
188                 addError(fd, MESSAGE.class_f);
189             break;
190         case Parent.interface_:
191             if (fd.templateParameters)
192                 addError(fd, MESSAGE.interface_t);
193             break;
194         case Parent.struct_:
195             addError(fd, MESSAGE.struct_f);
196             break;
197         case Parent.union_:
198             addError(fd, MESSAGE.union_f);
199             break;
200         case Parent.function_:
201             addError(fd, MESSAGE.func_n);
202             break;
203         case Parent.module_:
204             addError(fd, MESSAGE.func_g);
205             break;
206         }
207 	}
208 }
209 
210 @system unittest
211 {
212     import analysis.config : StaticAnalysisConfig, Check;
213     import analysis.helpers : assertAnalyzerWarnings;
214     import std.stdio : stderr;
215     import std.format : format;
216 
217     StaticAnalysisConfig sac;
218     sac.final_attribute_check = Check.enabled;
219 
220     // pass
221 
222     assertAnalyzerWarnings(q{
223         void foo(){}
224     }, sac);
225 
226     assertAnalyzerWarnings(q{
227         void foo(){void foo(){}}
228     }, sac);
229 
230     assertAnalyzerWarnings(q{
231         struct S{}
232     }, sac);
233 
234     assertAnalyzerWarnings(q{
235         union U{}
236     }, sac);
237 
238     assertAnalyzerWarnings(q{
239         class Foo{public final void foo(){}}
240     }, sac);
241 
242     assertAnalyzerWarnings(q{
243         final class Foo{static struct Bar{}}
244     }, sac);
245 
246     assertAnalyzerWarnings(q{
247         class Foo{private: public final void foo(){}}
248     }, sac);
249 
250     assertAnalyzerWarnings(q{
251         class Foo{private: public: final void foo(){}}
252     }, sac);
253 
254     assertAnalyzerWarnings(q{
255         class Foo{private: public: final void foo(){}}
256     }, sac);
257 
258     assertAnalyzerWarnings(q{
259         class Impl
260         {
261             private:
262             static if (true)
263             {
264                 protected final void _wrap_getSource() {}
265             }
266         }
267     }, sac);
268 
269     // fail
270 
271     assertAnalyzerWarnings(q{
272         final void foo(){} // [warn]: %s
273     }}.format(
274         FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.func_g)
275     ), sac);
276 
277     assertAnalyzerWarnings(q{
278         void foo(){final void foo(){}} // [warn]: %s
279     }}.format(
280         FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.func_n)
281     ), sac);
282 
283     assertAnalyzerWarnings(q{
284         void foo()
285         {
286             static if (true)
287             final class A{ private: final protected void foo(){}} // [warn]: %s
288         }
289     }}.format(
290         FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.class_f)
291     ), sac);
292 
293     assertAnalyzerWarnings(q{
294         final struct Foo{} // [warn]: %s
295     }}.format(
296         FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.struct_i)
297     ), sac);
298 
299     assertAnalyzerWarnings(q{
300         final union Foo{} // [warn]: %s
301     }}.format(
302         FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.union_i)
303     ), sac);
304 
305     assertAnalyzerWarnings(q{
306         class Foo{private final void foo(){}} // [warn]: %s
307     }}.format(
308         FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.class_p)
309     ), sac);
310 
311     assertAnalyzerWarnings(q{
312         class Foo{private: final void foo(){}} // [warn]: %s
313     }}.format(
314         FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.class_p)
315     ), sac);
316 
317     assertAnalyzerWarnings(q{
318         interface Foo{final void foo(T)(){}} // [warn]: %s
319     }}.format(
320         FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.interface_t)
321     ), sac);
322 
323     assertAnalyzerWarnings(q{
324         final class Foo{final void foo(){}} // [warn]: %s
325     }}.format(
326         FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.class_f)
327     ), sac);
328 
329     assertAnalyzerWarnings(q{
330         private: final class Foo {public: private final void foo(){}} // [warn]: %s
331     }}.format(
332         FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.class_p)
333     ), sac);
334 
335     stderr.writeln("Unittest for FinalAttributeChecker passed.");
336 }