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