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 }