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 }