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