1 /**
2 Copyright: Copyright (c) 2020, Joakim Brännström. All rights reserved.
3 License: MPL-2
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 
6 This Source Code Form is subject to the terms of the Mozilla Public License,
7 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain
8 one at http://mozilla.org/MPL/2.0/.
9 */
10 module dextool.plugin.mutate.backend.analyze.pass_clang;
11 
12 import logger = std.experimental.logger;
13 import std.algorithm : among, map, sort, filter;
14 import std.array : empty, array;
15 import std.exception : collectException;
16 import std.format : formattedWrite;
17 import std.meta : AliasSeq;
18 import std.typecons : Nullable, scoped;
19 
20 import automem : vector, Vector;
21 import my.gc.refc : RefCounted;
22 
23 import clang.Cursor : Cursor;
24 import clang.Eval : Eval;
25 import clang.Type : Type;
26 import clang.c.Index : CXTypeKind, CXCursorKind, CXEvalResultKind, CXTokenKind;
27 
28 import cpptooling.analyzer.clang.cursor_logger : logNode, mixinNodeLog;
29 
30 import dextool.clang_extensions : getUnderlyingExprNode;
31 
32 import dextool.type : Path, AbsolutePath;
33 
34 import dextool.plugin.mutate.backend.analyze.ast : Interval, Location;
35 import dextool.plugin.mutate.backend.analyze.extensions;
36 import dextool.plugin.mutate.backend.analyze.utility;
37 import dextool.plugin.mutate.backend.interface_ : FilesysIO;
38 import dextool.plugin.mutate.backend.type : Language, SourceLoc, Offset, SourceLocRange;
39 
40 import analyze = dextool.plugin.mutate.backend.analyze.ast;
41 
42 alias accept = dextool.plugin.mutate.backend.analyze.extensions.accept;
43 
44 /** Translate a clang AST to a mutation AST.
45  */
46 RefCounted!(analyze.Ast) toMutateAst(const Cursor root, FilesysIO fio) @trusted {
47     import cpptooling.analyzer.clang.ast : ClangAST;
48 
49     auto svisitor = scoped!BaseVisitor(fio);
50     auto visitor = cast(BaseVisitor) svisitor;
51     auto ast = ClangAST!BaseVisitor(root);
52     ast.accept(visitor);
53     return visitor.ast;
54 }
55 
56 private:
57 
58 struct OperatorCursor {
59     analyze.Expr astOp;
60 
61     // the whole expression
62     analyze.Location exprLoc;
63     DeriveCursorTypeResult exprTy;
64 
65     // the operator itself
66     analyze.Operator operator;
67     analyze.Location opLoc;
68 
69     Cursor lhs;
70     Cursor rhs;
71 
72     /// Add the result to the AST and astOp to the parent.
73     /// astOp is set to have two children, lhs and rhs.
74     void put(analyze.Node parent, ref analyze.Ast ast) @safe {
75         ast.put(astOp, exprLoc);
76         ast.put(operator, opLoc);
77 
78         exprTy.put(ast);
79 
80         if (exprTy.type !is null)
81             ast.put(astOp, exprTy.id);
82 
83         if (exprTy.symbol !is null)
84             ast.put(astOp, exprTy.symId);
85 
86         parent.children ~= astOp;
87     }
88 }
89 
90 Nullable!OperatorCursor operatorCursor(T)(T node) {
91     import dextool.clang_extensions : getExprOperator, OpKind, ValueKind, getUnderlyingExprNode;
92 
93     auto op = getExprOperator(node.cursor);
94     if (!op.isValid)
95         return typeof(return)();
96 
97     auto path = op.cursor.location.path.Path;
98     if (path.empty)
99         return typeof(return)();
100 
101     OperatorCursor res;
102 
103     void sidesPoint() {
104         auto sides = op.sides;
105         if (sides.lhs.isValid) {
106             res.lhs = getUnderlyingExprNode(sides.lhs);
107         }
108         if (sides.rhs.isValid) {
109             res.rhs = getUnderlyingExprNode(sides.rhs);
110         }
111     }
112 
113     // the operator itself
114     void opPoint() {
115         auto loc = op.location;
116         auto sr = loc.spelling;
117         res.operator = new analyze.Operator;
118         res.opLoc = new analyze.Location(path, Interval(sr.offset,
119                 cast(uint)(sr.offset + op.length)), SourceLocRange(SourceLoc(loc.line,
120                 loc.column), SourceLoc(loc.line, cast(uint)(loc.column + op.length))));
121     }
122 
123     // the arguments and the operator
124     void exprPoint() {
125         auto sr = op.cursor.extent;
126         res.exprLoc = new analyze.Location(path, Interval(sr.start.offset,
127                 sr.end.offset), SourceLocRange(SourceLoc(sr.start.line,
128                 sr.start.column), SourceLoc(sr.end.line, sr.end.column)));
129         res.exprTy = deriveCursorType(op.cursor);
130         switch (op.kind) with (OpKind) {
131         case OO_Star: // "*"
132             goto case;
133         case Mul: // "*"
134             res.astOp = new analyze.OpMul;
135             break;
136         case OO_Slash: // "/"
137             goto case;
138         case Div: // "/"
139             res.astOp = new analyze.OpDiv;
140             break;
141         case OO_Percent: // "%"
142             goto case;
143         case Rem: // "%"
144             res.astOp = new analyze.OpMod;
145             break;
146         case OO_Plus: // "+"
147             goto case;
148         case Add: // "+"
149             res.astOp = new analyze.OpAdd;
150             break;
151         case OO_Minus: // "-"
152             goto case;
153         case Sub: // "-"
154             res.astOp = new analyze.OpSub;
155             break;
156         case OO_Less: // "<"
157             goto case;
158         case LT: // "<"
159             res.astOp = new analyze.OpLess;
160             break;
161         case OO_Greater: // ">"
162             goto case;
163         case GT: // ">"
164             res.astOp = new analyze.OpGreater;
165             break;
166         case OO_LessEqual: // "<="
167             goto case;
168         case LE: // "<="
169             res.astOp = new analyze.OpLessEq;
170             break;
171         case OO_GreaterEqual: // ">="
172             goto case;
173         case GE: // ">="
174             res.astOp = new analyze.OpGreaterEq;
175             break;
176         case OO_EqualEqual: // "=="
177             goto case;
178         case EQ: // "=="
179             res.astOp = new analyze.OpEqual;
180             break;
181         case OO_Exclaim: // "!"
182             goto case;
183         case LNot: // "!"
184             res.astOp = new analyze.OpNegate;
185             break;
186         case OO_ExclaimEqual: // "!="
187             goto case;
188         case NE: // "!="
189             res.astOp = new analyze.OpNotEqual;
190             break;
191         case OO_AmpAmp: // "&&"
192             goto case;
193         case LAnd: // "&&"
194             res.astOp = new analyze.OpAnd;
195             break;
196         case OO_PipePipe: // "||"
197             goto case;
198         case LOr: // "||"
199             res.astOp = new analyze.OpOr;
200             break;
201         case OO_Amp: // "&"
202             goto case;
203         case And: // "&"
204             res.astOp = new analyze.OpAndBitwise;
205             break;
206         case OO_Pipe: // "|"
207             goto case;
208         case Or: // "|"
209             res.astOp = new analyze.OpOrBitwise;
210             break;
211         case OO_StarEqual: // "*="
212             goto case;
213         case MulAssign: // "*="
214             res.astOp = new analyze.OpAssignMul;
215             break;
216         case OO_SlashEqual: // "/="
217             goto case;
218         case DivAssign: // "/="
219             res.astOp = new analyze.OpAssignDiv;
220             break;
221         case OO_PercentEqual: // "%="
222             goto case;
223         case RemAssign: // "%="
224             res.astOp = new analyze.OpAssignMod;
225             break;
226         case OO_PlusEqual: // "+="
227             goto case;
228         case AddAssign: // "+="
229             res.astOp = new analyze.OpAssignAdd;
230             break;
231         case OO_MinusEqual: // "-="
232             goto case;
233         case SubAssign: // "-="
234             res.astOp = new analyze.OpAssignSub;
235             break;
236         case OO_AmpEqual: // "&="
237             goto case;
238         case AndAssign: // "&="
239             res.astOp = new analyze.OpAssignAndBitwise;
240             break;
241         case OrAssign: // "|="
242             goto case;
243         case OO_PipeEqual: // "|="
244             res.astOp = new analyze.OpAssignOrBitwise;
245             break;
246         case ShlAssign: // "<<="
247             goto case;
248         case ShrAssign: // ">>="
249             goto case;
250         case XorAssign: // "^="
251             goto case;
252         case OO_CaretEqual: // "^="
253             goto case;
254         case OO_Equal: // "="
255             goto case;
256         case Assign: // "="
257             res.astOp = new analyze.OpAssign;
258             break;
259             //case Xor: // "^"
260             //case OO_Caret: // "^"
261             //case OO_Tilde: // "~"
262         default:
263             res.astOp = new analyze.BinaryOp;
264         }
265     }
266 
267     exprPoint;
268     opPoint;
269     sidesPoint;
270     return typeof(return)(res);
271 }
272 
273 struct CaseStmtCursor {
274     analyze.Location branch;
275     analyze.Location insideBranch;
276 
277     Cursor inner;
278 }
279 
280 Nullable!CaseStmtCursor caseStmtCursor(T)(T node) {
281     import dextool.clang_extensions : getCaseStmt;
282 
283     auto mp = getCaseStmt(node.cursor);
284     if (!mp.isValid)
285         return typeof(return)();
286 
287     auto path = node.cursor.location.path.Path;
288     if (path.empty)
289         return typeof(return)();
290 
291     auto extent = node.cursor.extent;
292 
293     CaseStmtCursor res;
294     res.inner = mp.subStmt;
295 
296     auto sr = res.inner.extent;
297 
298     auto insideLoc = SourceLocRange(SourceLoc(sr.start.line, sr.start.column),
299             SourceLoc(sr.end.line, sr.end.column));
300     auto offs = Interval(sr.start.offset, sr.end.offset);
301     if (res.inner.kind == CXCursorKind.caseStmt) {
302         auto colon = mp.colonLocation;
303         insideLoc.begin = SourceLoc(colon.line, colon.column + 1);
304         insideLoc.end = insideLoc.begin;
305 
306         // a case statement with fallthrough. the only point to inject a bomb
307         // is directly after the semicolon
308         offs.begin = colon.offset + 1;
309         offs.end = offs.begin;
310     } else if (res.inner.kind != CXCursorKind.compoundStmt) {
311         offs.end = findTokenOffset(node.cursor.translationUnit.cursor.tokens,
312                 offs, CXTokenKind.punctuation);
313     }
314 
315     void subStmt() {
316         res.insideBranch = new analyze.Location(path, offs, insideLoc);
317     }
318 
319     void stmt() {
320         auto loc = extent.start;
321         auto loc_end = extent.end;
322         // reuse the end from offs because it covers either only the
323         // fallthrough OR also the end semicolon
324         auto stmt_offs = Interval(extent.start.offset, offs.end);
325         res.branch = new analyze.Location(path, stmt_offs,
326                 SourceLocRange(SourceLoc(loc.line, loc.column),
327                     SourceLoc(loc_end.line, loc_end.column)));
328     }
329 
330     stmt;
331     subStmt;
332     return typeof(return)(res);
333 }
334 
335 @safe:
336 
337 Location toLocation(ref const Cursor c) {
338     auto e = c.extent;
339     auto interval = Interval(e.start.offset, e.end.offset);
340     auto begin = e.start;
341     auto end = e.end;
342     return new Location(e.path.Path, interval, SourceLocRange(SourceLoc(begin.line,
343             begin.column), SourceLoc(end.line, end.column)));
344 }
345 
346 /** Find all mutation points that affect a whole expression.
347  *
348  * TODO change the name of the class. It is more than just an expression
349  * visitor.
350  *
351  * # Usage of kind_stack
352  * All usage of the kind_stack shall be documented here.
353  *  - track assignments to avoid generating unary insert operators for the LHS.
354  */
355 final class BaseVisitor : ExtendedVisitor {
356     import clang.c.Index : CXCursorKind, CXTypeKind;
357     import cpptooling.analyzer.clang.ast;
358     import dextool.clang_extensions : getExprOperator, OpKind;
359 
360     alias visit = ExtendedVisitor.visit;
361 
362     // the depth the visitor is at.
363     uint indent;
364     // A stack of the nodes that are generated up to the current one.
365     Stack!(analyze.Node) nstack;
366 
367     // A stack of visited cursors up to the current one.
368     Stack!(CXCursorKind) cstack;
369 
370     /// The elements that where removed from the last decrement.
371     Vector!(analyze.Node) lastDecr;
372 
373     /// List of macro locations which blacklist mutants that would be injected
374     /// in any of them.
375     BlackList blacklist;
376 
377     RefCounted!(analyze.Ast) ast;
378 
379     FilesysIO fio;
380 
381     this(FilesysIO fio) nothrow {
382         this.fio = fio;
383         this.ast = analyze.Ast.init;
384     }
385 
386     /// Returns: if the previous nodes is a CXCursorKind `k`.
387     bool isDirectParent(CXCursorKind k) {
388         if (cstack.empty)
389             return false;
390         return cstack[$ - 1].data == k;
391     }
392 
393     override void incr() @safe {
394         ++indent;
395         lastDecr.clear;
396     }
397 
398     override void decr() @trusted {
399         --indent;
400         lastDecr = nstack.popUntil(indent);
401         cstack.popUntil(indent);
402     }
403 
404     private void pushStack(analyze.Node n, analyze.Location l, const CXCursorKind cKind) @trusted {
405         n.blacklist = blacklist.inside(l);
406         nstack.put(n, indent);
407         cstack.put(cKind, indent);
408     }
409 
410     /// Returns: true if it is OK to modify the cursor
411     private void pushStack(AstT, ClangT)(AstT n, ClangT c) @trusted {
412         auto loc = c.cursor.toLocation;
413         ast.put(n, loc);
414         nstack.back.children ~= n;
415         pushStack(n, loc, c.kind);
416     }
417 
418     override void visit(const TranslationUnit v) {
419         import clang.c.Index : CXLanguageKind;
420 
421         mixin(mixinNodeLog!());
422 
423         blacklist = BlackList(v.cursor);
424 
425         ast.root = new analyze.TranslationUnit;
426         auto loc = v.cursor.toLocation;
427         ast.put(ast.root, loc);
428         pushStack(ast.root, loc, v.cursor.kind);
429 
430         // it is most often invalid
431         switch (v.cursor.language) {
432         case CXLanguageKind.c:
433             ast.lang = Language.c;
434             break;
435         case CXLanguageKind.cPlusPlus:
436             ast.lang = Language.cpp;
437             break;
438         default:
439             ast.lang = Language.assumeCpp;
440         }
441 
442         v.accept(this);
443     }
444 
445     override void visit(const Attribute v) {
446         mixin(mixinNodeLog!());
447         v.accept(this);
448     }
449 
450     override void visit(const Declaration v) {
451         mixin(mixinNodeLog!());
452         v.accept(this);
453     }
454 
455     override void visit(const VarDecl v) @trusted {
456         mixin(mixinNodeLog!());
457         visitVar(v);
458         v.accept(this);
459     }
460 
461     override void visit(const ParmDecl v) @trusted {
462         mixin(mixinNodeLog!());
463         visitVar(v);
464         v.accept(this);
465     }
466 
467     override void visit(const TemplateTypeParameter v) {
468         mixin(mixinNodeLog!());
469         // block mutants inside template parameters
470     }
471 
472     override void visit(const TemplateTemplateParameter v) {
473         mixin(mixinNodeLog!());
474         // block mutants inside template parameters
475     }
476 
477     override void visit(const NonTypeTemplateParameter v) {
478         mixin(mixinNodeLog!());
479         // block mutants inside template parameters
480     }
481 
482     override void visit(const TypeAliasDecl v) {
483         mixin(mixinNodeLog!());
484         // block mutants inside template parameters
485     }
486 
487     override void visit(const CxxBaseSpecifier v) {
488         mixin(mixinNodeLog!());
489         // block mutants inside template parameters.
490         // the only mutants that are inside an inheritance is template
491         // parameters and such.
492     }
493 
494     private void visitVar(T)(T v) @trusted {
495         auto n = new analyze.VarDecl;
496 
497         auto ty = v.cursor.type;
498         if (ty.isValid) {
499             n.isConst = ty.isConst;
500         }
501 
502         pushStack(n, v);
503     }
504 
505     override void visit(const Directive v) {
506         mixin(mixinNodeLog!());
507         v.accept(this);
508     }
509 
510     override void visit(const Reference v) {
511         mixin(mixinNodeLog!());
512         v.accept(this);
513     }
514 
515     override void visit(const Statement v) {
516         mixin(mixinNodeLog!());
517         v.accept(this);
518     }
519 
520     override void visit(const Expression v) {
521         import cpptooling.analyzer.clang.ast : dispatch;
522         import dextool.clang_extensions : getUnderlyingExprNode;
523 
524         mixin(mixinNodeLog!());
525 
526         auto ue = Cursor(getUnderlyingExprNode(v.cursor));
527         if (ue.isValid && ue != v.cursor) {
528             incr;
529             scope (exit)
530                 decr;
531             dispatch(ue, this);
532         } else {
533             pushStack(new analyze.Expr, v);
534             v.accept(this);
535         }
536     }
537 
538     override void visit(const Preprocessor v) {
539         mixin(mixinNodeLog!());
540 
541         const bool isCpp = v.spelling == "__cplusplus";
542 
543         if (isCpp)
544             ast.lang = Language.cpp;
545         else if (!isCpp && ast.lang != Language.cpp)
546             ast.lang = Language.c;
547 
548         v.accept(this);
549     }
550 
551     override void visit(const EnumDecl v) @trusted {
552         mixin(mixinNodeLog!());
553 
554         import std.typecons : scoped;
555 
556         // extract the boundaries of the enum to update the type db.
557         auto vis = scoped!EnumVisitor(indent);
558         vis.visit(v);
559         ast.types.set(vis.id, vis.toType);
560     }
561 
562     override void visit(const FunctionDecl v) @trusted {
563         mixin(mixinNodeLog!());
564         visitFunc(v);
565     }
566 
567     override void visit(const CxxMethod v) {
568         mixin(mixinNodeLog!());
569 
570         // model C++ methods as functions. It should be enough to know that it
571         // is a function and the return type when generating mutants.
572         visitFunc(v);
573     }
574 
575     override void visit(const BreakStmt v) {
576         mixin(mixinNodeLog!());
577         v.accept(this);
578     }
579 
580     override void visit(const BinaryOperator v) @trusted {
581         mixin(mixinNodeLog!());
582         visitOp(v, v.cursor.kind);
583     }
584 
585     override void visit(const UnaryOperator v) @trusted {
586         mixin(mixinNodeLog!());
587         visitOp(v, v.cursor.kind);
588     }
589 
590     override void visit(const CompoundAssignOperator v) {
591         mixin(mixinNodeLog!());
592         // TODO: implement all aor assignment such as +=
593         pushStack(new analyze.OpAssign, v);
594         v.accept(this);
595     }
596 
597     override void visit(const CallExpr v) {
598         mixin(mixinNodeLog!());
599 
600         if (!visitOp(v, v.cursor.kind)) {
601             pushStack(new analyze.Call, v);
602             v.accept(this);
603         }
604     }
605 
606     override void visit(const CxxThrowExpr v) {
607         mixin(mixinNodeLog!());
608         // model a C++ exception as a return expression because that is
609         // "basically" what happens.
610         pushStack(new analyze.Return, v);
611         v.accept(this);
612     }
613 
614     override void visit(const InitListExpr v) {
615         mixin(mixinNodeLog!());
616         pushStack(new analyze.Constructor, v);
617         v.accept(this);
618     }
619 
620     override void visit(const LambdaExpr v) @trusted {
621         mixin(mixinNodeLog!());
622 
623         // model C++ lambdas as functions. It should be enough to know that it
624         // is a function and the return type when generating mutants.
625         visitFunc(v);
626     }
627 
628     override void visit(const ReturnStmt v) {
629         mixin(mixinNodeLog!());
630         pushStack(new analyze.Return, v);
631         v.accept(this);
632     }
633 
634     override void visit(const CompoundStmt v) {
635         mixin(mixinNodeLog!());
636 
637         if (isDirectParent(CXCursorKind.switchStmt)) {
638             // the CompoundStmt statement {} directly inside a switch statement
639             // isn't useful to manipulate as a block. The useful part is the
640             // smaller blocks that the case and default break down the block
641             // into thus this avoid generating useless blocks that lead to
642             // equivalent or unproductive mutants.
643         } else
644             try {
645                 auto loc = v.cursor.toLocation;
646                 auto fin = fio.makeInput(loc.file);
647 
648                 // a CompoundStmt that represent a "{..}" can for example be the
649                 // body of a function or the block that a try statement encompase.
650                 // The block that can be modified is the inside of it thus the
651                 // location has to be the inside. If this modification to isn't
652                 // done then a SDL can't be generated that delete the inside of
653                 // e.g. void functions.
654                 if (fin.content[loc.interval.begin .. loc.interval.begin + 1] == cast(
655                         const(ubyte)[]) "{") {
656                     const begin = loc.interval.begin + 1;
657                     const end = loc.interval.end - 1;
658                     if (begin < end) {
659                         loc.interval = Interval(begin, end);
660                     }
661 
662                     auto n = new analyze.Block;
663                     ast.put(n, loc);
664                     nstack.back.children ~= n;
665                     pushStack(n, loc, v.cursor.kind);
666                 } else {
667                     pushStack(new analyze.Block, v);
668                 }
669             } catch (Exception e) {
670                 logger.trace(e.msg).collectException;
671             }
672 
673         v.accept(this);
674     }
675 
676     override void visit(const CaseStmt v) {
677         mixin(mixinNodeLog!());
678         visitCaseStmt(v);
679     }
680 
681     override void visit(const DefaultStmt v) {
682         mixin(mixinNodeLog!());
683         visitCaseStmt(v);
684     }
685 
686     override void visit(const ForStmt v) {
687         mixin(mixinNodeLog!());
688         pushStack(new analyze.Loop, v);
689         v.accept(this);
690     }
691 
692     override void visit(const CxxForRangeStmt v) {
693         mixin(mixinNodeLog!());
694         pushStack(new analyze.Loop, v);
695         v.accept(this);
696     }
697 
698     override void visit(const WhileStmt v) {
699         mixin(mixinNodeLog!());
700         pushStack(new analyze.Loop, v);
701         v.accept(this);
702     }
703 
704     override void visit(const DoStmt v) {
705         mixin(mixinNodeLog!());
706         pushStack(new analyze.Loop, v);
707         v.accept(this);
708     }
709 
710     override void visit(const SwitchStmt v) {
711         mixin(mixinNodeLog!());
712         pushStack(new analyze.BranchBundle, v);
713         v.accept(this);
714     }
715 
716     override void visit(const IfStmt v) @trusted {
717         mixin(mixinNodeLog!());
718         pushStack(new analyze.BranchBundle, v);
719         dextool.plugin.mutate.backend.analyze.extensions.accept(v, this);
720     }
721 
722     override void visit(const IfStmtCond v) {
723         mixin(mixinNodeLog!());
724         pushStack(new analyze.Condition, v);
725 
726         incr;
727         scope (exit)
728             decr;
729         if (!visitOp(v, v.cursor.kind)) {
730             v.accept(this);
731         }
732     }
733 
734     override void visit(const IfStmtThen v) {
735         mixin(mixinNodeLog!());
736         pushStack(new analyze.Branch, v);
737         v.accept(this);
738     }
739 
740     override void visit(const IfStmtElse v) {
741         mixin(mixinNodeLog!());
742         pushStack(new analyze.Branch, v);
743         v.accept(this);
744     }
745 
746     private bool visitOp(T)(ref const T v, const CXCursorKind cKind) @trusted {
747         auto op = operatorCursor(v);
748         if (op.isNull) {
749             return false;
750         }
751 
752         if (visitBinaryOp(op.get, cKind))
753             return true;
754         return visitUnaryOp(op.get, cKind);
755     }
756 
757     /// Returns: true if it added a binary operator, false otherwise.
758     private bool visitBinaryOp(ref OperatorCursor op, const CXCursorKind cKind) @trusted {
759         import cpptooling.analyzer.clang.ast : dispatch;
760 
761         auto astOp = cast(analyze.BinaryOp) op.astOp;
762         if (astOp is null)
763             return false;
764 
765         astOp.operator = op.operator;
766         astOp.operator.blacklist = blacklist.inside(op.opLoc);
767 
768         op.put(nstack.back, ast);
769         pushStack(astOp, op.exprLoc, cKind);
770         incr;
771         scope (exit)
772             decr;
773 
774         if (op.lhs.isValid) {
775             incr;
776             scope (exit)
777                 decr;
778             dispatch(op.lhs, this);
779             auto b = () {
780                 if (!lastDecr.empty)
781                     return cast(analyze.Expr) lastDecr[$ - 1];
782                 return null;
783             }();
784             if (b !is null && b != astOp) {
785                 astOp.lhs = b;
786                 auto ty = deriveCursorType(op.lhs);
787                 ty.put(ast);
788                 if (ty.type !is null) {
789                     ast.put(b, ty.id);
790                 }
791                 if (ty.symbol !is null) {
792                     ast.put(b, ty.symId);
793                 }
794             }
795         }
796         if (op.rhs.isValid) {
797             incr;
798             scope (exit)
799                 decr;
800             dispatch(op.rhs, this);
801             auto b = () {
802                 if (!lastDecr.empty)
803                     return cast(analyze.Expr) lastDecr[$ - 1];
804                 return null;
805             }();
806             if (b !is null && b != astOp) {
807                 astOp.rhs = b;
808                 auto ty = deriveCursorType(op.rhs);
809                 ty.put(ast);
810                 if (ty.type !is null) {
811                     ast.put(b, ty.id);
812                 }
813                 if (ty.symbol !is null) {
814                     ast.put(b, ty.symId);
815                 }
816             }
817         }
818 
819         return true;
820     }
821 
822     /// Returns: true if it added a binary operator, false otherwise.
823     private bool visitUnaryOp(ref OperatorCursor op, CXCursorKind cKind) @trusted {
824         import cpptooling.analyzer.clang.ast : dispatch;
825 
826         auto astOp = cast(analyze.UnaryOp) op.astOp;
827         if (astOp is null)
828             return false;
829 
830         astOp.operator = op.operator;
831         astOp.operator.blacklist = blacklist.inside(op.opLoc);
832 
833         op.put(nstack.back, ast);
834         pushStack(astOp, op.exprLoc, cKind);
835         incr;
836         scope (exit)
837             decr;
838 
839         if (op.lhs.isValid) {
840             incr;
841             scope (exit)
842                 decr;
843             dispatch(op.lhs, this);
844             auto b = () {
845                 if (!lastDecr.empty)
846                     return cast(analyze.Expr) lastDecr[$ - 1];
847                 return null;
848             }();
849             if (b !is null && b != astOp) {
850                 astOp.expr = b;
851                 auto ty = deriveCursorType(op.lhs);
852                 ty.put(ast);
853                 if (ty.type !is null) {
854                     ast.put(b, ty.id);
855                 }
856                 if (ty.symbol !is null) {
857                     ast.put(b, ty.symId);
858                 }
859             }
860         } else if (op.rhs.isValid) {
861             incr;
862             scope (exit)
863                 decr;
864             dispatch(op.rhs, this);
865             auto b = () {
866                 if (!lastDecr.empty)
867                     return cast(analyze.Expr) lastDecr[$ - 1];
868                 return null;
869             }();
870             if (b !is null && b != astOp) {
871                 astOp.expr = b;
872                 auto ty = deriveCursorType(op.rhs);
873                 ty.put(ast);
874                 if (ty.type !is null) {
875                     ast.put(b, ty.id);
876                 }
877                 if (ty.symbol !is null) {
878                     ast.put(b, ty.symId);
879                 }
880             }
881         }
882 
883         return true;
884     }
885 
886     private void visitFunc(T)(ref const T v) @trusted {
887         auto loc = v.cursor.toLocation;
888         auto n = new analyze.Function;
889         ast.put(n, loc);
890         nstack.back.children ~= n;
891         pushStack(n, loc, v.cursor.kind);
892 
893         auto fRetval = new analyze.Return;
894         auto rty = deriveType(v.cursor.func.resultType);
895         rty.put(ast);
896         if (rty.type !is null) {
897             ast.put(fRetval, loc);
898             n.return_ = fRetval;
899             ast.put(fRetval, rty.id);
900         }
901         if (rty.symbol !is null) {
902             ast.put(fRetval, rty.symId);
903         }
904 
905         v.accept(this);
906     }
907 
908     private void visitCaseStmt(T)(ref const T v) @trusted {
909         auto res = caseStmtCursor(v);
910         if (res.isNull) {
911             pushStack(new analyze.Block, v);
912             v.accept(this);
913             return;
914         }
915 
916         auto branch = new analyze.Branch;
917         ast.put(branch, res.get.branch);
918         nstack.back.children ~= branch;
919         pushStack(branch, res.get.branch, v.cursor.kind);
920 
921         // create a node depth that diverge from the clang AST wherein the
922         // inside of a case stmt is modelled as a block.
923         incr;
924         scope (exit)
925             decr;
926         auto inner = new analyze.Block;
927         ast.put(inner, res.get.insideBranch);
928         branch.children ~= inner;
929         branch.inside = inner;
930         pushStack(inner, res.get.insideBranch, v.cursor.kind);
931 
932         dispatch(res.get.inner, this);
933     }
934 }
935 
936 final class EnumVisitor : ExtendedVisitor {
937     import std.typecons : Nullable;
938     import cpptooling.analyzer.clang.ast;
939 
940     alias visit = ExtendedVisitor.visit;
941 
942     mixin generateIndentIncrDecr;
943 
944     analyze.TypeId id;
945     Nullable!long minValue;
946     Nullable!long maxValue;
947 
948     this(const uint indent) {
949         this.indent = indent;
950     }
951 
952     override void visit(const EnumDecl v) @trusted {
953         mixin(mixinNodeLog!());
954         id = make!(analyze.TypeId)(v.cursor);
955         v.accept(this);
956     }
957 
958     override void visit(const EnumConstantDecl v) @trusted {
959         mixin(mixinNodeLog!());
960 
961         long value = v.cursor.enum_.signedValue;
962 
963         if (minValue.isNull) {
964             minValue = value;
965             maxValue = value;
966         }
967 
968         if (value < minValue.get)
969             minValue = value;
970         if (value > maxValue.get)
971             maxValue = value;
972 
973         v.accept(this);
974     }
975 
976     analyze.Type toType() const {
977         auto l = () {
978             if (minValue.isNull)
979                 return analyze.Value(analyze.Value.NegInf.init);
980             return analyze.Value(analyze.Value.Int(minValue.get));
981         }();
982         auto u = () {
983             if (maxValue.isNull)
984                 return analyze.Value(analyze.Value.PosInf.init);
985             return analyze.Value(analyze.Value.Int(maxValue.get));
986         }();
987 
988         return new analyze.DiscreteType(analyze.Range(l, u));
989     }
990 }
991 
992 enum discreteCategory = AliasSeq!(CXTypeKind.charU, CXTypeKind.uChar, CXTypeKind.char16,
993             CXTypeKind.char32, CXTypeKind.uShort, CXTypeKind.uInt, CXTypeKind.uLong, CXTypeKind.uLongLong,
994             CXTypeKind.uInt128, CXTypeKind.charS, CXTypeKind.sChar, CXTypeKind.wChar, CXTypeKind.short_,
995             CXTypeKind.int_, CXTypeKind.long_, CXTypeKind.longLong,
996             CXTypeKind.int128, CXTypeKind.enum_,);
997 enum floatCategory = AliasSeq!(CXTypeKind.float_, CXTypeKind.double_,
998             CXTypeKind.longDouble, CXTypeKind.float128, CXTypeKind.half, CXTypeKind.float16,);
999 enum pointerCategory = AliasSeq!(CXTypeKind.nullPtr, CXTypeKind.pointer,
1000             CXTypeKind.blockPointer, CXTypeKind.memberPointer);
1001 enum boolCategory = AliasSeq!(CXTypeKind.bool_);
1002 
1003 struct DeriveTypeResult {
1004     analyze.TypeId id;
1005     analyze.Type type;
1006     analyze.SymbolId symId;
1007     analyze.Symbol symbol;
1008 
1009     void put(ref analyze.Ast ast) @safe {
1010         if (type !is null) {
1011             ast.types.require(id, type);
1012         }
1013         if (symbol !is null) {
1014             ast.symbols.require(symId, symbol);
1015         }
1016     }
1017 }
1018 
1019 DeriveTypeResult deriveType(Type cty) {
1020     DeriveTypeResult rval;
1021 
1022     auto ctydecl = cty.declaration;
1023     if (ctydecl.isValid) {
1024         rval.id = make!(analyze.TypeId)(ctydecl);
1025     } else {
1026         rval.id = make!(analyze.TypeId)(cty.cursor);
1027     }
1028 
1029     if (cty.isEnum) {
1030         rval.type = new analyze.DiscreteType(analyze.Range.makeInf);
1031         if (!cty.isSigned) {
1032             rval.type.range.low = analyze.Value(analyze.Value.Int(0));
1033         }
1034     } else if (cty.kind.among(floatCategory)) {
1035         rval.type = new analyze.ContinuesType(analyze.Range.makeInf);
1036     } else if (cty.kind.among(pointerCategory)) {
1037         rval.type = new analyze.UnorderedType(analyze.Range.makeInf);
1038     } else if (cty.kind.among(boolCategory)) {
1039         rval.type = new analyze.BooleanType(analyze.Range.makeBoolean);
1040     } else if (cty.kind.among(discreteCategory)) {
1041         rval.type = new analyze.DiscreteType(analyze.Range.makeInf);
1042         if (!cty.isSigned) {
1043             rval.type.range.low = analyze.Value(analyze.Value.Int(0));
1044         }
1045     }
1046 
1047     return rval;
1048 }
1049 
1050 struct DeriveCursorTypeResult {
1051     Cursor expr;
1052     DeriveTypeResult typeResult;
1053     alias typeResult this;
1054 }
1055 
1056 /** Analyze a cursor to derive the type of it and if it has a concrete value
1057  * and what it is in that case.
1058  *
1059  * This is intended for expression nodes in the clang AST.
1060  */
1061 DeriveCursorTypeResult deriveCursorType(const Cursor baseCursor) {
1062     auto c = Cursor(getUnderlyingExprNode(baseCursor));
1063     if (!c.isValid)
1064         return DeriveCursorTypeResult.init;
1065 
1066     auto rval = DeriveCursorTypeResult(c);
1067     auto cty = c.type.canonicalType;
1068     rval.typeResult = deriveType(cty);
1069 
1070     // evaluate the cursor to add a value for the symbol
1071     void eval(const ref Eval e) {
1072         if (!e.kind.among(CXEvalResultKind.int_))
1073             return;
1074 
1075         const long value = () {
1076             if (e.isUnsignedInt) {
1077                 const v = e.asUnsigned;
1078                 if (v < long.max)
1079                     return cast(long) v;
1080             }
1081             return e.asLong;
1082         }();
1083 
1084         rval.symId = make!(analyze.SymbolId)(c);
1085         rval.symbol = new analyze.DiscretSymbol(analyze.Value(analyze.Value.Int(value)));
1086     }
1087 
1088     if (cty.isEnum) {
1089         // TODO: check if c.eval give the same result. If so it may be easier
1090         // to remove this special case of an enum because it is covered by the
1091         // generic branch for discretes.
1092 
1093         auto ctydecl = cty.declaration;
1094         if (!ctydecl.isValid)
1095             return rval;
1096 
1097         const cref = c.referenced;
1098         if (!cref.isValid)
1099             return rval;
1100 
1101         if (cref.kind == CXCursorKind.enumConstantDecl) {
1102             const long value = cref.enum_.signedValue;
1103             rval.symId = make!(analyze.SymbolId)(c);
1104             rval.symbol = new analyze.DiscretSymbol(analyze.Value(analyze.Value.Int(value)));
1105         }
1106     } else if (cty.kind.among(discreteCategory)) {
1107         // crashes in clang 7.x. Investigate why.
1108         //const e = c.eval;
1109         //if (e.isValid)
1110         //    eval(e);
1111     }
1112 
1113     return rval;
1114 }
1115 
1116 auto make(T)(const Cursor c) if (is(T == analyze.TypeId) || is(T == analyze.SymbolId)) {
1117     const usr = c.usr;
1118     if (usr.empty) {
1119         return T(c.toHash);
1120     }
1121     return analyze.makeId!T(usr);
1122 }
1123 
1124 /// trusted: trusting the impl in clang.
1125 uint findTokenOffset(T)(T toks, Offset sr, CXTokenKind kind) @trusted {
1126     foreach (ref t; toks) {
1127         if (t.location.offset >= sr.end) {
1128             if (t.kind == kind) {
1129                 return t.extent.end.offset;
1130             }
1131             break;
1132         }
1133     }
1134 
1135     return sr.end;
1136 }
1137 
1138 /** Create an index of all macros that then can be queried to see if a Cursor
1139  * or Interval overlap a macro.
1140  */
1141 struct BlackList {
1142     import dextool.plugin.mutate.backend.analyze.utility : Index;
1143 
1144     Index!string macros;
1145 
1146     this(const Cursor root) {
1147         Interval[][string] macros;
1148 
1149         foreach (c, parent; root.all) {
1150             if (c.kind != CXCursorKind.macroExpansion || c.isMacroBuiltin)
1151                 continue;
1152 
1153             auto spelling = c.spelling;
1154             // C code almost always implement these as macros. They should not
1155             // be blocked from being mutated.
1156             if (spelling.among("bool", "TRUE", "FALSE"))
1157                 continue;
1158 
1159             const file = c.location.path;
1160             const e = c.extent;
1161             const interval = Interval(e.start.offset, e.end.offset);
1162             if (auto v = file in macros) {
1163                 (*v) ~= interval;
1164             } else {
1165                 macros[file] = [interval];
1166             }
1167         }
1168 
1169         foreach (k; macros.byKey) {
1170             macros[k] = macros[k].sort.array;
1171         }
1172 
1173         this.macros = Index!string(macros);
1174     }
1175 
1176     bool inside(const Cursor c) {
1177         const file = c.location.path.Path;
1178         const e = c.extent;
1179         const interval = Interval(e.start.offset, e.end.offset);
1180         return inside(file, interval);
1181     }
1182 
1183     bool inside(analyze.Location l) {
1184         return inside(l.file, l.interval);
1185     }
1186 
1187     /**
1188      * assuming that an invalid mutant is always inside a macro thus only
1189      * checking if the `i` is inside. Removing a "block" of code that happens
1190      * to contain a macro is totally "ok". It doesn't create any problem.
1191      *
1192      * Returns: true if `i` is inside a macro interval.
1193      */
1194     bool inside(const Path file, const Interval i) {
1195         return macros.inside(file, i);
1196     }
1197 }