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 extern(C++) final class CognitiveVisitor : SemanticTimeTransitiveVisitor
18 {
19     alias visit = SemanticTimeTransitiveVisitor.visit;
20 
21     private uint depth = 0U;
22     private Source source_;
23     private List!TOK stack;
24     private Meter* parent;
25 
26     extern(D) this()
27     {
28         this.source_ = Source(List!Meter());
29     }
30 
31     extern(D) this(string filename)
32     {
33         this.source_ = Source(List!Meter(), filename);
34     }
35 
36     /**
37      * Returns collected scores.
38      */
39     @property ref List!Meter meter()
40     {
41         return this.parent is null ? this.source_.inner : this.parent.inner;
42     }
43 
44     /**
45      * Returns collected source file score.
46      */
47     @property ref Source source()
48     {
49         return this.source_;
50     }
51 
52     /**
53      * Increases the score in the current or the top-level scope.
54      */
55     private void increase(uint by = 1U)
56     {
57         if (this.parent !is null)
58         {
59             this.parent.ownScore += by;
60         }
61         else
62         {
63             this.source.ownScore += by;
64         }
65     }
66 
67     override void visit(AST.Dsymbol symbol)
68     {
69         debug printf("Symbol %s\n", symbol.toPrettyChars());
70 
71         super.visit(symbol);
72     }
73 
74     override void visit(AST.Expression expression)
75     {
76         debug writeln("Expression ", expression);
77 
78         super.visit(expression);
79     }
80 
81     override void visit(AST.TemplateParameter parameter)
82     {
83         debug writeln("Parameter ", parameter);
84 
85         super.visit(parameter);
86     }
87 
88     override void visit(AST.Condition condition)
89     {
90         debug writeln("Condition ", condition);
91 
92         super.visit(condition);
93     }
94 
95     override void visit(AST.Initializer initializer)
96     {
97         debug writeln("Initializer ", initializer);
98 
99         super.visit(initializer);
100     }
101 
102     override void visit(AST.PeelStatement statement)
103     {
104         debug writeln("Peel statement ", statement);
105 
106         super.visit(statement);
107     }
108 
109     override void visit(AST.UnrolledLoopStatement statement)
110     {
111         debug writeln("Unrolled loop statement ", statement);
112 
113         super.visit(statement);
114     }
115 
116     override void visit(AST.DebugStatement statement)
117     {
118         debug writeln("Debug statement ", statement);
119         // Handled as ConditionalStatement or Condition
120         super.visit(statement);
121     }
122 
123     override void visit(AST.ForwardingStatement statement)
124     {
125         debug writeln("Forwarding statement ", statement);
126 
127         super.visit(statement);
128     }
129 
130     override void visit(AST.StructLiteralExp expression)
131     {
132         debug writeln("Struct literal expression ", expression);
133 
134         super.visit(expression);
135     }
136 
137     override void visit(AST.CompoundLiteralExp expression)
138     {
139         debug writeln("Compound literal expression ", expression);
140 
141         super.visit(expression);
142     }
143 
144     override void visit(AST.DotTemplateExp expression)
145     {
146         debug writeln("Dot template expression ", expression);
147 
148         super.visit(expression);
149     }
150 
151     override void visit(AST.DotVarExp expression)
152     {
153         debug writeln("dot var expression ", expression);
154 
155         super.visit(expression);
156     }
157 
158     override void visit(AST.DelegateExp expression)
159     {
160         debug writeln("Delegate expression ", expression);
161 
162         super.visit(expression);
163     }
164 
165     override void visit(AST.DelegatePtrExp expression)
166     {
167         debug writeln("Delegate pointer expression ", expression);
168 
169         super.visit(expression);
170     }
171 
172     override void visit(AST.DelegateFuncptrExp expression)
173     {
174         debug writeln("Delegate function pointer expression ", expression);
175 
176         super.visit(expression);
177     }
178 
179     override void visit(AST.DotTypeExp expression)
180     {
181         debug writeln("Dot type expression ", expression);
182 
183         super.visit(expression);
184     }
185 
186     override void visit(AST.VectorExp expression)
187     {
188         debug writeln("Vector expression ", expression);
189 
190         super.visit(expression);
191     }
192 
193     override void visit(AST.VectorArrayExp expression)
194     {
195         debug writeln("Vector array expression ", expression);
196 
197         super.visit(expression);
198     }
199 
200     override void visit(AST.SliceExp expression)
201     {
202         debug writeln("Slice expression ", expression);
203 
204         super.visit(expression);
205     }
206 
207     override void visit(AST.ArrayLengthExp expression)
208     {
209         debug writeln("Array length expression ", expression);
210 
211         super.visit(expression);
212     }
213 
214     override void visit(AST.DotExp expression)
215     {
216         debug writeln("Dot expression ", expression);
217 
218         super.visit(expression);
219     }
220 
221     override void visit(AST.IndexExp expression)
222     {
223         debug writeln("Index expression ", expression);
224 
225         super.visit(expression);
226     }
227 
228     override void visit(AST.RemoveExp expression)
229     {
230         debug writeln("Remove expression ", expression);
231 
232         super.visit(expression);
233     }
234 
235     override void visit(AST.Declaration declaration)
236     {
237         debug writeln("Declaration ", declaration);
238 
239         super.visit(declaration);
240     }
241 
242     override void visit(AST.ScopeDsymbol statement)
243     {
244         debug writeln("Scope symbol ", statement);
245 
246         super.visit(statement);
247     }
248 
249     override void visit(AST.Package statement)
250     {
251         debug writeln("Package ", statement);
252 
253         super.visit(statement);
254     }
255 
256     override void visit(AST.AggregateDeclaration statement)
257     {
258         debug writeln("Aggregate declaration ", statement);
259 
260         super.visit(statement);
261     }
262 
263     override void visit(AST.TupleDeclaration statement)
264     {
265         debug writeln("Tuple declaration ", statement);
266 
267         super.visit(statement);
268     }
269 
270     override void visit(AST.CtorDeclaration statement)
271     {
272         debug writeln("Constructor declaration ", statement);
273 
274         super.visit(statement);
275     }
276 
277     override void visit(AST.SharedStaticCtorDeclaration declaration)
278     {
279         debug writeln("Shared static constructor declaration ", declaration);
280 
281         stepInFunction!(AST.SharedStaticCtorDeclaration)(declaration);
282     }
283 
284     override void visit(AST.SharedStaticDtorDeclaration declaration)
285     {
286         debug writeln("Shared static destructor declaration ", declaration);
287 
288         stepInFunction!(AST.SharedStaticDtorDeclaration)(declaration);
289     }
290 
291     override void visit(AST.UnionDeclaration statement)
292     {
293         debug writeln("Union ", statement);
294 
295         // Unions are handled as StructDeclarations
296         super.visit(statement);
297     }
298 
299     override void visit(AST.InterfaceDeclaration statement)
300     {
301         debug writeln("Interface ", statement);
302 
303         // Interfaces are handled as ClassDeclarations
304         super.visit(statement);
305     }
306 
307     override void visit(AST.BitFieldDeclaration statement)
308     {
309         debug writeln(statement.stringof, ' ', statement);
310 
311         super.visit(statement);
312     }
313 
314     override void visit(AST.StaticForeachStatement statement)
315     {
316         debug writeln("Static foreach statement ", statement);
317 
318         stepInStaticDeclaration(statement);
319     }
320 
321     override void visit(AST.GotoStatement statement)
322     { // There are also GotoDefaultStatement and GotoCaseStatement
323         debug writeln("Goto statement ", statement);
324 
325         increase;
326         super.visit(statement);
327     }
328 
329     override void visit(AST.StructDeclaration structDeclaration)
330     {
331         debug writeln("Struct declaration ", structDeclaration);
332 
333         stepInAggregate!(AST.StructDeclaration)(structDeclaration);
334     }
335 
336     override void visit(AST.ClassDeclaration classDeclaration)
337     {
338         debug writeln("Class declaration ", classDeclaration);
339 
340         stepInAggregate!(AST.ClassDeclaration)(classDeclaration);
341     }
342 
343     private void stepInAggregate(Declaration : AST.Dsymbol)(Declaration declaration)
344     {
345         auto newMeter = Meter(declaration.ident, declaration.loc, Meter.Type.aggregate);
346         auto parent = this.parent;
347         this.parent = &newMeter;
348 
349         super.visit(declaration);
350 
351         this.parent = parent;
352         this.meter.insert(newMeter);
353     }
354 
355     override void visit(AST.FuncLiteralDeclaration declaration)
356     {
357         debug writeln("Function literal ", declaration);
358 
359         stepInFunction(declaration);
360     }
361 
362     override void visit(AST.FuncDeclaration declaration)
363     {
364         debug writeln("Function declaration ", declaration);
365 
366         stepInFunction(declaration);
367     }
368 
369     override void visit(AST.DtorDeclaration declaration)
370     {
371         debug writeln("Destructor ", declaration);
372 
373         stepInFunction(declaration);
374     }
375 
376     private void stepInFunction(T : AST.FuncDeclaration)(T declaration)
377     {
378         auto newMeter = Meter(declaration.ident, declaration.loc, Meter.Type.callable);
379         auto parent = this.parent;
380         this.parent = &newMeter;
381 
382         ++this.depth;
383         super.visit(declaration);
384         --this.depth;
385 
386         this.parent = parent;
387         this.meter.insert(newMeter);
388     }
389 
390     override void visit(AST.Statement s)
391     {
392         debug writeln("Statement ", s.stmt);
393 
394         super.visit(s);
395     }
396 
397     override void visit(AST.TemplateDeclaration declaration)
398     {
399         debug writeln("Template declaration ", declaration);
400 
401         stepInAggregate!(AST.TemplateDeclaration)(declaration);
402     }
403 
404     override void visit(AST.BinExp expression)
405     {
406         debug writeln("Binary expression ", expression);
407 
408         if (expression.isLogicalExp()) {
409             // Each operator like && or || is counted once in an expression
410             // chain.
411             if (find(this.stack[], expression.op).empty)
412             {
413                 increase;
414             }
415             this.stack.insert(expression.op);
416         }
417 
418         super.visit(expression);
419 
420         if (expression.isLogicalExp()) {
421             this.stack.removeFront();
422         }
423     }
424 
425     override void visit(AST.IfStatement statement)
426     {
427         debug writeln("if statement ", statement);
428 
429         statement.condition.accept(this);
430 
431         if (statement.ifbody)
432         {
433             increase(this.depth);
434 
435             ++this.depth;
436             statement.ifbody.accept(this);
437             --this.depth;
438         }
439         visitElseStatement(statement.elsebody);
440     }
441 
442     private void visitElseStatement(AST.Statement statement)
443     {
444         if (statement is null)
445         {
446             return;
447         }
448         auto elseIf = statement.isIfStatement();
449         if (elseIf !is null)
450         {
451             if (elseIf.ifbody)
452             {
453                 increase;
454 
455                 ++this.depth;
456                 elseIf.ifbody.accept(this);
457                 --this.depth;
458             }
459             visitElseStatement(elseIf.elsebody);
460         }
461         else
462         {
463             increase;
464 
465             ++this.depth;
466             statement.accept(this);
467             --this.depth;
468         }
469     }
470 
471     override void visit(AST.StaticIfDeclaration declaration)
472     {
473         debug writeln("static if declaration ", declaration);
474 
475         declaration.condition.accept(this);
476 
477         if (declaration.decl)
478         {
479             increase(max(1, this.depth));
480             visitNestedDeclarations(declaration);
481         }
482         visitStaticElseDeclaration(declaration.elsedecl);
483     }
484 
485     private void visitStaticElseDeclaration(AST.Dsymbols* declaration)
486     {
487         if (declaration is null)
488         {
489             return;
490         }
491         if (declaration.length == 0)
492         {
493             increase;
494         }
495         each!(x => forEachStaticElseDeclaration(x))((*declaration)[]);
496     }
497 
498     private void forEachStaticElseDeclaration(AST.Dsymbol elseDeclaration)
499     {
500         if (strcmp(elseDeclaration.kind, "static if") == 0)
501         {
502             auto elseIf = cast(AST.StaticIfDeclaration) elseDeclaration;
503             if (elseIf.decl !is null)
504             {
505                 increase;
506                 visitNestedDeclarations(elseIf);
507             }
508             visitStaticElseDeclaration(elseIf.elsedecl);
509         }
510         else
511         {
512             increase;
513 
514             ++this.depth;
515             elseDeclaration.accept(this);
516             --this.depth;
517         }
518     }
519 
520     private void visitNestedDeclarations(ref AST.StaticIfDeclaration elseIf)
521     {
522         ++this.depth;
523         foreach (de; *elseIf.decl)
524         {
525             de.accept(this);
526         }
527         --this.depth;
528     }
529 
530     override void visit(AST.ConditionalStatement statement)
531     {
532         debug writeln("Conditional statement ", statement);
533 
534         statement.condition.accept(this);
535 
536         if (statement.ifbody)
537         {
538             increase(this.depth);
539 
540             ++this.depth;
541             statement.ifbody.accept(this);
542             --this.depth;
543         }
544         visitStaticElseStatement(statement.elsebody);
545     }
546 
547     private void visitStaticElseStatement(AST.Statement statement)
548     {
549         if (statement is null)
550         {
551             return;
552         }
553         auto elseIf = statement.isConditionalStatement();
554         if (elseIf !is null)
555         {
556             if (elseIf.ifbody)
557             {
558                 increase;
559 
560                 ++this.depth;
561                 elseIf.ifbody.accept(this);
562                 --this.depth;
563             }
564             visitStaticElseStatement(elseIf.elsebody);
565         }
566         else
567         {
568             increase;
569 
570             ++this.depth;
571             statement.accept(this);
572             --this.depth;
573         }
574     }
575 
576     override void visit(AST.StaticForeachDeclaration foreachDeclaration)
577     {
578         debug writeln("Static foreach declaration ", foreachDeclaration);
579 
580         stepInStaticDeclaration(foreachDeclaration);
581     }
582 
583     private void stepInStaticDeclaration(T : ASTNode)(T declaration)
584     {
585         increase(max(this.depth, 1));
586 
587         ++this.depth;
588         super.visit(declaration);
589         --this.depth;
590     }
591 
592     override void visit(AST.WhileStatement whileStatement)
593     {
594         debug writeln("while statement ", whileStatement);
595 
596         stepInLoop(whileStatement);
597     }
598 
599     override void visit(AST.DoStatement doStatement)
600     {
601         debug writeln("do statement ", doStatement);
602 
603         stepInLoop(doStatement);
604     }
605 
606     override void visit(AST.ForStatement forStatement)
607     {
608         debug writeln("for statement ", forStatement);
609 
610         stepInLoop(forStatement);
611     }
612 
613     override void visit(AST.ForeachStatement foreachStatement)
614     {
615         debug writeln("foreach statement ", foreachStatement);
616 
617         stepInLoop(foreachStatement);
618     }
619 
620     private void stepInLoop(T : ASTNode)(T statement)
621     {
622          increase(this.depth);
623 
624         ++this.depth;
625         super.visit(statement);
626         --this.depth;
627    }
628 
629     override void visit(AST.Module moduleDeclaration)
630     {
631         debug writeln("Module declaration ", moduleDeclaration);
632 
633         this.source_.moduleName = moduleDeclaration.md is null
634             ? "app"
635             : moduleDeclaration.md.toString.idup;
636 
637         super.visit(moduleDeclaration);
638     }
639 
640     override void visit(AST.CondExp expression)
641     {
642         debug writeln("Ternary operator ", expression);
643 
644         stepInLoop(expression);
645     }
646 
647     override void visit(AST.SwitchStatement statement)
648     {
649         debug writeln("Switch ", statement);
650 
651         stepInLoop(statement);
652     }
653 
654     override void visit(AST.TryCatchStatement statement)
655     {
656         debug writeln("try-catch statement ", statement);
657 
658         if (statement._body)
659         {
660             increase(this.depth);
661 
662             statement._body.accept(this);
663         }
664         foreach (catch_; *statement.catches)
665         {
666             ++this.depth;
667             this.visit(catch_);
668             --this.depth;
669         }
670     }
671 
672     override void visit(AST.BreakStatement statement)
673     {
674         debug writeln("Break ", statement.ident);
675 
676         stepInStatementWithLabel(statement);
677     }
678 
679     private void stepInStatementWithLabel(T : AST.Statement)(T statement)
680     {
681         if (statement.ident !is null)
682         {
683             increase;
684         }
685         super.visit(statement);
686     }
687 
688     override void visit(AST.ContinueStatement statement)
689     {
690         debug writeln("Label ", statement);
691 
692         stepInStatementWithLabel(statement);
693     }
694 
695     override void visit(AST.PostBlitDeclaration declaration)
696     {
697         debug writeln("Blit ", declaration);
698 
699         stepInFunction(declaration);
700     }
701 
702     override void visit(AST.VersionCondition condition)
703     {
704         debug writeln("Version condition ", condition);
705 
706         stepInStaticDeclaration(condition);
707     }
708 }