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 `cpptooling.analyzer.clang.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 cpptooling.analyzer.clang.ast : Visitor;
19 
20 import my.set;
21 
22 static import dextool.clang_extensions;
23 
24 static import cpptooling.analyzer.clang.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 cpptooling.analyzer.clang.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 : cpptooling.analyzer.clang.ast.Statement {
62     this(Cursor cursor) @safe {
63         super(cursor);
64     }
65 
66     void accept(ExtendedVisitor v) @safe const {
67         static import cpptooling.analyzer.clang.ast;
68 
69         cpptooling.analyzer.clang.ast.dispatch(cursor, v);
70     }
71 }
72 
73 final class IfStmtCond : cpptooling.analyzer.clang.ast.Expression {
74     this(Cursor cursor) @safe {
75         super(cursor);
76     }
77 
78     void accept(ExtendedVisitor v) @safe const {
79         static import cpptooling.analyzer.clang.ast;
80 
81         cpptooling.analyzer.clang.ast.dispatch(cursor, v);
82     }
83 }
84 
85 final class IfStmtThen : cpptooling.analyzer.clang.ast.Statement {
86     this(Cursor cursor) @safe {
87         super(cursor);
88     }
89 
90     void accept(ExtendedVisitor v) @safe const {
91         static import cpptooling.analyzer.clang.ast;
92 
93         cpptooling.analyzer.clang.ast.dispatch(cursor, v);
94     }
95 }
96 
97 final class IfStmtElse : cpptooling.analyzer.clang.ast.Statement {
98     this(Cursor cursor) @safe {
99         super(cursor);
100     }
101 
102     void accept(ExtendedVisitor v) @safe const {
103         static import cpptooling.analyzer.clang.ast;
104 
105         cpptooling.analyzer.clang.ast.dispatch(cursor, v);
106     }
107 }
108 
109 void accept(T)(const(cpptooling.analyzer.clang.ast.IfStmt) n, T v)
110         if (is(T : ExtendedVisitor)) {
111     import dextool.clang_extensions;
112 
113     auto stmt = getIfStmt(n.cursor);
114     accept(stmt, v);
115 }
116 
117 void accept(T)(ref dextool.clang_extensions.IfStmt n, T v)
118         if (is(T : ExtendedVisitor)) {
119     import std.traits : hasMember;
120 
121     static if (hasMember!(T, "incr"))
122         v.incr;
123 
124     if (n.init_.isValid) {
125         auto sub = new IfStmtInit(n.init_);
126         static if (__traits(hasMember, T, "ignoreCursors")) {
127             v.ignoreCursors.add(n.init_.toHash);
128         }
129         v.visit(sub);
130     }
131     if (n.cond.isValid) {
132         auto sub = new IfStmtCond(n.cond);
133         static if (__traits(hasMember, T, "ignoreCursors")) {
134             v.ignoreCursors.add(n.cond.toHash);
135         }
136         v.visit(sub);
137     }
138     if (n.then.isValid) {
139         auto sub = new IfStmtThen(n.then);
140         v.visit(sub);
141     }
142     if (n.else_.isValid) {
143         auto sub = new IfStmtElse(n.else_);
144         v.visit(sub);
145     }
146 
147     static if (hasMember!(T, "decr"))
148         v.decr;
149 }