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 }