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