1 /**
2 Copyright: Copyright (c) 2017, Joakim Brännström. All rights reserved.
3 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 
6 This file tests the class visitor for C++.
7 */
8 module test.component.analyzer.cpp_class_visitor;
9 
10 import std.conv : to;
11 import std.format : format;
12 import std.typecons : scoped, Yes;
13 import std.variant : visit;
14 import logger = std.experimental.logger;
15 
16 import unit_threaded;
17 import test.clang_util;
18 import test.helpers;
19 
20 // TODO this is a mega import. Reduce it
21 import cpptooling.data;
22 
23 import cpptooling.analyzer.clang.ast;
24 import cpptooling.analyzer.clang.analyze_helper;
25 import cpptooling.analyzer.clang.context : ClangContext;
26 import cpptooling.analyzer.clang.cursor_logger : logNode, mixinNodeLog;
27 import cpptooling.analyzer.clang.type;
28 import cpptooling.data.symbol : Container;
29 import cpptooling.data.representation;
30 import cpptooling.data : TypeKindVariable, VariadicType, Location, CppNsStack,
31     USRType, toStringDecl;
32 import cpptooling.utility.virtualfilesystem : FileName, Content;
33 
34 /* These lines are useful when debugging.
35 import unit_threaded;
36 writelnUt(visitor.container.toString);
37 */
38 
39 final class TestVisitor : Visitor {
40     import cpptooling.analyzer.clang.ast;
41 
42     alias visit = Visitor.visit;
43     mixin generateIndentIncrDecr;
44 
45     Container container;
46 
47     /// The USR to find.
48     USRType find;
49 
50     CppClass[] classes;
51     bool found;
52 
53     override void visit(const(TranslationUnit) v) {
54         mixin(mixinNodeLog!());
55         v.accept(this);
56     }
57 
58     override void visit(const(ClassDecl) v) @trusted {
59         mixin(mixinNodeLog!());
60 
61         import std.typecons : scoped;
62         import cpptooling.analyzer.clang.analyze_helper : ClassVisitor;
63         import cpptooling.analyzer.clang.type : retrieveType;
64         import cpptooling.analyzer.clang.store : put;
65 
66         ///TODO add information if it is a public/protected/private class.
67         ///TODO add metadata to the class if it is a definition or declaration
68 
69         auto result = analyzeRecord(v, container, indent + 1);
70         debug logger.trace("class: ", result.name);
71 
72         if (v.cursor.isDefinition) {
73             auto visitor = scoped!ClassVisitor(v, CppNsStack.init, result, container, indent + 1);
74             v.accept(visitor);
75             classes ~= visitor.root;
76         } else {
77             auto type = retrieveType(v.cursor, container, indent);
78             put(type, container, indent);
79         }
80     }
81 }
82 
83 @("shall be a class with a method using the typedef as return value")
84 unittest {
85     // This test result in the return type for some_func being
86     // `const unsigned int( &x)[1]` in clang 3.7/3.8.
87     // But seems to work in 3.9
88 
89     immutable code = "
90 namespace ns1 {
91 typedef unsigned int some_unsigned;
92 typedef some_unsigned the_type;
93 typedef the_type some_array[1];
94 }
95 
96 class Class {
97 public:
98     const ns1::some_array& some_func();
99 };
100 ";
101 
102     // arrange
103     auto ctx = ClangContext(Yes.useInternalHeaders, Yes.prependParamSyntaxOnly);
104     ctx.virtualFileSystem.openAndWrite(cast(FileName) "/issue.hpp", cast(Content) code);
105     auto tu = ctx.makeTranslationUnit("/issue.hpp");
106     auto visitor = new TestVisitor;
107     //visitor.find = "c:@F@some_func#";
108 
109     // act
110     auto ast = ClangAST!(typeof(visitor))(tu.cursor);
111     ast.accept(visitor);
112 
113     // assert
114     checkForCompilerErrors(tu).shouldBeFalse;
115     visitor.classes.length.shouldEqual(1);
116     visitor.classes[0].methodPublicRange.length.shouldEqual(1);
117 
118     import std.variant : visit;
119 
120     CppMethod method;
121 
122     // dfmt off
123     visitor.classes[0].methodPublicRange[0].visit!(
124         (const CppMethod a) { method = cast(CppMethod) a; return; },
125         (const CppMethodOp a) => 0.shouldEqual(1),
126         (const CppCtor a) => 0.shouldEqual(1),
127         (const CppDtor a) => 0.shouldEqual(1));
128     // dfmt on
129 
130     method.returnType.toStringDecl("x").shouldEqual("const ns1::some_array &x");
131 }