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 }