1 /** 2 Copyright: Copyright (c) 2017, 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 dextool.plugin.cpptestdouble.backend.visitor; 11 12 import logger = std.experimental.logger; 13 14 import cpptooling.analyzer.clang.ast : Visitor; 15 import cpptooling.data : CppRoot, CppClass, CppMethod, CppCtor, CppDtor, 16 CFunction, CppNamespace, LocationTag, Location; 17 import cpptooling.data.symbol : USRType; 18 19 import dextool.plugin.cpptestdouble.backend.interface_; 20 21 /// Data derived during analyze 22 struct AnalyzeData { 23 import cpptooling.data.symbol.types : FullyQualifiedNameType; 24 25 static auto make() { 26 AnalyzeData r; 27 r.root = CppRoot.make; 28 return r; 29 } 30 31 CppRoot root; 32 33 /// Classes found during src analysis. 34 CppClass[FullyQualifiedNameType] classes; 35 36 void putForLookup(CppClass c) { 37 classes[c.fullyQualifiedName] = c; 38 } 39 } 40 41 private enum VisitorKind { 42 root, 43 child 44 } 45 46 alias CppTUVisitor = CppVisitor!(VisitorKind.root); 47 48 final class CppVisitor(VisitorKind RootT) : Visitor { 49 import std.typecons : scoped, NullableRef; 50 51 import cpptooling.analyzer.clang.ast : UnexposedDecl, VarDecl, FunctionDecl, 52 ClassDecl, Namespace, TranslationUnit, generateIndentIncrDecr, 53 StructDecl; 54 import cpptooling.analyzer.clang.analyze_helper : analyzeFunctionDecl, 55 analyzeVarDecl; 56 import cpptooling.data : CppRoot, CxGlobalVariable, CppNsStack, 57 CxReturnType, CppNs, TypeKindVariable; 58 import cpptooling.data.symbol : Container; 59 import cpptooling.analyzer.clang.cursor_logger : logNode, mixinNodeLog; 60 61 alias visit = Visitor.visit; 62 63 mixin generateIndentIncrDecr; 64 65 NullableRef!Container container; 66 NullableRef!AnalyzeData analyze_data; 67 68 private { 69 Controller ctrl; 70 Products prod; 71 CppNsStack ns_stack; 72 } 73 74 static if (RootT == VisitorKind.root) { 75 CppRoot root; 76 77 this(Controller ctrl, Products prod, NullableRef!AnalyzeData analyze, 78 NullableRef!Container container) { 79 this.ctrl = ctrl; 80 this.prod = prod; 81 this.analyze_data = analyze; 82 this.container = container; 83 this.root = CppRoot.make; 84 } 85 } else { 86 CppNamespace root; 87 88 this(Controller ctrl, Products prod, uint indent, CppNsStack ns_stack, 89 NullableRef!AnalyzeData analyze, NullableRef!Container container) { 90 this.root = CppNamespace(ns_stack); 91 this.ctrl = ctrl; 92 this.prod = prod; 93 this.indent = indent; 94 this.ns_stack = ns_stack; 95 this.analyze_data = analyze; 96 this.container = container; 97 } 98 } 99 100 override void visit(const(UnexposedDecl) v) { 101 mixin(mixinNodeLog!()); 102 103 // An unexposed may be: 104 105 // an extern "C" 106 // UnexposedDecl "" extern "C" {... 107 // FunctionDecl "fun_c_linkage" void func_c_linkage 108 v.accept(this); 109 } 110 111 override void visit(const(VarDecl) v) @trusted { 112 import clang.c.Index : CX_StorageClass; 113 114 mixin(mixinNodeLog!()); 115 116 // TODO investigate if linkage() == CXLinkage_External should be used 117 // instead. 118 if (v.cursor.storageClass() == CX_StorageClass.extern_) { 119 auto result = analyzeVarDecl(v, container, indent); 120 auto var = CxGlobalVariable(result.instanceUSR, 121 TypeKindVariable(result.type, result.name)); 122 root.put(var); 123 } 124 } 125 126 override void visit(const(FunctionDecl) v) { 127 mixin(mixinNodeLog!()); 128 129 auto result = analyzeFunctionDecl(v, container, indent); 130 if (result.isValid) { 131 auto func = CFunction(result.type.kind.usr, result.name, result.params, 132 CxReturnType(result.returnType), result.isVariadic, result.storageClass); 133 root.put(func); 134 } 135 } 136 137 override void visit(const(ClassDecl) v) { 138 visitRecord(v); 139 } 140 141 override void visit(const(StructDecl) v) { 142 visitRecord(v); 143 } 144 145 void visitRecord(T)(const T v) @trusted { 146 import std.typecons : scoped; 147 import cpptooling.analyzer.clang.analyze_helper : ClassVisitor, 148 analyzeRecord; 149 150 ///TODO add information if it is a public/protected/private class. 151 ///TODO add metadata to the class if it is a definition or declaration 152 153 mixin(mixinNodeLog!()); 154 155 auto result = analyzeRecord(v, container, indent + 1); 156 debug logger.trace("class: ", result.name); 157 158 // TODO remove the zero length check to add support for typedef'ed structs. 159 // Example: 160 // typedef struct { 161 // } Struct; 162 163 if (result.name.length != 0 && v.cursor.isDefinition) { 164 auto visitor = scoped!ClassVisitor(v, ns_stack, result, container, indent + 1); 165 v.accept(visitor); 166 167 root.put(visitor.root); 168 analyze_data.putForLookup(visitor.root); 169 } 170 } 171 172 override void visit(const(Namespace) v) @trusted { 173 mixin(mixinNodeLog!()); 174 175 () @trusted{ ns_stack ~= CppNs(v.cursor.spelling); }(); 176 // pop the stack when done 177 scope (exit) 178 ns_stack = ns_stack[0 .. $ - 1]; 179 180 auto ns_visitor = scoped!(CppVisitor!(VisitorKind.child))(ctrl, prod, 181 indent, ns_stack, analyze_data, container); 182 183 v.accept(ns_visitor); 184 185 // fill the namespace with content from the analysis 186 root.put(ns_visitor.root); 187 } 188 189 override void visit(const(TranslationUnit) v) { 190 import std.algorithm : filter; 191 import cpptooling.analyzer.clang.type : makeLocation; 192 import cpptooling.testdouble.header_filter : LocationType; 193 import dextool.type : FileName; 194 195 mixin(mixinNodeLog!()); 196 197 LocationTag tu_loc; 198 () @trusted{ tu_loc = LocationTag(Location(v.cursor.spelling, 0, 0)); }(); 199 200 if (tu_loc.kind != LocationTag.Kind.noloc && ctrl.doFile(tu_loc.file, 201 "root " ~ tu_loc.toString)) { 202 prod.putLocation(FileName(tu_loc.file), LocationType.Root); 203 } 204 205 v.accept(this); 206 } 207 }