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 }