1 /**
2 Copyright: Copyright (c) 2016, 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 Algorithm for classifying a class such as being "pure virtual".
11 */
12 module cpptooling.data.class_classification;
13 
14 import std.typecons : Flag;
15 import logger = std.experimental.logger;
16 
17 import cpptooling.data.representation : CppClass, CppMethod, CppMethodOp, CppCtor, CppDtor;
18 import cpptooling.data.type : MemberVirtualType;
19 
20 /// The state and result of the classification is in.
21 enum State {
22     Unknown,
23     Normal,
24     Virtual,
25     VirtualDtor, // only one method, a d'tor and it is virtual
26     Abstract,
27     Pure
28 }
29 
30 /// The type the method is.
31 enum MethodKind {
32     Unknown,
33     Method,
34     Ctor,
35     Dtor,
36 }
37 
38 /// Input data for determining the next State.
39 struct InData {
40     /// Metadata regarding normal/virtual/pure of the indata.
41     MemberVirtualType value;
42 
43     /// The kind of method.
44     MethodKind t;
45 }
46 
47 //TODO change to being a template so to!ClassMethod(..) can be used
48 //TODO move to representation.d
49 private auto toInData(T)(T func) @trusted
50 out (result) {
51     assert(result.value != MemberVirtualType.Unknown);
52 }
53 do {
54     import std.variant : visit;
55 
56     //dfmt off
57     return func.visit!(
58         (CppMethod a) => InData(a.classification(), MethodKind.Method),
59         (CppMethodOp a) => InData(a.classification(), MethodKind.Method),
60         // A ctor can't be anything else than Normal
61         (CppCtor a) => InData(MemberVirtualType.Normal, MethodKind.Ctor),
62         (CppDtor a) => InData(a.classification(), MethodKind.Dtor));
63     //dfmt on
64 }
65 
66 /** Classify a class from a current state.
67  *
68  * Problem that this function solve:
69  * Clang have no property that classifies a class as virtual/abstract/pure.
70  *
71  * Design:
72  * The classification is sequential according to an informal FSM.
73  * The classification only depend on the input data. No globals, no side effects.
74  *
75  * Params:
76  *  current = current state of the classification.
77  *  method_kind = kind of method
78  *  method_virtual_type = kind of "virtuality" the function is
79  *  hasMember = a class with any members can at the most be virtual
80  *
81  * Returns: new classification state.
82  */
83 State classifyClass(in State current, in MethodKind method_kind,
84         in MemberVirtualType method_virtual_type, Flag!"hasMember" hasMember) @safe pure {
85     import std.algorithm : among;
86 
87     auto data = InData(method_virtual_type, method_kind);
88     State next = current;
89 
90     final switch (current) {
91     case State.Pure:
92         // a pure interface can't have members
93         if (hasMember) {
94             next = State.Abstract;
95         }  // a non-virtual destructor lowers purity
96         else if (data.t == MethodKind.Dtor && data.value == MemberVirtualType.Normal) {
97             next = State.Abstract;
98         } else if (data.t == MethodKind.Method && data.value == MemberVirtualType.Virtual) {
99             next = State.Abstract;
100         }
101         break;
102     case State.Abstract:
103         // one or more methods are pure, stay at this state
104         break;
105     case State.Virtual:
106         if (data.value == MemberVirtualType.Pure) {
107             next = State.Abstract;
108         }
109         break;
110     case State.VirtualDtor:
111         if (data.value == MemberVirtualType.Pure) {
112             next = State.Pure;
113         } else {
114             next = State.Virtual;
115         }
116         break;
117     case State.Normal:
118         if (data.t.among(MethodKind.Method, MethodKind.Dtor)
119                 && data.value == MemberVirtualType.Pure) {
120             next = State.Abstract;
121         } else if (data.t.among(MethodKind.Method, MethodKind.Dtor)
122                 && data.value == MemberVirtualType.Virtual) {
123             next = State.Virtual;
124         }
125         break;
126     case State.Unknown:
127         // ctor cannot affect purity evaluation
128         if (data.t == MethodKind.Dtor
129                 && data.value.among(MemberVirtualType.Pure, MemberVirtualType.Virtual)) {
130             next = State.VirtualDtor;
131         } else if (data.t != MethodKind.Ctor) {
132             final switch (data.value) {
133             case MemberVirtualType.Unknown:
134                 next = State.Unknown;
135                 break;
136             case MemberVirtualType.Normal:
137                 next = State.Normal;
138                 break;
139             case MemberVirtualType.Virtual:
140                 next = State.Virtual;
141                 break;
142             case MemberVirtualType.Pure:
143                 next = State.Pure;
144                 break;
145             }
146         }
147         break;
148     }
149 
150     debug {
151         import std.conv : to;
152 
153         logger.trace(to!string(current), ":", to!string(data), ":",
154                 to!string(current), "->", to!string(next));
155     }
156 
157     return next;
158 }
159 
160 /// ditto
161 State classifyClass(State current, CppClass.CppFunc p, Flag!"hasMember" hasMember) @safe {
162     auto data = toInData(p);
163     return classifyClass(current, data.t, data.value, hasMember);
164 }