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