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, appender, Appender;
15 import std.exception : collectException;
16 import std.format : formattedWrite;
17 import std.meta : AliasSeq;
18 import std.typecons : Nullable;
19 
20 import blob_model : Blob;
21 import my.container.vector : vector, Vector;
22 import my.gc.refc : RefCounted;
23 import my.optional;
24 
25 import clang.Cursor : Cursor;
26 import clang.Eval : Eval;
27 import clang.Type : Type;
28 import clang.c.Index : CXTypeKind, CXCursorKind, CXEvalResultKind, CXTokenKind;
29 
30 import libclang_ast.ast : Visitor;
31 import libclang_ast.cursor_logger : logNode, mixinNodeLog;
32 
33 import dextool.clang_extensions : getUnderlyingExprNode;
34 
35 import dextool.type : Path, AbsolutePath;
36 
37 import dextool.plugin.mutate.backend.analyze.ast : Interval, Location, TypeKind,
38     Node, Ast, BreathFirstRange;
39 import dextool.plugin.mutate.backend.analyze.extensions;
40 import dextool.plugin.mutate.backend.analyze.utility;
41 import dextool.plugin.mutate.backend.interface_ : FilesysIO, InvalidPathException;
42 import dextool.plugin.mutate.backend.type : Language, SourceLoc, Offset, SourceLocRange;
43 
44 import analyze = dextool.plugin.mutate.backend.analyze.ast;
45 
46 alias accept = dextool.plugin.mutate.backend.analyze.extensions.accept;
47 
48 /** Translate a clang AST to a mutation AST.
49  */
50 ClangResult toMutateAst(const Cursor root, FilesysIO fio) @safe {
51     import libclang_ast.ast;
52 
53     auto visitor = new BaseVisitor(fio);
54     scope (exit)
55         visitor.dispose;
56     auto ast = ClangAST!BaseVisitor(root);
57     ast.accept(visitor);
58     visitor.ast.releaseCache;
59 
60     auto rval = ClangResult(visitor.ast, visitor.includes.data);
61     return rval;
62 }
63 
64 struct ClangResult {
65     RefCounted!(analyze.Ast) ast;
66 
67     /// All dependencies that the root has.
68     Path[] dependencies;
69 }
70 
71 private:
72 
73 struct OperatorCursor {
74     analyze.Expr astOp;
75 
76     // true if the operator is overloaded.
77     bool isOverload;
78 
79     // the whole expression
80     analyze.Location exprLoc;
81     DeriveCursorTypeResult exprTy;
82 
83     // the operator itself
84     analyze.Operator operator;
85     analyze.Location opLoc;
86 
87     Cursor lhs;
88     Cursor rhs;
89 
90     /// Add the result to the AST and astOp to the parent.
91     /// astOp is set to have two children, lhs and rhs.
92     void put(analyze.Node parent, ref analyze.Ast ast) @safe {
93         ast.put(astOp, exprLoc);
94         ast.put(operator, opLoc);
95 
96         exprTy.put(ast);
97 
98         if (exprTy.type !is null)
99             ast.put(astOp, exprTy.id);
100 
101         if (exprTy.symbol !is null)
102             ast.put(astOp, exprTy.symId);
103 
104         parent.children ~= astOp;
105     }
106 }
107 
108 Nullable!OperatorCursor operatorCursor(T)(ref Ast ast, T node) {
109     import dextool.clang_extensions : getExprOperator, OpKind, ValueKind, getUnderlyingExprNode;
110 
111     auto op = getExprOperator(node.cursor);
112     if (!op.isValid)
113         return typeof(return)();
114 
115     auto path = op.cursor.location.path.Path;
116     if (path.empty)
117         return typeof(return)();
118 
119     OperatorCursor res;
120 
121     void sidesPoint() {
122         auto sides = op.sides;
123         if (sides.lhs.isValid) {
124             res.lhs = getUnderlyingExprNode(sides.lhs);
125         }
126         if (sides.rhs.isValid) {
127             res.rhs = getUnderlyingExprNode(sides.rhs);
128         }
129     }
130 
131     // the operator itself
132     void opPoint() {
133         auto loc = op.location;
134         auto sr = loc.spelling;
135         res.operator = ast.make!(analyze.Operator);
136         res.opLoc = analyze.Location(path, Interval(sr.offset,
137                 cast(uint)(sr.offset + op.length)), SourceLocRange(SourceLoc(loc.line,
138                 loc.column), SourceLoc(loc.line, cast(uint)(loc.column + op.length))));
139     }
140 
141     // the arguments and the operator
142     void exprPoint() {
143         auto sr = op.cursor.extent;
144         res.exprLoc = analyze.Location(path, Interval(sr.start.offset,
145                 sr.end.offset), SourceLocRange(SourceLoc(sr.start.line,
146                 sr.start.column), SourceLoc(sr.end.line, sr.end.column)));
147         res.exprTy = deriveCursorType(ast, op.cursor);
148         switch (op.kind) with (OpKind) {
149         case OO_Star: // "*"
150             res.isOverload = true;
151             goto case;
152         case Mul: // "*"
153             res.astOp = ast.make!(analyze.OpMul);
154             break;
155         case OO_Slash: // "/"
156             res.isOverload = true;
157             goto case;
158         case Div: // "/"
159             res.astOp = ast.make!(analyze.OpDiv);
160             break;
161         case OO_Percent: // "%"
162             res.isOverload = true;
163             goto case;
164         case Rem: // "%"
165             res.astOp = ast.make!(analyze.OpMod);
166             break;
167         case OO_Plus: // "+"
168             res.isOverload = true;
169             goto case;
170         case Add: // "+"
171             res.astOp = ast.make!(analyze.OpAdd);
172             break;
173         case OO_Minus: // "-"
174             res.isOverload = true;
175             goto case;
176         case Sub: // "-"
177             res.astOp = ast.make!(analyze.OpSub);
178             break;
179         case OO_Less: // "<"
180             res.isOverload = true;
181             goto case;
182         case LT: // "<"
183             res.astOp = ast.make!(analyze.OpLess);
184             break;
185         case OO_Greater: // ">"
186             res.isOverload = true;
187             goto case;
188         case GT: // ">"
189             res.astOp = ast.make!(analyze.OpGreater);
190             break;
191         case OO_LessEqual: // "<="
192             res.isOverload = true;
193             goto case;
194         case LE: // "<="
195             res.astOp = ast.make!(analyze.OpLessEq);
196             break;
197         case OO_GreaterEqual: // ">="
198             res.isOverload = true;
199             goto case;
200         case GE: // ">="
201             res.astOp = ast.make!(analyze.OpGreaterEq);
202             break;
203         case OO_EqualEqual: // "=="
204             res.isOverload = true;
205             goto case;
206         case EQ: // "=="
207             res.astOp = ast.make!(analyze.OpEqual);
208             break;
209         case OO_Exclaim: // "!"
210             res.isOverload = true;
211             goto case;
212         case LNot: // "!"
213             res.astOp = ast.make!(analyze.OpNegate);
214             break;
215         case OO_ExclaimEqual: // "!="
216             res.isOverload = true;
217             goto case;
218         case NE: // "!="
219             res.astOp = ast.make!(analyze.OpNotEqual);
220             break;
221         case OO_AmpAmp: // "&&"
222             res.isOverload = true;
223             goto case;
224         case LAnd: // "&&"
225             res.astOp = ast.make!(analyze.OpAnd);
226             break;
227         case OO_PipePipe: // "||"
228             res.isOverload = true;
229             goto case;
230         case LOr: // "||"
231             res.astOp = ast.make!(analyze.OpOr);
232             break;
233         case OO_Amp: // "&"
234             res.isOverload = true;
235             goto case;
236         case And: // "&"
237             res.astOp = ast.make!(analyze.OpAndBitwise);
238             break;
239         case OO_Pipe: // "|"
240             res.isOverload = true;
241             goto case;
242         case Or: // "|"
243             res.astOp = ast.make!(analyze.OpOrBitwise);
244             break;
245         case OO_StarEqual: // "*="
246             res.isOverload = true;
247             goto case;
248         case MulAssign: // "*="
249             res.astOp = ast.make!(analyze.OpAssignMul);
250             break;
251         case OO_SlashEqual: // "/="
252             res.isOverload = true;
253             goto case;
254         case DivAssign: // "/="
255             res.astOp = ast.make!(analyze.OpAssignDiv);
256             break;
257         case OO_PercentEqual: // "%="
258             res.isOverload = true;
259             goto case;
260         case RemAssign: // "%="
261             res.astOp = ast.make!(analyze.OpAssignMod);
262             break;
263         case OO_PlusEqual: // "+="
264             res.isOverload = true;
265             goto case;
266         case AddAssign: // "+="
267             res.astOp = ast.make!(analyze.OpAssignAdd);
268             break;
269         case OO_MinusEqual: // "-="
270             res.isOverload = true;
271             goto case;
272         case SubAssign: // "-="
273             res.astOp = ast.make!(analyze.OpAssignSub);
274             break;
275         case OO_AmpEqual: // "&="
276             res.isOverload = true;
277             goto case;
278         case AndAssign: // "&="
279             res.astOp = ast.make!(analyze.OpAssignAndBitwise);
280             break;
281         case OO_PipeEqual: // "|="
282             res.isOverload = true;
283             goto case;
284         case OrAssign: // "|="
285             res.astOp = ast.make!(analyze.OpAssignOrBitwise);
286             break;
287         case OO_CaretEqual: // "^="
288             res.isOverload = true;
289             goto case;
290         case OO_Equal: // "="
291             goto case;
292         case ShlAssign: // "<<="
293             goto case;
294         case ShrAssign: // ">>="
295             goto case;
296         case XorAssign: // "^="
297             goto case;
298         case Assign: // "="
299             res.astOp = ast.make!(analyze.OpAssign);
300             break;
301             //case Xor: // "^"
302             //case OO_Caret: // "^"
303             //case OO_Tilde: // "~"
304         default:
305             res.astOp = ast.make!(analyze.BinaryOp);
306         }
307     }
308 
309     exprPoint;
310     opPoint;
311     sidesPoint;
312     return typeof(return)(res);
313 }
314 
315 @safe:
316 
317 Location toLocation(ref const Cursor c) {
318     auto e = c.extent;
319     auto interval = Interval(e.start.offset, e.end.offset);
320     auto begin = e.start;
321     auto end = e.end;
322     return Location(e.path.Path, interval, SourceLocRange(SourceLoc(begin.line,
323             begin.column), SourceLoc(end.line, end.column)));
324 }
325 
326 /** Find all mutation points that affect a whole expression.
327  *
328  * TODO change the name of the class. It is more than just an expression
329  * visitor.
330  *
331  * # Usage of kind_stack
332  * All usage of the kind_stack shall be documented here.
333  *  - track assignments to avoid generating unary insert operators for the LHS.
334  */
335 final class BaseVisitor : ExtendedVisitor {
336     import clang.c.Index : CXCursorKind, CXTypeKind;
337     import libclang_ast.ast;
338     import dextool.clang_extensions : getExprOperator, OpKind;
339     import my.set;
340 
341     alias visit = ExtendedVisitor.visit;
342 
343     // the depth the visitor is at.
344     uint indent;
345     // A stack of the nodes that are generated up to the current one.
346     Stack!(analyze.Node) nstack;
347 
348     // A stack of visited cursors up to the current one.
349     Stack!(CXCursorKind) cstack;
350 
351     /// The elements that where removed from the last decrement.
352     Vector!(analyze.Node) lastDecr;
353 
354     /// List of macro locations which blacklist mutants that would be injected
355     /// in any of them.
356     BlackList blacklist;
357 
358     RefCounted!(analyze.Ast) ast;
359     Appender!(Path[]) includes;
360 
361     /// Keep track of visited nodes to avoid circulare references.
362     Set!size_t isVisited;
363 
364     FilesysIO fio;
365 
366     this(FilesysIO fio) nothrow {
367         this.fio = fio;
368         this.ast = analyze.Ast.init;
369     }
370 
371     void dispose() {
372         ast.release;
373     }
374 
375     /// Returns: the depth (1+) if any of the parent nodes is `k`.
376     uint isParent(K...)(auto ref K k) {
377         return cstack.isParent(k);
378     }
379 
380     /// Returns: if the previous nodes is a CXCursorKind `k`.
381     bool isDirectParent(CXCursorKind k) {
382         if (cstack.empty)
383             return false;
384         return cstack[$ - 1].data == k;
385     }
386 
387     override void incr() @safe {
388         ++indent;
389         lastDecr.clear;
390     }
391 
392     override void decr() @trusted {
393         --indent;
394         lastDecr = nstack.popUntil(indent);
395         cstack.popUntil(indent);
396     }
397 
398     private void pushStack(analyze.Node n, analyze.Location l, const CXCursorKind cKind) @trusted {
399         n.blacklist = n.blacklist || blacklist.inside(l);
400         n.schemaBlacklist = n.blacklist || n.schemaBlacklist;
401         if (!nstack.empty)
402             n.schemaBlacklist = n.schemaBlacklist || nstack[$ - 1].data.schemaBlacklist;
403         nstack.put(n, indent);
404         cstack.put(cKind, indent);
405         ast.put(n, l);
406     }
407 
408     /// Returns: true if it is OK to modify the cursor
409     private void pushStack(AstT, ClangT)(AstT n, ClangT c) @trusted {
410         static if (is(ClangT == Cursor))
411             auto loc = c.toLocation;
412         else
413             auto loc = c.cursor.toLocation;
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         ast.root = ast.make!(analyze.TranslationUnit);
424         auto loc = v.cursor.toLocation;
425         pushStack(ast.root, loc, v.cursor.kind);
426 
427         blacklist = BlackList(v.cursor);
428 
429         // it is most often invalid
430         switch (v.cursor.language) {
431         case CXLanguageKind.c:
432             ast.lang = Language.c;
433             break;
434         case CXLanguageKind.cPlusPlus:
435             ast.lang = Language.cpp;
436             break;
437         default:
438             ast.lang = Language.assumeCpp;
439         }
440 
441         v.accept(this);
442     }
443 
444     override void visit(const Attribute v) {
445         mixin(mixinNodeLog!());
446         v.accept(this);
447     }
448 
449     override void visit(const Declaration v) {
450         mixin(mixinNodeLog!());
451         v.accept(this);
452     }
453 
454     override void visit(const VarDecl v) @trusted {
455         mixin(mixinNodeLog!());
456         visitVar(v);
457         v.accept(this);
458     }
459 
460     override void visit(const ParmDecl v) @trusted {
461         mixin(mixinNodeLog!());
462         visitVar(v);
463         v.accept(this);
464     }
465 
466     override void visit(const DeclStmt v) {
467         mixin(mixinNodeLog!());
468 
469         // this, in clang-11, is one of the patterns in the AST when struct
470         // binding is used:
471         // declStmt
472         //   | unexposedDecl
473         //     | unexposedDecl
474         //       | unexposedDecl
475         //       | unexposedDecl
476         //       | unexposedDecl
477         //       | unexposedDecl
478         //         | callExpr
479         // it can't be assumed to be the only one.
480         // by injecting a Poision node for a DeclStmt it signal that there are
481         // hidden traps thus any mutation and schemata should be careful.
482         auto n = ast.make!(analyze.Poision);
483         pushStack(n, v);
484 
485         v.accept(this);
486     }
487 
488     override void visit(const ClassTemplate v) {
489         mixin(mixinNodeLog!());
490         // by adding the node it is possible to search for it in cstack
491         auto n = ast.make!(analyze.Poision);
492         pushStack(n, v);
493         v.accept(this);
494     }
495 
496     override void visit(const ClassTemplatePartialSpecialization v) {
497         mixin(mixinNodeLog!());
498         // by adding the node it is possible to search for it in cstack
499         auto n = ast.make!(analyze.Poision);
500         pushStack(n, v);
501         v.accept(this);
502     }
503 
504     override void visit(const FunctionTemplate v) {
505         mixin(mixinNodeLog!());
506         auto n = ast.make!(analyze.Function);
507         // it is too uncertain to inject mutant schematan inside a template
508         // because the types are not known which lead to a high probability
509         // that the schemata code will fail to compile.
510         n.schemaBlacklist = true;
511         pushStack(n, v);
512 
513         v.accept(this);
514     }
515 
516     override void visit(const TemplateTypeParameter v) {
517         mixin(mixinNodeLog!());
518         // block mutants inside template parameters
519     }
520 
521     override void visit(const TemplateTemplateParameter v) {
522         mixin(mixinNodeLog!());
523         // block mutants inside template parameters
524     }
525 
526     override void visit(const NonTypeTemplateParameter v) {
527         mixin(mixinNodeLog!());
528         // block mutants inside template parameters
529     }
530 
531     override void visit(const TypeAliasDecl v) {
532         mixin(mixinNodeLog!());
533         // block mutants inside template parameters
534     }
535 
536     override void visit(const CxxBaseSpecifier v) {
537         mixin(mixinNodeLog!());
538         // block mutants inside template parameters.
539         // the only mutants that are inside an inheritance is template
540         // parameters and such.
541     }
542 
543     private void visitVar(T)(T v) @trusted {
544         pushStack(ast.make!(analyze.VarDecl), v);
545     }
546 
547     override void visit(const Directive v) {
548         mixin(mixinNodeLog!());
549         v.accept(this);
550     }
551 
552     override void visit(const InclusionDirective v) @trusted {
553         import std.file : exists;
554         import std.path : buildPath, dirName;
555 
556         mixin(mixinNodeLog!());
557 
558         const spell = v.spelling;
559         const file = v.cursor.location.file.name;
560         const p = buildPath(file.dirName, spell);
561         if (exists(p))
562             includes.put(Path(p));
563         else
564             includes.put(Path(spell));
565 
566         v.accept(this);
567     }
568 
569     override void visit(const Reference v) {
570         mixin(mixinNodeLog!());
571         v.accept(this);
572     }
573 
574     // TODO overlapping logic with Expression. deduplicate
575     override void visit(const DeclRefExpr v) @trusted {
576         import libclang_ast.ast : dispatch;
577         import clang.SourceRange : intersects;
578 
579         mixin(mixinNodeLog!());
580 
581         if (v.cursor.toHash in isVisited)
582             return;
583         isVisited.add(v.cursor.toHash);
584 
585         auto n = ast.make!(analyze.Expr);
586         n.schemaBlacklist = isParent(CXCursorKind.classTemplate,
587                 CXCursorKind.classTemplatePartialSpecialization, CXCursorKind.functionTemplate) != 0;
588 
589         auto ue = deriveCursorType(ast, v.cursor);
590         ue.put(ast);
591         if (ue.type !is null) {
592             ast.put(n, ue.id);
593         }
594 
595         // only deref a node which is a self-reference
596         auto r = v.cursor.referenced;
597         if (r.isValid && r != v.cursor && intersects(v.cursor.extent, r.extent)
598                 && r.toHash !in isVisited) {
599             isVisited.add(r.toHash);
600             pushStack(n, v);
601 
602             incr;
603             scope (exit)
604                 decr;
605             dispatch(r, this);
606         } else if (ue.expr.isValid && ue.expr != v.cursor && ue.expr.toHash !in isVisited) {
607             isVisited.add(ue.expr.toHash);
608             pushStack(n, ue.expr);
609 
610             incr;
611             scope (exit)
612                 decr;
613             dispatch(ue.expr, this);
614         } else {
615             pushStack(n, v);
616             v.accept(this);
617         }
618     }
619 
620     override void visit(const Statement v) {
621         mixin(mixinNodeLog!());
622         v.accept(this);
623     }
624 
625     override void visit(const ArraySubscriptExpr v) {
626         mixin(mixinNodeLog!());
627         // block schematan inside subscripts because some lead to compilation
628         // errors. Need to investigate more to understand why and how to avoid.
629         // For now they are blocked.
630         auto n = ast.make!(analyze.Poision);
631         n.schemaBlacklist = true;
632         pushStack(n, v);
633         v.accept(this);
634     }
635 
636     override void visit(const Expression v) {
637         mixin(mixinNodeLog!());
638 
639         if (v.cursor.toHash in isVisited)
640             return;
641         isVisited.add(v.cursor.toHash);
642 
643         auto n = ast.make!(analyze.Expr);
644         n.schemaBlacklist = isParent(CXCursorKind.classTemplate,
645                 CXCursorKind.classTemplatePartialSpecialization, CXCursorKind.functionTemplate) != 0;
646 
647         auto ue = deriveCursorType(ast, v.cursor);
648         ue.put(ast);
649         if (ue.type !is null) {
650             ast.put(n, ue.id);
651         }
652 
653         pushStack(n, v);
654         v.accept(this);
655     }
656 
657     override void visit(const Preprocessor v) {
658         mixin(mixinNodeLog!());
659 
660         const bool isCpp = v.spelling == "__cplusplus";
661 
662         if (isCpp)
663             ast.lang = Language.cpp;
664         else if (!isCpp && ast.lang != Language.cpp)
665             ast.lang = Language.c;
666 
667         v.accept(this);
668     }
669 
670     override void visit(const EnumDecl v) @trusted {
671         mixin(mixinNodeLog!());
672 
673         // extract the boundaries of the enum to update the type db.
674         scope vis = new EnumVisitor(ast.get, indent);
675         vis.visit(v);
676         ast.types.set(vis.id, vis.toType);
677     }
678 
679     override void visit(const FunctionDecl v) @trusted {
680         mixin(mixinNodeLog!());
681         visitFunc(v);
682     }
683 
684     override void visit(const Constructor v) @trusted {
685         mixin(mixinNodeLog!());
686 
687         auto n = ast.make!(analyze.Poision);
688         n.schemaBlacklist = isConstExpr(v.cursor);
689         pushStack(n, v);
690 
691         // skip all "= default"
692         if (!v.cursor.isDefaulted)
693             v.accept(this);
694     }
695 
696     override void visit(const Destructor v) @trusted {
697         mixin(mixinNodeLog!());
698 
699         auto n = ast.make!(analyze.Poision);
700         n.schemaBlacklist = isConstExpr(v.cursor);
701         pushStack(n, v);
702 
703         // skip all "= default"
704         if (!v.cursor.isDefaulted)
705             v.accept(this);
706         // TODO: no test covers this case where = default is used for a
707         // destructor. For some versions of clang a CompoundStmt is generated
708     }
709 
710     override void visit(const CxxMethod v) {
711         mixin(mixinNodeLog!());
712 
713         // model C++ methods as functions. It should be enough to know that it
714         // is a function and the return type when generating mutants.
715 
716         // skip all "= default"
717         if (!v.cursor.isDefaulted)
718             visitFunc(v);
719     }
720 
721     override void visit(const BreakStmt v) {
722         mixin(mixinNodeLog!());
723         v.accept(this);
724     }
725 
726     override void visit(const BinaryOperator v) @trusted {
727         mixin(mixinNodeLog!());
728 
729         visitOp(v, v.cursor.kind);
730     }
731 
732     override void visit(const UnaryOperator v) @trusted {
733         mixin(mixinNodeLog!());
734         visitOp(v, v.cursor.kind);
735     }
736 
737     override void visit(const CompoundAssignOperator v) {
738         mixin(mixinNodeLog!());
739         // TODO: implement all aor assignment such as +=
740         pushStack(ast.make!(analyze.OpAssign), v);
741         v.accept(this);
742     }
743 
744     override void visit(const CallExpr v) {
745         mixin(mixinNodeLog!());
746 
747         if (!visitOp(v, v.cursor.kind)) {
748             visitCall(v);
749         }
750     }
751 
752     override void visit(const CxxThrowExpr v) {
753         mixin(mixinNodeLog!());
754         // model a C++ exception as a return expression because that is
755         // "basically" what happens.
756         auto n = ast.make!(analyze.Return);
757         n.blacklist = true;
758         pushStack(n, v);
759         v.accept(this);
760     }
761 
762     override void visit(const InitListExpr v) {
763         mixin(mixinNodeLog!());
764         pushStack(ast.make!(analyze.Constructor), v);
765         v.accept(this);
766     }
767 
768     override void visit(const LambdaExpr v) {
769         mixin(mixinNodeLog!());
770 
771         // model C++ lambdas as functions. It should be enough to know that it
772         // is a function and the return type when generating mutants.
773         visitFunc(v);
774     }
775 
776     override void visit(const ReturnStmt v) {
777         mixin(mixinNodeLog!());
778         pushStack(ast.make!(analyze.Return), v);
779         v.accept(this);
780     }
781 
782     override void visit(const CompoundStmt v) {
783         import std.algorithm : min;
784 
785         mixin(mixinNodeLog!());
786 
787         static uint findBraketOffset(Blob file, const uint begin, const uint end, const ubyte letter) {
788             for (uint i = begin; i < end; ++i) {
789                 if (file.content[i] == letter) {
790                     return i;
791                 }
792             }
793             return begin;
794         }
795 
796         if (isDirectParent(CXCursorKind.switchStmt)) {
797             // the CompoundStmt statement {} directly inside a switch statement
798             // isn't useful to manipulate as a block. The useful part is the
799             // smaller blocks that the case and default break down the block
800             // into thus this avoid generating useless blocks that lead to
801             // equivalent or unproductive mutants.
802         } else
803             try {
804                 auto loc = v.cursor.toLocation;
805                 auto file = fio.makeInput(loc.file);
806                 const maxEnd = file.content.length;
807 
808                 // The block that can be modified is the inside of it thus the
809                 // a CompoundStmt that represent a "{..}" can for example be the
810                 // body of a function or the block that a try statement encompase.
811                 // done then a SDL can't be generated that delete the inside of
812                 // e.g. void functions.
813 
814                 auto end = min(findBraketOffset(file, loc.interval.end == 0
815                         ? loc.interval.end : loc.interval.end - 1, cast(uint) maxEnd,
816                         cast(ubyte) '}'), maxEnd);
817                 auto begin = findBraketOffset(file, loc.interval.begin, end, cast(ubyte) '{');
818 
819                 if (begin < end)
820                     begin = begin + 1;
821 
822                 // TODO: need to adjust sloc too
823                 loc.interval = Interval(begin, end);
824 
825                 auto n = ast.make!(analyze.Block);
826                 nstack.back.children ~= n;
827                 pushStack(n, loc, v.cursor.kind);
828             } catch (InvalidPathException e) {
829             } catch (Exception e) {
830                 logger.trace(e.msg).collectException;
831             }
832 
833         v.accept(this);
834     }
835 
836     override void visit(const CaseStmt v) {
837         mixin(mixinNodeLog!());
838         v.accept(this);
839     }
840 
841     override void visit(const DefaultStmt v) {
842         mixin(mixinNodeLog!());
843         v.accept(this);
844     }
845 
846     override void visit(const ForStmt v) {
847         mixin(mixinNodeLog!());
848         pushStack(ast.make!(analyze.Loop), v);
849 
850         auto visitor = new FindVisitor!CompoundStmt;
851         v.accept(visitor);
852 
853         if (visitor.node !is null) {
854             this.visit(visitor.node);
855         }
856     }
857 
858     override void visit(const CxxForRangeStmt v) {
859         mixin(mixinNodeLog!());
860         pushStack(ast.make!(analyze.Loop), v);
861 
862         auto visitor = new FindVisitor!CompoundStmt;
863         v.accept(visitor);
864 
865         if (visitor.node !is null) {
866             this.visit(visitor.node);
867         }
868     }
869 
870     override void visit(const WhileStmt v) {
871         mixin(mixinNodeLog!());
872         pushStack(ast.make!(analyze.Loop), v);
873         v.accept(this);
874     }
875 
876     override void visit(const DoStmt v) {
877         mixin(mixinNodeLog!());
878         pushStack(ast.make!(analyze.Loop), v);
879         v.accept(this);
880     }
881 
882     override void visit(const SwitchStmt v) {
883         mixin(mixinNodeLog!());
884         auto n = ast.make!(analyze.BranchBundle);
885         pushStack(n, v);
886         v.accept(this);
887 
888         auto caseVisitor = new FindVisitor!CaseStmt;
889         v.accept(caseVisitor);
890 
891         if (caseVisitor.node is null) {
892             logger.warning(
893                     "switch without any case statements may result in a high number of mutant scheman that do not compile");
894         } else {
895             incr;
896             scope (exit)
897                 decr;
898             auto block = ast.make!(analyze.Block);
899             auto l = ast.location(n);
900             l.interval.end = l.interval.begin;
901             pushStack(block, l, CXCursorKind.unexposedDecl);
902             rewriteSwitch(ast, n, block, caseVisitor.node.cursor.toLocation);
903         }
904     }
905 
906     override void visit(const ConditionalOperator v) {
907         mixin(mixinNodeLog!());
908         // need to push a node because a ternery can contain function calls.
909         // Without a node for the op it seems like it is in the body, which it
910         // isn't, and then can be removed.
911         pushStack(ast.make!(analyze.Poision), v);
912         v.accept(this);
913     }
914 
915     override void visit(const IfStmt v) @trusted {
916         mixin(mixinNodeLog!());
917         pushStack(ast.make!(analyze.BranchBundle), v);
918         dextool.plugin.mutate.backend.analyze.extensions.accept(v, this);
919     }
920 
921     override void visit(const IfStmtCond v) {
922         mixin(mixinNodeLog!());
923 
924         auto n = ast.make!(analyze.Condition);
925         pushStack(n, v);
926 
927         if (!visitOp(v, v.cursor.kind)) {
928             v.accept(this);
929         }
930 
931         if (!n.children.empty) {
932             auto tyId = ast.typeId(n.children[0]);
933             if (tyId.hasValue) {
934                 ast.put(n, tyId.orElse(analyze.TypeId.init));
935             }
936         }
937 
938         rewriteCondition(ast, n);
939     }
940 
941     override void visit(const IfStmtThen v) {
942         mixin(mixinNodeLog!());
943         visitIfBranch(v);
944     }
945 
946     override void visit(const IfStmtElse v) {
947         mixin(mixinNodeLog!());
948         visitIfBranch(v);
949     }
950 
951     private void visitIfBranch(T)(ref const T v) @trusted {
952         pushStack(ast.make!(analyze.Branch), v);
953         v.accept(this);
954     }
955 
956     private bool visitOp(T)(ref const T v, const CXCursorKind cKind) @trusted {
957         auto op = operatorCursor(ast.get, v);
958         if (op.isNull) {
959             return false;
960         }
961 
962         if (visitBinaryOp(op.get, cKind))
963             return true;
964         return visitUnaryOp(op.get, cKind);
965     }
966 
967     /// Returns: true if it added a binary operator, false otherwise.
968     private bool visitBinaryOp(ref OperatorCursor op, const CXCursorKind cKind) @trusted {
969         import libclang_ast.ast : dispatch;
970 
971         auto astOp = cast(analyze.BinaryOp) op.astOp;
972         if (astOp is null)
973             return false;
974 
975         const blockSchema = op.isOverload || blacklist.blockSchema(op.opLoc) || isParent(CXCursorKind.classTemplate,
976                 CXCursorKind.classTemplatePartialSpecialization, CXCursorKind.functionTemplate) != 0;
977 
978         astOp.schemaBlacklist = blockSchema;
979         astOp.operator = op.operator;
980         astOp.operator.blacklist = blacklist.inside(op.opLoc);
981         astOp.operator.schemaBlacklist = blockSchema;
982 
983         op.put(nstack.back, ast);
984         pushStack(astOp, op.exprLoc, cKind);
985         incr;
986         scope (exit)
987             decr;
988 
989         if (op.lhs.isValid) {
990             incr;
991             scope (exit)
992                 decr;
993             dispatch(op.lhs, this);
994             auto b = () {
995                 if (!lastDecr.empty)
996                     return cast(analyze.Expr) lastDecr[$ - 1];
997                 return null;
998             }();
999             if (b !is null && b != astOp) {
1000                 astOp.lhs = b;
1001                 auto ty = deriveCursorType(ast, op.lhs);
1002                 ty.put(ast);
1003                 if (ty.type !is null) {
1004                     ast.put(b, ty.id);
1005                 }
1006                 if (ty.symbol !is null) {
1007                     ast.put(b, ty.symId);
1008                 }
1009             }
1010         }
1011         if (op.rhs.isValid) {
1012             incr;
1013             scope (exit)
1014                 decr;
1015             dispatch(op.rhs, this);
1016             auto b = () {
1017                 if (!lastDecr.empty)
1018                     return cast(analyze.Expr) lastDecr[$ - 1];
1019                 return null;
1020             }();
1021             if (b !is null && b != astOp) {
1022                 astOp.rhs = b;
1023                 auto ty = deriveCursorType(ast, op.rhs);
1024                 ty.put(ast);
1025                 if (ty.type !is null) {
1026                     ast.put(b, ty.id);
1027                 }
1028                 if (ty.symbol !is null) {
1029                     ast.put(b, ty.symId);
1030                 }
1031             }
1032         }
1033 
1034         // TODO: this is crude and shouldn't be here as a check but we must
1035         // block aor/rorp schematan when the type is a pointer.
1036         foreach (_; getChildrenTypes(ast, astOp).filter!(a => a.among(TypeKind.unordered,
1037                 TypeKind.bottom))) {
1038             foreach (c; BreathFirstRange(astOp))
1039                 c.schemaBlacklist = true;
1040             break;
1041         }
1042 
1043         return true;
1044     }
1045 
1046     /// Returns: true if it added a binary operator, false otherwise.
1047     private bool visitUnaryOp(ref OperatorCursor op, CXCursorKind cKind) @trusted {
1048         import libclang_ast.ast : dispatch;
1049 
1050         auto astOp = cast(analyze.UnaryOp) op.astOp;
1051         if (astOp is null)
1052             return false;
1053 
1054         const blockSchema = op.isOverload || blacklist.blockSchema(op.opLoc) || isParent(CXCursorKind.classTemplate,
1055                 CXCursorKind.classTemplatePartialSpecialization, CXCursorKind.functionTemplate) != 0;
1056 
1057         astOp.operator = op.operator;
1058         astOp.operator.blacklist = blacklist.inside(op.opLoc);
1059         astOp.operator.schemaBlacklist = blockSchema;
1060 
1061         op.put(nstack.back, ast);
1062         pushStack(astOp, op.exprLoc, cKind);
1063         incr;
1064         scope (exit)
1065             decr;
1066 
1067         if (op.lhs.isValid) {
1068             incr;
1069             scope (exit)
1070                 decr;
1071             dispatch(op.lhs, this);
1072             auto b = () {
1073                 if (!lastDecr.empty)
1074                     return cast(analyze.Expr) lastDecr[$ - 1];
1075                 return null;
1076             }();
1077             if (b !is null && b != astOp) {
1078                 astOp.expr = b;
1079                 auto ty = deriveCursorType(ast, op.lhs);
1080                 ty.put(ast);
1081                 if (ty.type !is null)
1082                     ast.put(b, ty.id);
1083                 if (ty.symbol !is null)
1084                     ast.put(b, ty.symId);
1085             }
1086         }
1087         if (op.rhs.isValid) {
1088             incr;
1089             scope (exit)
1090                 decr;
1091             dispatch(op.rhs, this);
1092             auto b = () {
1093                 if (!lastDecr.empty)
1094                     return cast(analyze.Expr) lastDecr[$ - 1];
1095                 return null;
1096             }();
1097             if (b !is null && b != astOp) {
1098                 astOp.expr = b;
1099                 auto ty = deriveCursorType(ast, op.rhs);
1100                 ty.put(ast);
1101                 if (ty.type !is null)
1102                     ast.put(b, ty.id);
1103                 if (ty.symbol !is null)
1104                     ast.put(b, ty.symId);
1105             }
1106         }
1107 
1108         return true;
1109     }
1110 
1111     private void visitFunc(T)(ref const T v) @trusted {
1112         auto loc = v.cursor.toLocation;
1113         auto n = ast.make!(analyze.Function);
1114         n.schemaBlacklist = isConstExpr(v.cursor);
1115         nstack.back.children ~= n;
1116         pushStack(n, loc, v.cursor.kind);
1117 
1118         auto fRetval = ast.make!(analyze.Return);
1119         auto rty = deriveType(ast.get, v.cursor.func.resultType);
1120         rty.put(ast);
1121         if (rty.type !is null) {
1122             ast.put(fRetval, loc);
1123             n.return_ = fRetval;
1124             ast.put(fRetval, rty.id);
1125         }
1126         if (rty.symbol !is null)
1127             ast.put(fRetval, rty.symId);
1128 
1129         v.accept(this);
1130     }
1131 
1132     private void visitCall(T)(ref const T v) @trusted {
1133         auto n = ast.make!(analyze.Call);
1134         pushStack(n, v);
1135 
1136         auto ty = deriveType(ast.get, v.cursor.type);
1137         ty.put(ast);
1138         if (ty.type !is null)
1139             ast.put(n, ty.id);
1140         if (ty.symbol !is null)
1141             ast.put(n, ty.symId);
1142 
1143         v.accept(this);
1144     }
1145 }
1146 
1147 final class EnumVisitor : ExtendedVisitor {
1148     import libclang_ast.ast;
1149 
1150     alias visit = ExtendedVisitor.visit;
1151 
1152     mixin generateIndentIncrDecr;
1153 
1154     analyze.Ast* ast;
1155     analyze.TypeId id;
1156     Nullable!long minValue;
1157     Nullable!long maxValue;
1158 
1159     this(ref analyze.Ast ast, const uint indent) @trusted {
1160         this.ast = &ast;
1161         this.indent = indent;
1162     }
1163 
1164     override void visit(const EnumDecl v) @trusted {
1165         mixin(mixinNodeLog!());
1166         id = make!(analyze.TypeId)(v.cursor);
1167         v.accept(this);
1168     }
1169 
1170     override void visit(const EnumConstantDecl v) @trusted {
1171         mixin(mixinNodeLog!());
1172 
1173         long value = v.cursor.enum_.signedValue;
1174 
1175         if (minValue.isNull) {
1176             minValue = value;
1177             maxValue = value;
1178         }
1179 
1180         if (value < minValue.get)
1181             minValue = value;
1182         if (value > maxValue.get)
1183             maxValue = value;
1184 
1185         v.accept(this);
1186     }
1187 
1188     analyze.Type toType() {
1189         auto l = () {
1190             if (minValue.isNull)
1191                 return analyze.Value(analyze.Value.NegInf.init);
1192             return analyze.Value(analyze.Value.Int(minValue.get));
1193         }();
1194         auto u = () {
1195             if (maxValue.isNull)
1196                 return analyze.Value(analyze.Value.PosInf.init);
1197             return analyze.Value(analyze.Value.Int(maxValue.get));
1198         }();
1199 
1200         return ast.make!(analyze.DiscreteType)(analyze.Range(l, u));
1201     }
1202 }
1203 
1204 /// Find first occurences of the node type `T`.
1205 final class FindVisitor(T) : Visitor {
1206     import libclang_ast.ast;
1207 
1208     alias visit = Visitor.visit;
1209 
1210     const(T)[] nodes;
1211 
1212     const(T) node() @safe pure nothrow const @nogc {
1213         if (nodes.empty)
1214             return null;
1215         return nodes[0];
1216     }
1217 
1218     //uint indent;
1219     //override void incr() @safe {
1220     //    ++indent;
1221     //}
1222     //
1223     //override void decr() @safe {
1224     //    --indent;
1225     //}
1226 
1227     override void visit(const Statement v) {
1228         //mixin(mixinNodeLog!());
1229         if (nodes.empty)
1230             v.accept(this);
1231     }
1232 
1233     override void visit(const T v) @trusted {
1234         //mixin(mixinNodeLog!());
1235         if (nodes.empty)
1236             nodes = [v];
1237     }
1238 }
1239 
1240 /** Rewrite the position of a condition to perfectly match the parenthesis.
1241  *
1242  * The source code:
1243  * ```
1244  * if (int x = 42) y = 43;
1245  * ```
1246  *
1247  * Results in something like this in the AST.
1248  *
1249  * `-Condition
1250  *   `-Expr
1251  *     `-Expr
1252  *       `-VarDecl
1253  *         `-OpGreater
1254  *           `-Operator
1255  *           `-Expr
1256  *           `-Expr
1257  *
1258  * The problem is that the location of the Condition node will be OpGreater and
1259  * not the VarDecl.
1260  */
1261 void rewriteCondition(ref analyze.Ast ast, analyze.Condition root) {
1262     import sumtype;
1263     import dextool.plugin.mutate.backend.analyze.ast : TypeId, VarDecl, Kind;
1264 
1265     // set the type of the Condition to the first expression with a type.
1266     foreach (ty; BreathFirstRange(root).map!(a => ast.typeId(a))
1267             .filter!(a => a.hasValue)) {
1268         sumtype.match!((Some!TypeId a) => ast.put(root, a), (None a) {})(ty);
1269         break;
1270     }
1271 
1272     foreach (a; BreathFirstRange(root).filter!(a => a.kind == Kind.VarDecl)) {
1273         ast.put(root, ast.location(a));
1274         // can't delete the VarDecl explicitly because the code will not compile
1275         a.schemaBlacklist = true;
1276         break;
1277     }
1278 }
1279 
1280 void rewriteSwitch(ref analyze.Ast ast, analyze.BranchBundle root,
1281         analyze.Block block, Location firstCase) {
1282     import std.range : enumerate;
1283     import std.typecons : tuple;
1284 
1285     Node[] beforeCase = root.children;
1286     Node[] rest;
1287 
1288     foreach (n; root.children
1289             .map!(a => tuple!("node", "loc")(a, ast.location(a)))
1290             .enumerate
1291             .filter!(a => a.value.loc.interval.begin > firstCase.interval.begin)) {
1292         beforeCase = root.children[0 .. n.index];
1293         rest = root.children[n.index .. $];
1294         break;
1295     }
1296 
1297     root.children = beforeCase ~ block;
1298     block.children = rest;
1299 }
1300 
1301 enum discreteCategory = AliasSeq!(CXTypeKind.charU, CXTypeKind.uChar, CXTypeKind.char16,
1302             CXTypeKind.char32, CXTypeKind.uShort, CXTypeKind.uInt, CXTypeKind.uLong, CXTypeKind.uLongLong,
1303             CXTypeKind.uInt128, CXTypeKind.charS, CXTypeKind.sChar, CXTypeKind.wChar, CXTypeKind.short_,
1304             CXTypeKind.int_, CXTypeKind.long_, CXTypeKind.longLong,
1305             CXTypeKind.int128, CXTypeKind.enum_,);
1306 enum floatCategory = AliasSeq!(CXTypeKind.float_, CXTypeKind.double_,
1307             CXTypeKind.longDouble, CXTypeKind.float128, CXTypeKind.half, CXTypeKind.float16,);
1308 enum pointerCategory = AliasSeq!(CXTypeKind.nullPtr, CXTypeKind.pointer,
1309             CXTypeKind.blockPointer, CXTypeKind.memberPointer, CXTypeKind.record);
1310 enum boolCategory = AliasSeq!(CXTypeKind.bool_);
1311 
1312 enum voidCategory = AliasSeq!(CXTypeKind.void_);
1313 
1314 struct DeriveTypeResult {
1315     analyze.TypeId id;
1316     analyze.Type type;
1317     analyze.SymbolId symId;
1318     analyze.Symbol symbol;
1319 
1320     void put(ref analyze.Ast ast) @safe {
1321         if (type !is null) {
1322             ast.types.require(id, type);
1323         }
1324         if (symbol !is null) {
1325             ast.symbols.require(symId, symbol);
1326         }
1327     }
1328 }
1329 
1330 DeriveTypeResult deriveType(ref Ast ast, Type cty) {
1331     DeriveTypeResult rval;
1332 
1333     if (!cty.isValid)
1334         return rval;
1335 
1336     auto ctydecl = cty.declaration;
1337     if (ctydecl.isValid) {
1338         rval.id = make!(analyze.TypeId)(ctydecl);
1339     } else {
1340         rval.id = make!(analyze.TypeId)(cty.cursor);
1341     }
1342 
1343     if (cty.isEnum) {
1344         rval.type = ast.make!(analyze.DiscreteType)(analyze.Range.makeInf);
1345         if (!cty.isSigned) {
1346             rval.type.range.low = analyze.Value(analyze.Value.Int(0));
1347         }
1348     } else if (cty.kind.among(floatCategory)) {
1349         rval.type = ast.make!(analyze.ContinuesType)(analyze.Range.makeInf);
1350     } else if (cty.kind.among(pointerCategory)) {
1351         rval.type = ast.make!(analyze.UnorderedType)(analyze.Range.makeInf);
1352     } else if (cty.kind.among(boolCategory)) {
1353         rval.type = ast.make!(analyze.BooleanType)(analyze.Range.makeBoolean);
1354     } else if (cty.kind.among(discreteCategory)) {
1355         rval.type = ast.make!(analyze.DiscreteType)(analyze.Range.makeInf);
1356         if (!cty.isSigned) {
1357             rval.type.range.low = analyze.Value(analyze.Value.Int(0));
1358         }
1359     } else if (cty.kind.among(voidCategory)) {
1360         rval.type = ast.make!(analyze.VoidType)();
1361     } else {
1362         // unknown such as an elaborated
1363         rval.type = ast.make!(analyze.Type)();
1364     }
1365 
1366     return rval;
1367 }
1368 
1369 struct DeriveCursorTypeResult {
1370     Cursor expr;
1371     DeriveTypeResult typeResult;
1372     alias typeResult this;
1373 }
1374 
1375 /** Analyze a cursor to derive the type of it and if it has a concrete value
1376  * and what it is in that case.
1377  *
1378  * This is intended for expression nodes in the clang AST.
1379  */
1380 DeriveCursorTypeResult deriveCursorType(ref Ast ast, const Cursor baseCursor) {
1381     auto c = Cursor(getUnderlyingExprNode(baseCursor));
1382     if (!c.isValid)
1383         return DeriveCursorTypeResult.init;
1384 
1385     auto rval = DeriveCursorTypeResult(c);
1386     auto cty = c.type.canonicalType;
1387     rval.typeResult = deriveType(ast, cty);
1388 
1389     // evaluate the cursor to add a value for the symbol
1390     void eval(const ref Eval e) {
1391         if (!e.kind.among(CXEvalResultKind.int_))
1392             return;
1393 
1394         const long value = () {
1395             if (e.isUnsignedInt) {
1396                 const v = e.asUnsigned;
1397                 if (v < long.max)
1398                     return cast(long) v;
1399             }
1400             return e.asLong;
1401         }();
1402 
1403         rval.symId = make!(analyze.SymbolId)(c);
1404         rval.symbol = ast.make!(analyze.DiscretSymbol)(analyze.Value(analyze.Value.Int(value)));
1405     }
1406 
1407     if (cty.isEnum) {
1408         // TODO: check if c.eval give the same result. If so it may be easier
1409         // to remove this special case of an enum because it is covered by the
1410         // generic branch for discretes.
1411 
1412         auto ctydecl = cty.declaration;
1413         if (!ctydecl.isValid)
1414             return rval;
1415 
1416         const cref = c.referenced;
1417         if (!cref.isValid)
1418             return rval;
1419 
1420         if (cref.kind == CXCursorKind.enumConstantDecl) {
1421             const long value = cref.enum_.signedValue;
1422             rval.symId = make!(analyze.SymbolId)(c);
1423             rval.symbol = ast.make!(analyze.DiscretSymbol)(
1424                     analyze.Value(analyze.Value.Int(value)));
1425         }
1426     } else if (cty.kind.among(discreteCategory)) {
1427         // crashes in clang 7.x. Investigate why.
1428         //const e = c.eval;
1429         //if (e.isValid)
1430         //    eval(e);
1431     }
1432 
1433     return rval;
1434 }
1435 
1436 auto make(T)(const Cursor c) if (is(T == analyze.TypeId) || is(T == analyze.SymbolId)) {
1437     const usr = c.usr;
1438     if (usr.empty) {
1439         return T(c.toHash);
1440     }
1441     return analyze.makeId!T(usr);
1442 }
1443 
1444 /// trusted: trusting the impl in clang.
1445 uint findTokenOffset(T)(T toks, Offset sr, CXTokenKind kind) @trusted {
1446     foreach (ref t; toks) {
1447         if (t.location.offset >= sr.end) {
1448             if (t.kind == kind) {
1449                 return t.extent.end.offset;
1450             }
1451             break;
1452         }
1453     }
1454 
1455     return sr.end;
1456 }
1457 
1458 /** Check if a function has the constexpr keyword.
1459  *
1460  * The implementation opt for higher precision than efficiency which is why it
1461  * looks at the tokens. That should eliminate such factors as "whitespace".
1462  */
1463 bool isConstExpr(const Cursor c) @trusted {
1464     bool helper(T)(ref T toks) {
1465         foreach (ref t; toks.filter!(a => a.kind.among(CXTokenKind.keyword,
1466                 CXTokenKind.identifier))) {
1467             if (t.spelling == "constexpr") {
1468                 return true;
1469             }
1470         }
1471         return false;
1472     }
1473 
1474     auto toks = c.tokens;
1475     return helper(toks);
1476 }
1477 
1478 /** Create an index of all macros that then can be queried to see if a Cursor
1479  * or Interval overlap a macro.
1480  */
1481 struct BlackList {
1482     import dextool.plugin.mutate.backend.analyze.utility : Index;
1483 
1484     Index!string macros;
1485     /// schemas are blacklisted for these
1486     Index!string schemas;
1487 
1488     this(const Cursor root) {
1489         Interval[][string] macros;
1490         Interval[][string] schemas;
1491 
1492         foreach (c, parent; root.all) {
1493             if (!c.kind.among(CXCursorKind.macroExpansion,
1494                     CXCursorKind.macroDefinition) || c.isMacroBuiltin)
1495                 continue;
1496 
1497             auto spelling = c.spelling;
1498             // C code almost always implement these as macros. They should not
1499             // be blocked from being mutated.
1500             if (spelling.among("bool", "TRUE", "FALSE")) {
1501                 add(c, schemas);
1502             } else {
1503                 add(c, macros);
1504             }
1505         }
1506 
1507         foreach (k; macros.byKey) {
1508             macros[k] = macros[k].sort.array;
1509         }
1510         foreach (k; schemas.byKey) {
1511             schemas[k] = schemas[k].sort.array;
1512         }
1513 
1514         this.macros = Index!string(macros);
1515         this.schemas = Index!string(schemas);
1516     }
1517 
1518     static void add(const Cursor c, ref Interval[][string] idx) {
1519         const file = c.location.path;
1520         if (file.empty)
1521             return;
1522         const e = c.extent;
1523         const interval = Interval(e.start.offset, e.end.offset);
1524         if (auto v = file in idx) {
1525             (*v) ~= interval;
1526         } else {
1527             idx[file] = [interval];
1528         }
1529     }
1530 
1531     bool blockSchema(analyze.Location l) {
1532         return schemas.intersect(l.file, l.interval);
1533     }
1534 
1535     bool inside(const Cursor c) {
1536         const file = c.location.path.Path;
1537         const e = c.extent;
1538         const interval = Interval(e.start.offset, e.end.offset);
1539         return inside(file, interval);
1540     }
1541 
1542     bool inside(analyze.Location l) {
1543         return inside(l.file, l.interval);
1544     }
1545 
1546     /**
1547      * assuming that an invalid mutant is always inside a macro thus only
1548      * checking if the `i` is inside. Removing a "block" of code that happens
1549      * to contain a macro is totally "ok". It doesn't create any problem.
1550      *
1551      * Returns: true if `i` is inside a macro interval.
1552      */
1553     bool inside(const Path file, const Interval i) {
1554         return macros.overlap(file, i);
1555     }
1556 }
1557 
1558 /// Returns: the types of the children
1559 auto getChildrenTypes(ref Ast ast, Node parent) {
1560     return BreathFirstRange(parent).map!(a => ast.type(a))
1561         .filter!(a => a !is null)
1562         .map!(a => a.kind);
1563 }