1 module cogito.visitor; 2 3 import core.stdc.string; 4 import dmd.ast_node; 5 import dmd.astcodegen; 6 import dmd.parsetimevisitor; 7 import dmd.visitor; 8 import dmd.tokens; 9 10 import cogito.list; 11 import cogito.meter; 12 import std.algorithm; 13 import std.stdio; 14 15 alias AST = ASTCodegen; 16 17 private Meter.Type declarationType(Declaration : AST.Dsymbol)(Declaration declaration) 18 { 19 if (declaration.isUnionDeclaration()) 20 { 21 return Meter.Type.union_; 22 } 23 else if (declaration.isStructDeclaration()) 24 { 25 return Meter.Type.struct_; 26 } 27 else if (declaration.isInterfaceDeclaration()) 28 { 29 return Meter.Type.interface_; 30 } 31 else if (declaration.isClassDeclaration()) 32 { 33 return Meter.Type.class_; 34 } 35 else if (declaration.isTemplateDeclaration()) 36 { 37 return Meter.Type.template_; 38 } 39 return Meter.Type.aggregate; 40 } 41 42 private string moduleName(AST.ModuleDeclaration* moduleDeclaration) 43 { 44 return moduleDeclaration is null 45 ? "app" 46 : moduleDeclaration.toString.idup; 47 } 48 49 private mixin template VisitorHelper() 50 { 51 /** 52 * Increases the score in the current or the top-level scope. 53 */ 54 private void increase(uint by = 1U) 55 { 56 if (this.parent !is null) 57 { 58 this.parent.ownScore += by; 59 } 60 else 61 { 62 this.source.ownScore += by; 63 } 64 } 65 66 private void stepInFunction(T : AST.FuncDeclaration)(T declaration) 67 { 68 auto newMeter = Meter(declaration.ident, declaration.loc, Meter.Type.callable); 69 auto parent = this.parent; 70 this.parent = &newMeter; 71 72 ++this.depth; 73 super.visit(declaration); 74 --this.depth; 75 76 this.parent = parent; 77 this.meter.insert(newMeter); 78 } 79 80 private void stepInAggregate(Declaration : AST.Dsymbol)(Declaration declaration) 81 { 82 debug writeln("Aggregate declaration ", declaration); 83 84 auto meterType = declarationType(declaration); 85 auto newMeter = Meter(declaration.ident, declaration.loc, meterType); 86 auto parent = this.parent; 87 this.parent = &newMeter; 88 89 super.visit(declaration); 90 91 this.parent = parent; 92 this.meter.insert(newMeter); 93 } 94 95 private void stepInStaticDeclaration(T : ASTNode)(T declaration) 96 { 97 increase(max(this.depth, 1)); 98 99 ++this.depth; 100 super.visit(declaration); 101 --this.depth; 102 } 103 104 private void stepInLoop(T : ASTNode)(T statement) 105 { 106 increase(this.depth); 107 108 ++this.depth; 109 super.visit(statement); 110 --this.depth; 111 } 112 113 private void stepInStatementWithLabel(T : AST.Statement)(T statement) 114 { 115 if (statement.ident !is null) 116 { 117 increase; 118 } 119 super.visit(statement); 120 } 121 } 122 123 extern(C++) final class CognitiveVisitor : SemanticTimeTransitiveVisitor 124 { 125 alias visit = SemanticTimeTransitiveVisitor.visit; 126 127 private uint depth = 0U; 128 private Source source_; 129 private List!TOK stack; 130 private Meter* parent; 131 132 extern(D) this() 133 { 134 this.source_ = Source(List!Meter()); 135 } 136 137 extern(D) this(string filename) 138 { 139 this.source_ = Source(List!Meter(), filename); 140 } 141 142 /** 143 * Returns collected scores. 144 */ 145 @property ref List!Meter meter() 146 { 147 return this.parent is null ? this.source_.inner : this.parent.inner; 148 } 149 150 /** 151 * Returns collected source file score. 152 */ 153 @property ref Source source() 154 { 155 return this.source_; 156 } 157 158 override void visit(AST.DebugStatement statement) 159 { 160 debug writeln("Debug statement ", statement); 161 // Handled as ConditionalStatement or Condition 162 super.visit(statement); 163 } 164 165 override void visit(AST.SharedStaticCtorDeclaration declaration) 166 { 167 debug writeln("Shared static constructor declaration ", declaration); 168 169 stepInFunction!(AST.SharedStaticCtorDeclaration)(declaration); 170 } 171 172 override void visit(AST.SharedStaticDtorDeclaration declaration) 173 { 174 debug writeln("Shared static destructor declaration ", declaration); 175 176 stepInFunction!(AST.SharedStaticDtorDeclaration)(declaration); 177 } 178 179 override void visit(AST.UnionDeclaration statement) 180 { 181 // Unions are handled as StructDeclarations 182 super.visit(statement); 183 } 184 185 override void visit(AST.InterfaceDeclaration statement) 186 { 187 // Interfaces are handled as ClassDeclarations 188 super.visit(statement); 189 } 190 191 override void visit(AST.StaticForeachStatement statement) 192 { 193 debug writeln("Static foreach statement ", statement); 194 195 stepInStaticDeclaration(statement); 196 } 197 198 override void visit(AST.GotoStatement statement) 199 { // There are also GotoDefaultStatement and GotoCaseStatement 200 debug writeln("Goto statement ", statement); 201 202 increase; 203 super.visit(statement); 204 } 205 206 override void visit(AST.StructDeclaration structDeclaration) 207 { 208 stepInAggregate!(AST.StructDeclaration)(structDeclaration); 209 } 210 211 override void visit(AST.ClassDeclaration classDeclaration) 212 { 213 stepInAggregate!(AST.ClassDeclaration)(classDeclaration); 214 } 215 216 override void visit(AST.FuncLiteralDeclaration declaration) 217 { 218 debug writeln("Function literal ", declaration); 219 220 stepInFunction(declaration); 221 } 222 223 override void visit(AST.FuncDeclaration declaration) 224 { 225 debug writeln("Function declaration ", declaration); 226 227 stepInFunction(declaration); 228 } 229 230 override void visit(AST.DtorDeclaration declaration) 231 { 232 debug writeln("Destructor ", declaration); 233 234 stepInFunction(declaration); 235 } 236 237 override void visit(AST.TemplateDeclaration declaration) 238 { 239 debug writeln("Template declaration ", declaration); 240 241 if (declaration.onemember !is null && declaration.members.length == 1) 242 { 243 // Ignore the template if it has only one member of the same name. 244 declaration.onemember.accept(this); 245 } 246 else 247 { 248 stepInAggregate!(AST.TemplateDeclaration)(declaration); 249 } 250 } 251 252 override void visit(AST.BinExp expression) 253 { 254 debug writeln("Binary expression ", expression); 255 256 if (expression.isLogicalExp()) { 257 // Each operator like && or || is counted once in an expression 258 // chain. 259 if (find(this.stack[], expression.op).empty) 260 { 261 increase; 262 } 263 this.stack.insert(expression.op); 264 } 265 266 super.visit(expression); 267 268 if (expression.isLogicalExp()) 269 { 270 this.stack.removeFront(); 271 } 272 } 273 274 override void visit(AST.IfStatement statement) 275 { 276 debug writeln("if statement ", statement); 277 278 statement.condition.accept(this); 279 280 if (statement.ifbody) 281 { 282 increase(this.depth); 283 284 ++this.depth; 285 statement.ifbody.accept(this); 286 --this.depth; 287 } 288 visitElseStatement(statement.elsebody); 289 } 290 291 private void visitElseStatement(AST.Statement statement) 292 { 293 if (statement is null) 294 { 295 return; 296 } 297 auto elseIf = statement.isIfStatement(); 298 if (elseIf !is null) 299 { 300 if (elseIf.ifbody) 301 { 302 increase; 303 304 ++this.depth; 305 elseIf.ifbody.accept(this); 306 --this.depth; 307 } 308 visitElseStatement(elseIf.elsebody); 309 } 310 else 311 { 312 increase; 313 314 ++this.depth; 315 statement.accept(this); 316 --this.depth; 317 } 318 } 319 320 override void visit(AST.StaticIfDeclaration declaration) 321 { 322 debug writeln("static if declaration ", declaration); 323 324 declaration.condition.accept(this); 325 326 if (declaration.decl) 327 { 328 increase(max(1, this.depth)); 329 visitNestedDeclarations(declaration); 330 } 331 visitStaticElseDeclaration(declaration.elsedecl); 332 } 333 334 private void visitStaticElseDeclaration(AST.Dsymbols* declaration) 335 { 336 if (declaration is null) 337 { 338 return; 339 } 340 if (declaration.length == 0) 341 { 342 increase; 343 } 344 each!(x => forEachStaticElseDeclaration(x))((*declaration)[]); 345 } 346 347 private void forEachStaticElseDeclaration(AST.Dsymbol elseDeclaration) 348 { 349 if (strcmp(elseDeclaration.kind, "static if") != 0) 350 { 351 increase; 352 353 ++this.depth; 354 elseDeclaration.accept(this); 355 --this.depth; 356 357 return; 358 } 359 auto elseIf = cast(AST.StaticIfDeclaration) elseDeclaration; 360 361 if (elseIf.decl !is null) 362 { 363 increase; 364 visitNestedDeclarations(elseIf); 365 } 366 visitStaticElseDeclaration(elseIf.elsedecl); 367 } 368 369 private void visitNestedDeclarations(ref AST.StaticIfDeclaration elseIf) 370 { 371 ++this.depth; 372 each!(de => de.accept(this))(*elseIf.decl); 373 --this.depth; 374 } 375 376 override void visit(AST.ConditionalStatement statement) 377 { 378 debug writeln("Conditional statement ", statement); 379 380 statement.condition.accept(this); 381 382 if (statement.ifbody) 383 { 384 increase(this.depth); 385 386 ++this.depth; 387 statement.ifbody.accept(this); 388 --this.depth; 389 } 390 visitStaticElseStatement(statement.elsebody); 391 } 392 393 private void visitStaticElseStatement(AST.Statement statement) 394 { 395 if (statement is null) 396 { 397 return; 398 } 399 auto elseIf = statement.isConditionalStatement(); 400 if (elseIf is null) 401 { 402 increase; 403 404 ++this.depth; 405 statement.accept(this); 406 --this.depth; 407 408 return; 409 } 410 if (elseIf.ifbody) 411 { 412 increase; 413 414 ++this.depth; 415 elseIf.ifbody.accept(this); 416 --this.depth; 417 } 418 visitStaticElseStatement(elseIf.elsebody); 419 } 420 421 override void visit(AST.StaticForeachDeclaration foreachDeclaration) 422 { 423 debug writeln("Static foreach declaration ", foreachDeclaration); 424 425 stepInStaticDeclaration(foreachDeclaration); 426 } 427 428 override void visit(AST.WhileStatement whileStatement) 429 { 430 debug writeln("while statement ", whileStatement); 431 432 stepInLoop(whileStatement); 433 } 434 435 override void visit(AST.DoStatement doStatement) 436 { 437 debug writeln("do statement ", doStatement); 438 439 stepInLoop(doStatement); 440 } 441 442 override void visit(AST.ForStatement forStatement) 443 { 444 debug writeln("for statement ", forStatement); 445 446 stepInLoop(forStatement); 447 } 448 449 override void visit(AST.ForeachStatement foreachStatement) 450 { 451 debug writeln("foreach statement ", foreachStatement); 452 453 stepInLoop(foreachStatement); 454 } 455 456 override void visit(AST.Module moduleDeclaration) 457 { 458 debug writeln("Module declaration ", moduleDeclaration); 459 460 this.source_.moduleName = moduleName(moduleDeclaration.md); 461 462 super.visit(moduleDeclaration); 463 } 464 465 override void visit(AST.CondExp expression) 466 { 467 debug writeln("Ternary operator ", expression); 468 469 stepInLoop(expression); 470 } 471 472 override void visit(AST.SwitchStatement statement) 473 { 474 debug writeln("Switch ", statement); 475 476 stepInLoop(statement); 477 } 478 479 override void visit(AST.TryCatchStatement statement) 480 { 481 debug writeln("try-catch statement ", statement); 482 483 if (statement._body) 484 { 485 increase(this.depth); 486 487 statement._body.accept(this); 488 } 489 ++this.depth; 490 each!(catch_ => this.visit(catch_))(*statement.catches); 491 --this.depth; 492 } 493 494 override void visit(AST.BreakStatement statement) 495 { 496 debug writeln("Break ", statement.ident); 497 498 stepInStatementWithLabel(statement); 499 } 500 501 override void visit(AST.ContinueStatement statement) 502 { 503 debug writeln("Label ", statement); 504 505 stepInStatementWithLabel(statement); 506 } 507 508 override void visit(AST.PostBlitDeclaration declaration) 509 { 510 debug writeln("Blit ", declaration); 511 512 stepInFunction(declaration); 513 } 514 515 override void visit(AST.VersionCondition condition) 516 { 517 debug writeln("Version condition ", condition); 518 519 stepInStaticDeclaration(condition); 520 } 521 522 mixin VisitorHelper; 523 }