public static bool TryCreate(GeneratorContext context, CreateMemoizeInterfaceContext interfaceContext, IMethodSymbol methodSymbol, [NotNullWhen(true)] out MemoizedMethodMember?method)
        {
            var @params = methodSymbol.Parameters;
            var args    = new List <MemoizedMethodMemberArgument>(@params.Length);

            var attributes = methodSymbol.GetAttributes();

            var slidingCache = SlidingCache.MaybeCreate(context, attributes);

            foreach (var param in @params)
            {
                if (MemoizedMethodMemberArgument.TryCreate(context, interfaceContext.ErrorLocation, param, out var arg))
                {
                    args.Add(arg);
                }
                else
                {
                    method = null;
                    return(false);
                }
            }

            var returnType = methodSymbol.ReturnType;
            var isAsync    = methodSymbol.IsTaskOfTOrValueTaskOfT();

            bool        typeIsNullable;
            bool        typeIsReferenceType;
            ITypeSymbol typeInCache;

            if (isAsync)
            {
                if (returnType is not INamedTypeSymbol {
                    IsGenericType : true
                } namedTypeSymbol || namedTypeSymbol.TypeArguments.Length != 1)
                {
                    context.CreateError("Async return types must return something", $"Expected 1 generic type argument for {methodSymbol.Name}", interfaceContext.ErrorLocation);
                    method = null;
                    return(false);
                }

                var taskReturnObj = namedTypeSymbol.TypeArguments[0];

                // TODO check it has SizeOf()
                // we dont care if they are IEquatable, but we do care they implement .SizeOf() at somepoint

                /*
                 * if (!taskReturnObj.AllInterfaces.Any(x => x.MetadataName == "IEquatable`1"))
                 * {
                 *  context.CreateError("Return types must implement IEquatable", $"Async return type Task<{taskReturnObj.Name}> does not implement IEquatable", interfaceContext.ErrorLocation);
                 *  method = null;
                 *  return false;
                 * }
                 */

                typeInCache    = taskReturnObj;
                typeIsNullable = taskReturnObj.NullableAnnotation == NullableAnnotation.Annotated;

                typeIsReferenceType = taskReturnObj.IsReferenceType;
            }
            else
            {
                typeInCache    = methodSymbol.ReturnType;
                typeIsNullable = methodSymbol.ReturnType.NullableAnnotation == NullableAnnotation.Annotated;

                typeIsReferenceType = methodSymbol.ReturnType.IsReferenceType;
            }

            var returnTypeAttributes = methodSymbol.GetReturnTypeAttributes();

            string?globalSizeOfMethod = null;
            string?selfSizeOfMethod   = null;

            // TODO [Attribute] set on the Interface that allows you to specify a class that can calculate this for you
            // Check TypeInCache has .SizeOfInBytes() method
            // Check for Attribute
            var sizeOfAttributeData = returnTypeAttributes.FirstOrDefault(x => SymbolEqualityComparer.Default.Equals(x.AttributeClass, context.SizeOfResultAttribute));

            if (sizeOfAttributeData == null && context.GlobalSizeOfAttribute == SizeOfAttributeData.Empty)
            {
                var sizeOfInBytesMethod = typeInCache.GetMembers().OfType <IMethodSymbol>().FirstOrDefault(x => x.Name == "SizeOfInBytes" && x.ReturnType.IsLong());
                if (sizeOfInBytesMethod == null)
                {
                    context.CreateError("Missing SizeOfInBytes function", $"Return type '{typeInCache.ToDisplayString()}' must have a 'public long SizeOfInBytes()' function or use SizeOfResultAttribute.", interfaceContext.ErrorLocation);
                    method = null;
                    return(false);
                }
                else
                {
                    selfSizeOfMethod = "SizeOfInBytes";
                }
            }
            else
            {
                if (sizeOfAttributeData == null)
                {
                    if (context.GlobalSizeOfAttribute == SizeOfAttributeData.Empty)
                    {
                        context.CreateError("Missing SizeOfInBytes function", $"Return type '{typeInCache.ToDisplayString()}' must have a 'public long SizeOfInBytes()' function or use SizeOfResultAttribute.", interfaceContext.ErrorLocation);
                        method = null;
                        return(false);
                    }

                    (globalSizeOfMethod, selfSizeOfMethod) = context.GlobalSizeOfAttribute;
                }
                else
                {
                    (globalSizeOfMethod, selfSizeOfMethod) = SizeOfAttributeData.Parse(sizeOfAttributeData);
                }
            }

            var returnTypeSizeOfMethod = new MemoizedMethodSizeOfFunction(selfSizeOfMethod, globalSizeOfMethod);

            method = new MemoizedMethodMember(methodSymbol, args, slidingCache, isAsync, returnType.ToDisplayString(), typeInCache, typeIsNullable, typeIsReferenceType, returnTypeSizeOfMethod);

            return(true);
        }
