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(IfStmtCondVar) value) {
50         visit(cast(const(Expression)) value);
51     }
52 
53     void visit(const(IfStmtThen) value) {
54         visit(cast(const(Statement)) value);
55     }
56 
57     void visit(const(IfStmtElse) value) {
58         visit(cast(const(Statement)) value);
59     }
60 }
61 
62 // using dispatch because the wrapped cursor has to be re-visited as its
63 // original `type`. Not just the colored `IfStmt` node.
64 
65 final class IfStmtInit : libclang_ast.ast.Statement {
66     this(Cursor cursor) @safe {
67         super(cursor);
68     }
69 
70     void accept(ExtendedVisitor v) @safe const {
71         static import libclang_ast.ast;
72 
73         libclang_ast.ast.dispatch(cursor, v);
74     }
75 }
76 
77 final class IfStmtCond : libclang_ast.ast.Expression {
78     this(Cursor cursor) @safe {
79         super(cursor);
80     }
81 
82     void accept(ExtendedVisitor v) @safe const {
83         static import libclang_ast.ast;
84 
85         libclang_ast.ast.dispatch(cursor, v);
86     }
87 }
88 
89 final class IfStmtCondVar : libclang_ast.ast.Expression {
90     this(Cursor cursor) @safe {
91         super(cursor);
92     }
93 
94     void accept(ExtendedVisitor v) @safe const {
95         static import libclang_ast.ast;
96 
97         libclang_ast.ast.dispatch(cursor, v);
98     }
99 }
100 
101 final class IfStmtThen : libclang_ast.ast.Statement {
102     this(Cursor cursor) @safe {
103         super(cursor);
104     }
105 
106     void accept(ExtendedVisitor v) @safe const {
107         static import libclang_ast.ast;
108 
109         libclang_ast.ast.dispatch(cursor, v);
110     }
111 }
112 
113 final class IfStmtElse : libclang_ast.ast.Statement {
114     this(Cursor cursor) @safe {
115         super(cursor);
116     }
117 
118     void accept(ExtendedVisitor v) @safe const {
119         static import libclang_ast.ast;
120 
121         libclang_ast.ast.dispatch(cursor, v);
122     }
123 }
124 
125 void accept(T)(const(libclang_ast.ast.IfStmt) n, T v) if (is(T : ExtendedVisitor)) {
126     import dextool.clang_extensions;
127 
128     auto stmt = getIfStmt(n.cursor);
129     accept(stmt, v);
130 }
131 
132 void accept(T)(dextool.clang_extensions.IfStmt n, T v) if (is(T : ExtendedVisitor)) {
133     import std.traits : hasMember;
134 
135     void incr() {
136         static if (hasMember!(T, "incr"))
137             v.incr;
138     }
139 
140     void decr() {
141         static if (hasMember!(T, "decr"))
142             v.decr;
143     }
144 
145     void ignore(Cursor c) {
146         static if (__traits(hasMember, T, "ignoreCursors"))
147             v.ignoreCursors.add(c.toHash);
148     }
149 
150     static if (__traits(hasMember, T, "ignoreCursors")) {
151         {
152             const h = n.cursor.toHash;
153             if (h in v.ignoreCursors) {
154                 v.ignoreCursors.remove(h);
155                 return;
156             }
157             v.ignoreCursors.add(h);
158         }
159     }
160 
161     incr();
162     scope (exit)
163         decr();
164 
165     if (n.init_.isValid) {
166         ignore(n.init_);
167         scope sub = new IfStmtInit(n.init_);
168         v.visit(sub);
169     }
170     if (n.cond.isValid) {
171         if (n.conditionVariable.isValid) {
172             incr();
173 
174             ignore(n.conditionVariable);
175             scope sub = new IfStmtCondVar(n.conditionVariable);
176             v.visit(sub);
177         }
178 
179         ignore(n.cond);
180         scope sub = new IfStmtCond(n.cond);
181         v.visit(sub);
182 
183         if (n.conditionVariable.isValid)
184             decr();
185     }
186     if (n.then.isValid) {
187         scope sub = new IfStmtThen(n.then);
188         v.visit(sub);
189     }
190     if (n.else_.isValid) {
191         scope sub = new IfStmtElse(n.else_);
192         v.visit(sub);
193     }
194 }