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(scope const IfStmtInit value) {
42         visit(cast(const(Statement)) value);
43     }
44 
45     void visit(scope const IfStmtCond value) {
46         visit(cast(const(Expression)) value);
47     }
48 
49     void visit(scope const IfStmtCondVar value) {
50         visit(cast(const(Expression)) value);
51     }
52 
53     void visit(scope const IfStmtThen value) {
54         visit(cast(const(Statement)) value);
55     }
56 
57     void visit(scope 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 scope {
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 scope {
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 scope {
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 scope {
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 scope {
119         static import libclang_ast.ast;
120 
121         libclang_ast.ast.dispatch(cursor, v);
122     }
123 }
124 
125 void accept(T)(scope const libclang_ast.ast.IfStmt n, T v)
126         if (is(T : ExtendedVisitor)) {
127     import dextool.clang_extensions;
128 
129     auto stmt = getIfStmt(n.cursor);
130     accept(stmt, v);
131 }
132 
133 void accept(T)(scope dextool.clang_extensions.IfStmt n, T v)
134         if (is(T : ExtendedVisitor)) {
135     import std.traits : hasMember;
136 
137     void incr() {
138         static if (hasMember!(T, "incr"))
139             v.incr;
140     }
141 
142     void decr() {
143         static if (hasMember!(T, "decr"))
144             v.decr;
145     }
146 
147     void ignore(Cursor c) {
148         static if (__traits(hasMember, T, "ignoreCursors"))
149             v.ignoreCursors.add(c.toHash);
150     }
151 
152     static if (__traits(hasMember, T, "ignoreCursors")) {
153         {
154             const h = n.cursor.toHash;
155             if (h in v.ignoreCursors) {
156                 v.ignoreCursors.remove(h);
157                 return;
158             }
159             v.ignoreCursors.add(h);
160         }
161     }
162 
163     incr();
164     scope (exit)
165         decr();
166 
167     if (n.init_.isValid) {
168         ignore(n.init_);
169         scope sub = new IfStmtInit(n.init_);
170         v.visit(sub);
171     }
172     if (n.cond.isValid) {
173         if (n.conditionVariable.isValid) {
174             incr();
175 
176             ignore(n.conditionVariable);
177             scope sub = new IfStmtCondVar(n.conditionVariable);
178             v.visit(sub);
179         }
180 
181         ignore(n.cond);
182         scope sub = new IfStmtCond(n.cond);
183         v.visit(sub);
184 
185         if (n.conditionVariable.isValid)
186             decr();
187     }
188     if (n.then.isValid) {
189         scope sub = new IfStmtThen(n.then);
190         v.visit(sub);
191     }
192     if (n.else_.isValid) {
193         scope sub = new IfStmtElse(n.else_);
194         v.visit(sub);
195     }
196 }