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 dscanner.analysis.final_attribute; 7 8 import dscanner.analysis.base; 9 import dscanner.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 class_s = "static functions are never virtual"; 34 static immutable interface_t = "templated functions declared within an interface are never virtual"; 35 static immutable struct_f = "functions declared within a struct are never virtual"; 36 static immutable union_f = "functions declared within an union are never virtual"; 37 static immutable func_n = "nested functions are never virtual"; 38 static immutable func_g = "global functions are never virtual"; 39 } 40 41 enum Parent 42 { 43 module_, 44 struct_, 45 union_, 46 class_, 47 function_, 48 interface_ 49 } 50 51 bool _private; 52 bool _finalAggregate; 53 bool _alwaysStatic; 54 bool _blockStatic; 55 Parent _parent = Parent.module_; 56 57 void addError(T)(T t, string msg) 58 { 59 import std.format : format; 60 const size_t lne = t.name.line; 61 const size_t col = t.name.column; 62 addErrorMessage(lne, col, KEY, MSGB.format(msg)); 63 } 64 65 public: 66 67 alias visit = BaseAnalyzer.visit; 68 69 enum pushPopPrivate = q{ 70 const bool wasPrivate = _private; 71 _private = false; 72 scope (exit) _private = wasPrivate; 73 }; 74 75 /// 76 this(string fileName, bool skipTests = false) 77 { 78 super(fileName, null, skipTests); 79 } 80 81 override void visit(const(StructDeclaration) sd) 82 { 83 mixin (pushPopPrivate); 84 const Parent saved = _parent; 85 _parent = Parent.struct_; 86 _alwaysStatic = false; 87 sd.accept(this); 88 _parent = saved; 89 } 90 91 override void visit(const(InterfaceDeclaration) id) 92 { 93 mixin (pushPopPrivate); 94 const Parent saved = _parent; 95 _parent = Parent.interface_; 96 _alwaysStatic = false; 97 id.accept(this); 98 _parent = saved; 99 } 100 101 override void visit(const(UnionDeclaration) ud) 102 { 103 mixin (pushPopPrivate); 104 const Parent saved = _parent; 105 _parent = Parent.union_; 106 _alwaysStatic = false; 107 ud.accept(this); 108 _parent = saved; 109 } 110 111 override void visit(const(ClassDeclaration) cd) 112 { 113 mixin (pushPopPrivate); 114 const Parent saved = _parent; 115 _parent = Parent.class_; 116 _alwaysStatic = false; 117 cd.accept(this); 118 _parent = saved; 119 } 120 121 override void visit(const(MixinTemplateDeclaration) mtd) 122 { 123 // can't really know where it'll be mixed (class |final class | struct ?) 124 } 125 126 override void visit(const(TemplateDeclaration) mtd) 127 { 128 // regular template are also mixable 129 } 130 131 override void visit(const(AttributeDeclaration) decl) 132 { 133 if (_parent == Parent.class_ && decl.attribute && 134 decl.attribute.attribute == tok!"static") 135 _alwaysStatic = true; 136 } 137 138 override void visit(const(Declaration) d) 139 { 140 import std.algorithm.iteration : filter; 141 import std.algorithm.searching : canFind; 142 143 const Parent savedParent = _parent; 144 145 bool undoBlockStatic; 146 if (_parent == Parent.class_ && d.attributes && 147 d.attributes.canFind!(a => a.attribute == tok!"static")) 148 { 149 _blockStatic = true; 150 undoBlockStatic = true; 151 } 152 153 const bool wasFinalAggr = _finalAggregate; 154 scope(exit) 155 { 156 d.accept(this); 157 _parent = savedParent; 158 if (undoBlockStatic) 159 _blockStatic = false; 160 _finalAggregate = wasFinalAggr; 161 } 162 163 if (!d.attributeDeclaration && 164 !d.classDeclaration && 165 !d.structDeclaration && 166 !d.unionDeclaration && 167 !d.interfaceDeclaration && 168 !d.functionDeclaration) 169 return; 170 171 if (d.attributeDeclaration && d.attributeDeclaration.attribute) 172 { 173 const tp = d.attributeDeclaration.attribute.attribute.type; 174 _private = isProtection(tp) & (tp == tok!"private"); 175 } 176 177 const bool isFinal = d.attributes 178 .canFind!(a => a.attribute.type == tok!"final"); 179 180 const bool isStaticOnce = d.attributes 181 .canFind!(a => a.attribute.type == tok!"static"); 182 183 // determine if private 184 const bool changeProtectionOnce = d.attributes 185 .canFind!(a => a.attribute.type.isProtection); 186 187 const bool isPrivateOnce = d.attributes 188 .canFind!(a => a.attribute.type == tok!"private"); 189 190 bool isPrivate; 191 192 if (isPrivateOnce) 193 isPrivate = true; 194 else if (_private && !changeProtectionOnce) 195 isPrivate = true; 196 197 // check final aggregate type 198 if (d.classDeclaration || d.structDeclaration || d.unionDeclaration) 199 { 200 _finalAggregate = isFinal; 201 if (_finalAggregate && savedParent == Parent.module_) 202 { 203 if (d.structDeclaration) 204 addError(d.structDeclaration, MESSAGE.struct_i); 205 else if (d.unionDeclaration) 206 addError(d.unionDeclaration, MESSAGE.union_i); 207 } 208 } 209 210 if (!d.functionDeclaration) 211 return; 212 213 // check final functions 214 _parent = Parent.function_; 215 const(FunctionDeclaration) fd = d.functionDeclaration; 216 217 if (isFinal) final switch(savedParent) 218 { 219 case Parent.class_: 220 if (fd.templateParameters) 221 addError(fd, MESSAGE.class_t); 222 if (isPrivate) 223 addError(fd, MESSAGE.class_p); 224 else if (isStaticOnce || _alwaysStatic || _blockStatic) 225 addError(fd, MESSAGE.class_s); 226 else if (_finalAggregate) 227 addError(fd, MESSAGE.class_f); 228 break; 229 case Parent.interface_: 230 if (fd.templateParameters) 231 addError(fd, MESSAGE.interface_t); 232 break; 233 case Parent.struct_: 234 addError(fd, MESSAGE.struct_f); 235 break; 236 case Parent.union_: 237 addError(fd, MESSAGE.union_f); 238 break; 239 case Parent.function_: 240 addError(fd, MESSAGE.func_n); 241 break; 242 case Parent.module_: 243 addError(fd, MESSAGE.func_g); 244 break; 245 } 246 } 247 } 248 249 @system unittest 250 { 251 import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig; 252 import dscanner.analysis.helpers : assertAnalyzerWarnings; 253 import std.stdio : stderr; 254 import std.format : format; 255 256 StaticAnalysisConfig sac = disabledConfig(); 257 sac.final_attribute_check = Check.enabled; 258 259 // pass 260 261 assertAnalyzerWarnings(q{ 262 void foo(){} 263 }, sac); 264 265 assertAnalyzerWarnings(q{ 266 void foo(){void foo(){}} 267 }, sac); 268 269 assertAnalyzerWarnings(q{ 270 struct S{} 271 }, sac); 272 273 assertAnalyzerWarnings(q{ 274 union U{} 275 }, sac); 276 277 assertAnalyzerWarnings(q{ 278 class Foo{public final void foo(){}} 279 }, sac); 280 281 assertAnalyzerWarnings(q{ 282 final class Foo{static struct Bar{}} 283 }, sac); 284 285 assertAnalyzerWarnings(q{ 286 class Foo{private: public final void foo(){}} 287 }, sac); 288 289 assertAnalyzerWarnings(q{ 290 class Foo{private: public: final void foo(){}} 291 }, sac); 292 293 assertAnalyzerWarnings(q{ 294 class Foo{private: public: final void foo(){}} 295 }, sac); 296 297 assertAnalyzerWarnings(q{ 298 class Impl 299 { 300 private: 301 static if (true) 302 { 303 protected final void _wrap_getSource() {} 304 } 305 } 306 }, sac); 307 308 assertAnalyzerWarnings(q{ 309 mixin template Impl() 310 { 311 protected final void mixin_template_can() {} 312 } 313 }, sac); 314 315 // fail 316 317 assertAnalyzerWarnings(q{ 318 final void foo(){} // [warn]: %s 319 }}.format( 320 FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.func_g) 321 ), sac); 322 323 assertAnalyzerWarnings(q{ 324 void foo(){final void foo(){}} // [warn]: %s 325 }}.format( 326 FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.func_n) 327 ), sac); 328 329 assertAnalyzerWarnings(q{ 330 void foo() 331 { 332 static if (true) 333 final class A{ private: final protected void foo(){}} // [warn]: %s 334 } 335 }}.format( 336 FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.class_f) 337 ), sac); 338 339 assertAnalyzerWarnings(q{ 340 final struct Foo{} // [warn]: %s 341 }}.format( 342 FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.struct_i) 343 ), sac); 344 345 assertAnalyzerWarnings(q{ 346 final union Foo{} // [warn]: %s 347 }}.format( 348 FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.union_i) 349 ), sac); 350 351 assertAnalyzerWarnings(q{ 352 class Foo{private final void foo(){}} // [warn]: %s 353 }}.format( 354 FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.class_p) 355 ), sac); 356 357 assertAnalyzerWarnings(q{ 358 class Foo{private: final void foo(){}} // [warn]: %s 359 }}.format( 360 FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.class_p) 361 ), sac); 362 363 assertAnalyzerWarnings(q{ 364 interface Foo{final void foo(T)(){}} // [warn]: %s 365 }}.format( 366 FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.interface_t) 367 ), sac); 368 369 assertAnalyzerWarnings(q{ 370 final class Foo{final void foo(){}} // [warn]: %s 371 }}.format( 372 FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.class_f) 373 ), sac); 374 375 assertAnalyzerWarnings(q{ 376 private: final class Foo {public: private final void foo(){}} // [warn]: %s 377 }}.format( 378 FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.class_p) 379 ), sac); 380 381 assertAnalyzerWarnings(q{ 382 class Foo {final static void foo(){}} // [warn]: %s 383 }}.format( 384 FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.class_s) 385 ), sac); 386 387 assertAnalyzerWarnings(q{ 388 class Foo 389 { 390 void foo(){} 391 static: final void foo(){} // [warn]: %s 392 } 393 }}.format( 394 FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.class_s) 395 ), sac); 396 397 assertAnalyzerWarnings(q{ 398 class Foo 399 { 400 void foo(){} 401 static{ final void foo(){}} // [warn]: %s 402 void foo(){} 403 } 404 }}.format( 405 FinalAttributeChecker.MSGB.format(FinalAttributeChecker.MESSAGE.class_s) 406 ), sac); 407 408 409 assertAnalyzerWarnings(q{ 410 class Statement 411 { 412 final class UsesEH{} 413 final void comeFrom(){} 414 } 415 }, sac); 416 417 stderr.writeln("Unittest for FinalAttributeChecker passed."); 418 }