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 Extensions to the visitor in `libclang_ast.ast`.
11 
12 Intende to move this code to clang_extensions if this approach to extending the
13 clang AST works well.
14 */
15 module dextool.plugin.mutate.backend.analyze.extensions;
16 
17 import clang.Cursor : Cursor;
18 import libclang_ast.ast : Visitor;
19 
20 import my.set;
21 
22 static import dextool.clang_extensions;
23 
24 static import libclang_ast.ast;
25 
26 /**
27  * the ignoreCursors solution is not particularly principaled. It is an ugly hack that should be moved to the core AST `Visitor` and thus completely h
28  */
29 class ExtendedVisitor : Visitor {
30     import libclang_ast.ast;
31     import dextool.clang_extensions;
32 
33     alias visit = Visitor.visit;
34 
35     // A cursors that has been decorated as one of the extended should be
36     // "ignored" if it is found "again". Which happens for e.g. a
37     // BinaryOperator inside a if-statement. It will first occur as a
38     // IfStmtCond and then a BinaryOperator.
39     Set!size_t ignoreCursors;
40 
41     void visit(const(IfStmtInit) value) {
42         visit(cast(const(Statement)) value);
43     }
44 
45     void visit(const(IfStmtCond) value) {
46         visit(cast(const(Expression)) value);
47     }
48 
49     void visit(const(IfStmtThen) value) {
50         visit(cast(const(Statement)) value);
51     }
52 
53     void visit(const(IfStmtElse) value) {
54         visit(cast(const(Statement)) value);
55     }
56 }
57 
58 // using dispatch because the wrapped cursor has to be re-visited as its
59 // original `type`. Not just the colored `IfStmt` node.
60 
61 final class IfStmtInit : libclang_ast.ast.Statement {
62     this(Cursor cursor) @safe {
63         super(cursor);
64     }
65 
66     void accept(ExtendedVisitor v) @safe const {
67         static import libclang_ast.ast;
68 
69         libclang_ast.ast.dispatch(cursor, v);
70     }
71 }
72 
73 final class IfStmtCond : libclang_ast.ast.Expression {
74     this(Cursor cursor) @safe {
75         super(cursor);
76     }
77 
78     void accept(ExtendedVisitor v) @safe const {
79         static import libclang_ast.ast;
80 
81         libclang_ast.ast.dispatch(cursor, v);
82     }
83 }
84 
85 final class IfStmtThen : libclang_ast.ast.Statement {
86     this(Cursor cursor) @safe {
87         super(cursor);
88     }
89 
90     void accept(ExtendedVisitor v) @safe const {
91         static import libclang_ast.ast;
92 
93         libclang_ast.ast.dispatch(cursor, v);
94     }
95 }
96 
97 final class IfStmtElse : libclang_ast.ast.Statement {
98     this(Cursor cursor) @safe {
99         super(cursor);
100     }
101 
102     void accept(ExtendedVisitor v) @safe const {
103         static import libclang_ast.ast;
104 
105         libclang_ast.ast.dispatch(cursor, v);
106     }
107 }
108 
109 void accept(T)(const(libclang_ast.ast.IfStmt) n, T v) if (is(T : ExtendedVisitor)) {
110     import dextool.clang_extensions;
111 
112     auto stmt = getIfStmt(n.cursor);
113     accept(stmt, v);
114 }
115 
116 void accept(T)(ref dextool.clang_extensions.IfStmt n, T v)
117         if (is(T : ExtendedVisitor)) {
118     import std.traits : hasMember;
119 
120     static if (hasMember!(T, "incr"))
121         v.incr;
122 
123     if (n.init_.isValid) {
124         auto sub = new IfStmtInit(n.init_);
125         static if (__traits(hasMember, T, "ignoreCursors")) {
126             v.ignoreCursors.add(n.init_.toHash);
127         }
128         v.visit(sub);
129     }
130     if (n.cond.isValid) {
131         auto sub = new IfStmtCond(n.cond);
132         static if (__traits(hasMember, T, "ignoreCursors")) {
133             v.ignoreCursors.add(n.cond.toHash);
134         }
135         v.visit(sub);
136     }
137     if (n.then.isValid) {
138         auto sub = new IfStmtThen(n.then);
139         v.visit(sub);
140     }
141     if (n.else_.isValid) {
142         auto sub = new IfStmtElse(n.else_);
143         v.visit(sub);
144     }
145 
146     static if (hasMember!(T, "decr"))
147         v.decr;
148 }