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 }