1 /** 2 Copyright: Copyright (c) 2016, 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 module cpptooling.analyzer.clang.ast.tree; 11 12 import logger = std.experimental.logger; 13 14 import clang.Cursor : Cursor; 15 16 import clang.c.Index : CXCursorKind; 17 18 import cpptooling.analyzer.clang.ast.attribute; 19 import cpptooling.analyzer.clang.ast.declaration; 20 import cpptooling.analyzer.clang.ast.directive; 21 import cpptooling.analyzer.clang.ast.expression; 22 import cpptooling.analyzer.clang.ast.extra; 23 import cpptooling.analyzer.clang.ast.preprocessor; 24 import cpptooling.analyzer.clang.ast.reference; 25 import cpptooling.analyzer.clang.ast.statement; 26 import cpptooling.analyzer.clang.ast.translationunit; 27 28 import cpptooling.analyzer.clang.ast.nodes : makeNodeClassName; 29 30 version (unittest) { 31 import unit_threaded : shouldEqual, shouldBeTrue; 32 } 33 34 /** 35 * Wrap a clang cursor. No restrictions on what type of cursor it is. 36 * Accept a Visitor. 37 * During accept traverse the AST to: 38 * - wrap Cursors's in specific classes corresponding to the kind of cursor. 39 * - call Visitor's visit(...) with wrapped cursor. 40 * 41 * Starts traversing the AST from the root. 42 * 43 * Optional functions: 44 * void incr(). Called before descending a node. 45 * void decr(). Called after ascending a node. 46 */ 47 struct ClangAST(VisitorT) { 48 import std.traits : isArray; 49 import clang.Cursor : Cursor; 50 51 Cursor root; 52 53 void accept(ref VisitorT visitor) @safe { 54 static if (isArray!VisitorT) { 55 foreach (v; visitor) { 56 v.incr(); 57 } 58 59 scope (success) { 60 foreach (v; visitor) { 61 v.decr(); 62 } 63 } 64 65 } else { 66 visitor.incr(); 67 scope (success) 68 visitor.decr(); 69 } 70 71 dispatch(root, visitor); 72 } 73 } 74 75 /** Apply the visitor to the children of the cursor. 76 * 77 * Optional functions: 78 * void incr(). Called before descending a node. 79 * void decr(). Called after ascending a node. 80 */ 81 void accept(VisitorT)(ref const(Cursor) cursor, ref VisitorT visitor) @safe { 82 import std.traits : isArray; 83 import clang.Visitor : Visitor; 84 85 static if (isArray!VisitorT) { 86 foreach (v; visitor) { 87 v.incr(); 88 } 89 90 scope (success) { 91 foreach (v; visitor) { 92 v.decr(); 93 } 94 } 95 96 } else { 97 visitor.incr(); 98 scope (success) 99 visitor.decr(); 100 } 101 102 () @trusted{ 103 foreach (child, _; Visitor(cursor)) { 104 dispatch(child, visitor); 105 } 106 }(); 107 } 108 109 /** Static wrapping of the cursor followed by a passing it to the visitor. 110 * 111 * The cursor is wrapped in the class that corresponds to the kind of the 112 * cursor. 113 * 114 * Note that the mixins shall be ordered alphabetically. 115 */ 116 void dispatch(VisitorT)(ref const(Cursor) cursor, VisitorT visitor) @safe { 117 import cpptooling.analyzer.clang.ast.nodes; 118 import std.conv : to; 119 120 switch (cursor.kind) { 121 mixin(wrapCursor!(visitor, cursor)(AttributeSeq)); 122 mixin(wrapCursor!(visitor, cursor)(DeclarationSeq)); 123 mixin(wrapCursor!(visitor, cursor)(DirectiveSeq)); 124 mixin(wrapCursor!(visitor, cursor)(ExpressionSeq)); 125 mixin(wrapCursor!(visitor, cursor)(ExtraSeq)); 126 mixin(wrapCursor!(visitor, cursor)(PreprocessorSeq)); 127 mixin(wrapCursor!(visitor, cursor)(ReferenceSeq)); 128 mixin(wrapCursor!(visitor, cursor)(StatementSeq)); 129 mixin(wrapCursor!(visitor, cursor)(TranslationUnitSeq)); 130 131 default: 132 debug logger.trace("Node not handled:", to!string(cursor.kind)); 133 } 134 } 135 136 private: 137 138 string wrapCursor(alias visitor, alias cursor)(immutable(string)[] cases) { 139 import std.format : format; 140 141 static if (is(typeof(visitor) : T[], T)) { 142 // is an array 143 enum visit = "foreach (v; " ~ visitor.stringof ~ ") { v.visit(wrapped); }"; 144 } else { 145 enum visit = visitor.stringof ~ ".visit(wrapped);"; 146 } 147 148 //TODO allocate in an allocator, not GC with "new" 149 string result; 150 151 foreach (case_; cases) { 152 result ~= format("case CXCursorKind.%s: auto wrapped = new %s(%s); %s break;\n", 153 case_, makeNodeClassName(case_), cursor.stringof, visit); 154 } 155 156 return result; 157 } 158 159 @("shall generate code for a case block that wraps a libclang Cursor in the correct class") 160 unittest { 161 import std.conv : to; 162 163 enum Dummy { 164 xCase1, 165 xCase2 166 } 167 168 int visitor; 169 int cursor; 170 171 wrapCursor!(visitor, cursor)(["Dummy.xCase1", "Dummy.xCase2"]).shouldEqual( 172 "case Dummy.xCase1: auto wrapped = new Case1(cursor); visitor.visit(wrapped); break; 173 case Dummy.xCase2: auto wrapped = new Case2(cursor); visitor.visit(wrapped); break; 174 "); 175 } 176 177 @("shall route nodes visits to the base node of each group") 178 @safe unittest { 179 import cpptooling.analyzer.clang.ast : Visitor; 180 import cpptooling.analyzer.clang.ast.nodes; 181 182 final class TestVisitor : Visitor { 183 alias visit = Visitor.visit; 184 185 bool attribute; 186 override void visit(const(Attribute)) { 187 attribute = true; 188 } 189 190 bool declaration; 191 override void visit(const(Declaration)) { 192 declaration = true; 193 } 194 195 bool directive; 196 override void visit(const(Directive)) { 197 directive = true; 198 } 199 200 bool expression; 201 override void visit(const(Expression)) { 202 expression = true; 203 } 204 205 bool extra; 206 override void visit(const(Extra)) { 207 extra = true; 208 } 209 210 bool preprocessor; 211 override void visit(const(Preprocessor)) { 212 preprocessor = true; 213 } 214 215 bool reference; 216 override void visit(const(Reference)) { 217 reference = true; 218 } 219 220 bool statement; 221 override void visit(const(Statement)) { 222 statement = true; 223 } 224 225 bool translationunit; 226 override void visit(const(TranslationUnit)) { 227 translationunit = true; 228 } 229 } 230 231 auto test = new TestVisitor; 232 233 Cursor cursor; 234 235 foreach (kind; [__traits(getMember, CXCursorKind, AttributeSeq[0]), __traits(getMember, 236 CXCursorKind, DeclarationSeq[0]), __traits(getMember, CXCursorKind, DirectiveSeq[0]), __traits(getMember, 237 CXCursorKind, ExpressionSeq[0]), __traits(getMember, CXCursorKind, 238 ExtraSeq[0]), __traits(getMember, CXCursorKind, 239 PreprocessorSeq[0]), __traits(getMember, CXCursorKind, ReferenceSeq[0]), __traits(getMember, CXCursorKind, 240 StatementSeq[0]), __traits(getMember, CXCursorKind, TranslationUnitSeq[0])]) { 241 switch (kind) { 242 mixin(wrapCursor!(test, cursor)(AttributeSeq)); 243 mixin(wrapCursor!(test, cursor)(DeclarationSeq)); 244 mixin(wrapCursor!(test, cursor)(DirectiveSeq)); 245 mixin(wrapCursor!(test, cursor)(ExpressionSeq)); 246 mixin(wrapCursor!(test, cursor)(ExtraSeq)); 247 mixin(wrapCursor!(test, cursor)(PreprocessorSeq)); 248 mixin(wrapCursor!(test, cursor)(ReferenceSeq)); 249 mixin(wrapCursor!(test, cursor)(StatementSeq)); 250 mixin(wrapCursor!(test, cursor)(TranslationUnitSeq)); 251 default: 252 assert(0); 253 } 254 } 255 256 test.attribute.shouldBeTrue; 257 test.declaration.shouldBeTrue; 258 test.directive.shouldBeTrue; 259 test.expression.shouldBeTrue; 260 test.extra.shouldBeTrue; 261 test.preprocessor.shouldBeTrue; 262 test.reference.shouldBeTrue; 263 test.statement.shouldBeTrue; 264 test.translationunit.shouldBeTrue; 265 }