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 }