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 }