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(MixinTemplateDeclaration) mtd)
114     {
115         // can't really know where it'll be mixed (class |final class | struct ?)
116     }
117 
118     override void visit(const(TemplateDeclaration) mtd)
119     {
120         // regular template are also mixable
121     }
122 
123     override void visit(const(Declaration) d)
124     {
125         const Parent savedParent = _parent;
126 
127         scope(exit)
128         {
129             d.accept(this);
130             _parent = savedParent;
131         }
132 
133         if (!d.attributeDeclaration &&
134             !d.classDeclaration &&
135             !d.structDeclaration &&
136             !d.unionDeclaration &&
137             !d.interfaceDeclaration &&
138             !d.functionDeclaration)
139                 return;
140 
141         import std.algorithm.iteration : filter;
142         import std.algorithm.searching : find;
143         import std.range.primitives : empty;
144 
145         if (d.attributeDeclaration && d.attributeDeclaration.attribute)
146         {
147             const tp = d.attributeDeclaration.attribute.attribute.type;
148             _private[$-1] = isProtection(tp) & (tp == tok!"private");
149         }
150 
151         const bool isFinal = !d.attributes
152             .find!(a => a.attribute.type == tok!"final")
153             .empty;
154 
155         // determine if private
156         const bool changeProtectionOnce = !d.attributes
157             .filter!(a => a.attribute.type.isProtection)
158             .empty;
159 
160         const bool isPrivateOnce = !d.attributes
161             .find!(a => a.attribute.type == tok!"private")
162             .empty;
163 
164         bool isPrivate;
165         if (isPrivateOnce)
166             isPrivate = true;
167         else if (_private[$-1] && !changeProtectionOnce)
168             isPrivate = true;
169 
170         // check final aggregate type
171         if (d.classDeclaration || d.structDeclaration || d.unionDeclaration)
172         {
173             _finalAggregate = isFinal;
174             if (_finalAggregate && savedParent == Parent.module_)
175             {
176                 if (d.structDeclaration)
177                     addError(d.structDeclaration, MESSAGE.struct_i);
178                 else if (d.unionDeclaration)
179                     addError(d.unionDeclaration, MESSAGE.union_i);
180             }
181         }
182 
183         if (!d.functionDeclaration)
184             return;
185 
186         // check final functions
187         _parent = Parent.function_;
188         const(FunctionDeclaration) fd = d.functionDeclaration;
189 
190         if (isFinal) final switch(savedParent)
191         {
192         case Parent.class_:
193             if (fd.templateParameters)
194                 addError(fd, MESSAGE.class_t);
195             if (isPrivate)
196                 addError(fd, MESSAGE.class_p);
197             else if (_finalAggregate)
198                 addError(fd, MESSAGE.class_f);
199             break;
200         case Parent.interface_:
201             if (fd.templateParameters)
202                 addError(fd, MESSAGE.interface_t);
203             break;
204         case Parent.struct_:
205             addError(fd, MESSAGE.struct_f);
206             break;
207         case Parent.union_:
208             addError(fd, MESSAGE.union_f);
209             break;
210         case Parent.function_:
211             addError(fd, MESSAGE.func_n);
212             break;
213         case Parent.module_:
214             addError(fd, MESSAGE.func_g);
215             break;
216         }
217 	}
218 }
219 
220 @system unittest
221 {
222     import analysis.config : StaticAnalysisConfig, Check;
223     import analysis.helpers : assertAnalyzerWarnings;
224     import std.stdio : stderr;
225     import std.format : format;
226 
227     StaticAnalysisConfig sac;
228     sac.final_attribute_check = Check.enabled;
229 
230     // pass
231 
232     assertAnalyzerWarnings(q{
233         void foo(){}
234     }, sac);
235 
236     assertAnalyzerWarnings(q{
237         void foo(){void foo(){}}
238     }, sac);
239 
240     assertAnalyzerWarnings(q{
241         struct S{}
242     }, sac);
243 
244     assertAnalyzerWarnings(q{
245         union U{}
246     }, sac);
247 
248     assertAnalyzerWarnings(q{
249         class Foo{public final void foo(){}}
250     }, sac);
251 
252     assertAnalyzerWarnings(q{
253         final class Foo{static struct Bar{}}
254     }, sac);
255 
256     assertAnalyzerWarnings(q{
257         class Foo{private: public final void foo(){}}
258     }, sac);
259 
260     assertAnalyzerWarnings(q{
261         class Foo{private: public: final void foo(){}}
262     }, sac);
263 
264     assertAnalyzerWarnings(q{
265         class Foo{private: public: final void foo(){}}
266     }, sac);
267 
268     assertAnalyzerWarnings(q{
269         class Impl
270         {
271             private:
272             static if (true)
273             {
274                 protected final void _wrap_getSource() {}
275             }
276         }
277     }, sac);
278 
279     assertAnalyzerWarnings(q{
280         mixin template Impl()
281         {
282             protected final void mixin_template_can() {}
283         }
284     }, sac);
285 
286     // fail
287 
288     assertAnalyzerWarnings(q{
289         final void foo(){} // [warn]: %s
290     }}.format(
291         FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.func_g)
292     ), sac);
293 
294     assertAnalyzerWarnings(q{
295         void foo(){final void foo(){}} // [warn]: %s
296     }}.format(
297         FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.func_n)
298     ), sac);
299 
300     assertAnalyzerWarnings(q{
301         void foo()
302         {
303             static if (true)
304             final class A{ private: final protected void foo(){}} // [warn]: %s
305         }
306     }}.format(
307         FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.class_f)
308     ), sac);
309 
310     assertAnalyzerWarnings(q{
311         final struct Foo{} // [warn]: %s
312     }}.format(
313         FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.struct_i)
314     ), sac);
315 
316     assertAnalyzerWarnings(q{
317         final union Foo{} // [warn]: %s
318     }}.format(
319         FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.union_i)
320     ), sac);
321 
322     assertAnalyzerWarnings(q{
323         class Foo{private final void foo(){}} // [warn]: %s
324     }}.format(
325         FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.class_p)
326     ), sac);
327 
328     assertAnalyzerWarnings(q{
329         class Foo{private: final void foo(){}} // [warn]: %s
330     }}.format(
331         FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.class_p)
332     ), sac);
333 
334     assertAnalyzerWarnings(q{
335         interface Foo{final void foo(T)(){}} // [warn]: %s
336     }}.format(
337         FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.interface_t)
338     ), sac);
339 
340     assertAnalyzerWarnings(q{
341         final class Foo{final void foo(){}} // [warn]: %s
342     }}.format(
343         FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.class_f)
344     ), sac);
345 
346     assertAnalyzerWarnings(q{
347         private: final class Foo {public: private final void foo(){}} // [warn]: %s
348     }}.format(
349         FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.class_p)
350     ), sac);
351 
352     stderr.writeln("Unittest for FinalAttributeChecker passed.");
353 }