/// <summary>
        /// Constructs most appropriate CLR return type of given routine.
        /// The method handles returning by alias, PHP7 return type, PHPDoc @return tag and result of flow analysis.
        /// In case the routine is an override or can be overriden, the CLR type is a value.
        /// </summary>
        internal static TypeSymbol ConstructClrReturnType(SourceRoutineSymbol routine)
        {
            var compilation = routine.DeclaringCompilation;

            // if the method is generator and can't be overriden then the return type must be generator
            // TODO: would not be necessary if GN_SGS got fixed (the routine could report the return type correctly itself)
            if (routine.IsGeneratorMethod())
            {
                // if non-virtual -> return Generator directly
                if (IsNotOverriding(routine))
                {
                    return(compilation.CoreTypes.Generator);
                }
                else //can't be sure -> play safe with PhpValue
                {
                    return(compilation.CoreTypes.PhpValue);
                }
            }

            // &
            if (routine.SyntaxSignature.AliasReturn)
            {
                return(compilation.CoreTypes.PhpAlias);
            }

            // : return type
            if (routine.SyntaxReturnType != null)
            {
                return(compilation.GetTypeFromTypeRef(routine.SyntaxReturnType));
            }

            // for non virtual methods:
            if (IsNotOverriding(routine))
            {
                // /** @return */
                var typeCtx = routine.TypeRefContext;
                if (routine.PHPDocBlock != null && (compilation.Options.PhpDocTypes & PhpDocTypes.ReturnTypes) != 0)
                {
                    var returnTag = routine.PHPDocBlock.Returns;
                    if (returnTag != null && returnTag.TypeNames.Length != 0)
                    {
                        var tmask = PHPDoc.GetTypeMask(typeCtx, returnTag.TypeNamesArray, routine.GetNamingContext());
                        if (!tmask.IsVoid && !tmask.IsAnyType)
                        {
                            return(compilation.GetTypeFromTypeRef(typeCtx, tmask));
                        }
                    }
                }

                // determine from code flow
                return(compilation.GetTypeFromTypeRef(typeCtx, routine.ResultTypeMask));
            }
            else
            {
                // TODO: an override that respects the base? check routine.ResultTypeMask (flow analysis) and base implementation is it matches
            }

            // any value by default
            return(compilation.CoreTypes.PhpValue);
        }
예제 #2
0
        public SourceGeneratorSymbol(SourceRoutineSymbol originalRoutine)
            : base(originalRoutine.ContainingType, string.Format(WellKnownPchpNames.GeneratorStateMachineNameFormatString, originalRoutine.RoutineName),
                   isstatic: true, isvirtual: false,
                   returnType: originalRoutine.DeclaringCompilation.CoreTypes.Void, ps: null)
        {
            Debug.Assert(originalRoutine.IsGeneratorMethod());

            // Need to postpone settings the params because I can't access 'this' in base constructor call
            base.SetParameters(CreateParameters(originalRoutine));
        }