예제 #2
0
        public static bool TryCreate
        (
            GeneratorContext context,
            MemberAccessExpressionSyntax expressionSyntax,
            ITypeSymbol interfaceType,
            ITypeSymbol implementationType,
            string mode,
            [NotNullWhen(true)] out MemoizerCall?call)
        {
            var interfaceAttributes = interfaceType.GetAttributes();

            if (!context.TryGetInterfaceContext(interfaceType, out var interfaceContext))
            {
                // The Interface could exist in a referenced project which we dont have the interfacesyntax for.. lets check that
                var memoizeAttribute2 = interfaceAttributes.FirstOrDefault(x => SymbolEqualityComparer.Default.Equals(x.AttributeClass, context.CreateMemoizedAttribute));

                if (memoizeAttribute2 == null)
                {
                    context.CreateError("Missing Memoized Attribute", "Interface must have the [CreateMemoizedImplementation] attribute attached", expressionSyntax.Name.GetLocation());
                    call = null;
                    return(false);
                }

                interfaceContext = CreateMemoizeInterfaceContext.CreateFromType(interfaceType, memoizeAttribute2, expressionSyntax.GetLocation());
            }

            var memoizeAttribute = interfaceContext.CreateMemoizedAttributeData;

            if (!TryGetClassName(memoizeAttribute, interfaceType, out var className, out var humanId))
            {
                call = null;
                return(false);
            }

            var memoizerFactory = memoizeAttribute.NamedArguments.FirstOrDefault(x => x.Key == nameof(CreateMemoizedImplementationAttribute.MemoizerFactory)).Value;
            INamedTypeSymbol?memoizerFactoryTypeSymbol = null;

            if (!memoizerFactory.IsNull)
            {
                memoizerFactoryTypeSymbol = memoizerFactory.Value as INamedTypeSymbol;
            }

            if (memoizerFactoryTypeSymbol != null)
            {
                if (!memoizerFactoryTypeSymbol.AllInterfaces.Any(i => SymbolEqualityComparer.Default.Equals(i, context.MemoizerFactoryInterface)))
                {
                    context.CreateError($"Wrong {nameof(CreateMemoizedImplementationAttribute.MemoizerFactory)} type", $"MemoizerFactory type must implement {nameof(IMemoizerFactory)}", interfaceContext.InterfaceWithAttribute?.GetLocation() ?? expressionSyntax.Name.GetLocation());
                    call = null;
                    return(false);
                }
            }

            var slidingCache = SlidingCache.MaybeCreate(context, interfaceAttributes);

            var members       = interfaceType.GetMembers();
            var methods       = new List <MemoizedMethodMember>(members.Length);
            var methodSymbols = members.OfType <IMethodSymbol>().ToList();

            // TODO Create different ArgKey class name implementations
            // then Check names to see what ArgKey_ class name algo we can use

            foreach (var interfaceMethod in methodSymbols)
            {
                if (MemoizedMethodMember.TryCreate(context, interfaceContext, interfaceMethod, out var methodMember))
                {
                    methods.Add(methodMember);
                }
                else
                {
                    call = null;
                    return(false);
                }
            }

            var @namespace = GetNamespaceFrom(expressionSyntax);

            call = new MemoizerCall(interfaceType, implementationType, className, methods, slidingCache, memoizerFactoryTypeSymbol, humanId, @namespace, mode);

            return(true);
        }