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