///// <summary> ///// Gets value determining whether <paramref name="totype"/> type can be assigned from <paramref name="fromtype"/>. ///// </summary> ///// <param name="totype">Type mask which we check whether is assignable from <paramref name="fromtype"/>.</param> ///// <param name="fromtype">Type mask we check whether is equal or is a subclass of <paramref name="totype"/>.</param> ///// <param name="ctx">Type context for resolving class names from type mask.</param> ///// <param name="model">Helper object which caches class inheritance.</param> ///// <remarks> ///// Gets <c>true</c>, if <paramref name="totype"/> is equal to or is a base type of <paramref name="fromtype"/>. ///// Gets <c>False</c> for <c>void</c> type masks. ///// </remarks> //public static bool IsAssignableFrom(this TypeRefMask totype, TypeRefMask fromtype, TypeRefContext/*!*/ctx, ISemanticModel/*!*/model) //{ // Debug.Assert(ctx != null); // Debug.Assert(model != null); // if ((totype & fromtype & ~(ulong)TypeRefMask.FlagsMask) != 0) // return true; // types are equal (or at least one of them is Any Type) // // object <-> unspecified object instance // if ((ctx.IsObject(totype) && ctx.IsObject(fromtype)) && // (ctx.IsAnObject(totype) || ctx.IsAnObject(fromtype))) // return true; // if (IsImplicitConversion(fromtype, totype, ctx, model)) // return true; // // cut off object types (primitive types do not have subclasses) // var selfObjs = ctx.GetObjectTypes(totype); // if (selfObjs.Count == 0) // return false; // self mask does not represent a class // var typeObjs = ctx.GetObjectTypes(fromtype); // if (typeObjs.Count == 0) // return false; // type mask does not represent a class // // build inheritance graph and check whether any type from self is a base of anything in type // if (selfObjs.Count == 1 && typeObjs.Count == 1) // return model.IsAssignableFrom(selfObjs[0].QualifiedName, typeObjs[0].QualifiedName); // // // return model.IsAssignableFrom(selfObjs.Select(t => t.QualifiedName), typeObjs.Select(t => t.QualifiedName)); //} ///// <summary> ///// Determines whether there is an implicit conversion from one type to another. ///// </summary> //private static bool IsImplicitConversion(TypeRefMask fromtype, TypeRefMask totype, TypeRefContext/*!*/ctx, ISemanticModel/*!*/model) //{ // // TODO: optimize bit operations // // // if (ctx.IsArray(totype) && ctx.IsArray(fromtype)) // { // // both types are arrays, type may be more specific (int[]) and self just ([]) // // TODO: check whether their element types are assignable, // // avoid infinite recursion! // return true; // } // // any callable -> "callable" // if (ctx.IsLambda(totype) && IsCallable(fromtype, ctx, model)) // return true; // //// allowed conversions // // int <-> bool // //if (ctx.IsInteger(totype) && ctx.IsBoolean(fromtype)) // int -> bool // // return true; // // int <-> double // if (ctx.IsNumber(fromtype) && ctx.IsNumber(totype)) // TODO: maybe settings for strict number type check // return true; // if (ctx.IsString(totype) && IsConversionToString(fromtype, ctx, model)) // return true; // // // if (ctx.IsNull(fromtype) && ctx.IsNullable(totype)) // return true; // NULL can be assigned to any nullable // // // return false; //} ///// <summary> ///// Determines whether given type can be converted to string without warning. ///// </summary> //internal static bool IsConversionToString(TypeRefMask fromtype, TypeRefContext/*!*/ctx, ISemanticModel/*!*/model) //{ // // primitive -> string // if (ctx.IsPrimitiveType(fromtype)) // return true; // // object with __toString() -> string // if (ctx.IsObject(fromtype) && ctx.GetObjectTypes(fromtype).Any(tref => model.GetClass(tref.QualifiedName).HasMethod(NameUtils.SpecialNames.__toString, model))) // return true; // // // return false; //} /// <summary> /// Checks whether given type may be callable. /// </summary> internal static bool IsCallable(TypeRefContext /*!*/ ctx, TypeRefMask type) { if (type.IsAnyType || type.IsRef || ctx.IsLambda(type) || ctx.IsAString(type) || ctx.IsArray(type) || ctx.IsObject(type)) { return(true); } //// type has "__invoke" method //if (type.IsSingleType) // just optimization //{ // var tref = ctx.GetObjectTypes(type).FirstOrDefault(); // if (tref != null) // { // var node = model.GetClass(tref.QualifiedName); // // type has __invoke method or is assignable from Closure // if (node.HasMethod(NameUtils.SpecialNames.__invoke, model) || model.IsAssignableFrom(NameUtils.SpecialNames.Closure, node)) // return true; // } //} return(false); }
/// <summary> /// Helper method getting parameter type. /// </summary> /// <param name="typeCtx">Routine type context.</param> /// <param name="paramTag">PHPDoc param tag if available.</param> /// <param name="signature">Call signature.</param> /// <param name="call">Call information if called specifically with given context.</param> /// <param name="paramIndex">Parameter index.</param> /// <returns>Expected type of parameter. Cannot be uninitialized.</returns> private static TypeRefMask GetParamType(TypeRefContext /*!*/ typeCtx, PHPDocBlock.ParamTag paramTag, FormalParam syntax, CallInfo call, int paramIndex) { Contract.ThrowIfNull(typeCtx); Debug.Assert(paramIndex >= 0); TypeRefMask result = 0; bool isvariadic = false; bool isalias = false; // lookup actual type hint if (syntax != null) { isvariadic = syntax.IsVariadic; isalias = syntax.PassedByRef || syntax.IsOut; var hint = syntax.TypeHint; if (hint != null) { result = typeCtx.GetTypeMaskFromTypeHint(syntax.TypeHint); if (isvariadic) // PHP 5.6 variadic parameter (...) // TypeHint -> TypeHint[] { result = typeCtx.GetArrayTypeMask(result); } } } if (result.IsUninitialized) { // lookup callInfo result = call.GetParamType(typeCtx, paramIndex); if (result.IsUninitialized) { // lookup PHPDoc if (paramTag != null && paramTag.TypeNamesArray.Length != 0) { result = PHPDoc.GetTypeMask(typeCtx, paramTag.TypeNamesArray); } if (result.IsUninitialized) { // NOTE: if still unknown, we can use type of the FormalParam.InitValue as Hint result = TypeRefMask.AnyType; } } // PHP 5.6, variadic parameter (...) is always of type array, // if specified else, user meant type of its elements if (isvariadic && !typeCtx.IsArray(result)) { result = typeCtx.GetArrayTypeMask(result); // hint -> hint[] } } // result.IsRef = isalias; // Debug.Assert(!result.IsUninitialized); return(result); }