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 dextool.type : Path; 15 16 import libclang_ast.ast : Visitor; 17 import cpptooling.data : CppRoot, CppClass, CppMethod, CppCtor, CppDtor, 18 CFunction, CppNamespace, LocationTag, Location; 19 import cpptooling.data.symbol : USRType; 20 21 import dextool.plugin.cpptestdouble.backend.interface_; 22 23 /// Data derived during analyze 24 struct AnalyzeData { 25 import cpptooling.data.symbol.types : FullyQualifiedNameType; 26 27 static auto make() { 28 AnalyzeData r; 29 r.root = CppRoot.make; 30 return r; 31 } 32 33 CppRoot root; 34 35 /// Classes found during src analysis. 36 CppClass[FullyQualifiedNameType] classes; 37 38 void putForLookup(CppClass c) { 39 classes[c.fullyQualifiedName] = c; 40 } 41 } 42 43 private enum VisitorKind { 44 root, 45 child 46 } 47 48 alias CppTUVisitor = CppVisitor!(VisitorKind.root); 49 50 final class CppVisitor(VisitorKind RootT) : Visitor { 51 import std.typecons : scoped, NullableRef; 52 53 import libclang_ast.ast : UnexposedDecl, VarDecl, FunctionDecl, ClassDecl, 54 Namespace, TranslationUnit, generateIndentIncrDecr, StructDecl; 55 import cpptooling.analyzer.clang.analyze_helper : analyzeFunctionDecl, analyzeVarDecl; 56 import cpptooling.data : CppRoot, CxGlobalVariable, CppNsStack, 57 CxReturnType, CppNs, TypeKindVariable; 58 import cpptooling.data.symbol : Container; 59 import libclang_ast.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(scope 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(scope const VarDecl v) { 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(scope 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(scope const ClassDecl v) { 138 visitRecord(v); 139 } 140 141 override void visit(scope const StructDecl v) { 142 visitRecord(v); 143 } 144 145 void visitRecord(T)(scope const T v) @trusted { 146 import std.typecons : scoped; 147 import cpptooling.analyzer.clang.analyze_helper : ClassVisitor, analyzeRecord; 148 149 ///TODO add information if it is a public/protected/private class. 150 ///TODO add metadata to the class if it is a definition or declaration 151 152 mixin(mixinNodeLog!()); 153 154 auto result = analyzeRecord(v, container, indent + 1); 155 debug logger.trace("class: ", result.name); 156 157 // TODO remove the zero length check to add support for typedef'ed structs. 158 // Example: 159 // typedef struct { 160 // } Struct; 161 162 if (result.name.length != 0 && v.cursor.isDefinition) { 163 auto visitor = scoped!ClassVisitor(v, ns_stack, result, container, indent + 1); 164 v.accept(visitor); 165 166 root.put(visitor.root); 167 analyze_data.putForLookup(visitor.root); 168 } 169 } 170 171 override void visit(scope const Namespace v) @trusted { 172 mixin(mixinNodeLog!()); 173 174 () @trusted { ns_stack ~= CppNs(v.cursor.spelling); }(); 175 // pop the stack when done 176 scope (exit) 177 ns_stack = ns_stack[0 .. $ - 1]; 178 179 auto ns_visitor = scoped!(CppVisitor!(VisitorKind.child))(ctrl, prod, 180 indent, ns_stack, analyze_data, container); 181 182 v.accept(ns_visitor); 183 184 // fill the namespace with content from the analysis 185 root.put(ns_visitor.root); 186 } 187 188 override void visit(scope const TranslationUnit v) { 189 import std.algorithm : filter; 190 import cpptooling.analyzer.clang.type : makeLocation; 191 import cpptooling.testdouble.header_filter : LocationType; 192 193 mixin(mixinNodeLog!()); 194 195 LocationTag tu_loc; 196 () @trusted { tu_loc = LocationTag(Location(v.cursor.spelling, 0, 0)); }(); 197 198 if (tu_loc.kind != LocationTag.Kind.noloc && ctrl.doFile(tu_loc.file, 199 "root " ~ tu_loc.toString)) { 200 prod.putLocation(Path(tu_loc.file), LocationType.Root); 201 } 202 203 v.accept(this); 204 } 205 }