private static bool IsCustomDataType()

in plugins/toolkit/jetbrains-rider/ReSharper.AWS/src/AWS.Psi/Lambda/LambdaFinder.cs [105:237]


        private static bool IsCustomDataType(IType type)
        {
            return IsCustomDataType(type, new HashSet<IType>());

            // "typesUnderProcess" store all types that are processing right now. Is used to avoid falling into infinitive recursion
            bool IsCustomDataType(IType typeToVerify, HashSet<IType> typesUnderProcess)
            {
                if (!typesUnderProcess.Add(typeToVerify)) return true;

                if (ourLogger.IsTraceEnabled())
                    ourLogger.Trace("Check is Custom Data for a type: {0}",
                        typeToVerify.GetPresentableName(CSharpLanguage.Instance));

                if (typeToVerify.IsVoid()) return false;

                // Skip any primitive types, DateTime, and DateTimeOffset according to Newtonsoft.Json.Serialization logic.
                if (typeToVerify.IsSimplePredefined() || typeToVerify.IsDateTime() || typeToVerify.IsDateTimeOffset()) return true;

                switch (typeToVerify)
                {
                    case IArrayType arrayType:
                        return IsCustomDataType(arrayType.ElementType, typesUnderProcess);

                    case IDeclaredType declaredType:
                    {
                        var predefinedType = declaredType.Module.GetPredefinedType();

                        var typeElement = declaredType.GetTypeElement();
                        if (ourLogger.IsTraceEnabled())
                            ourLogger.Trace("Check type element: {0}", typeElement?.GetClrName());
                        if (typeElement == null) return false;

                        // Define a substitution to verify generic types.
                        var substitution = declaredType.GetSubstitution();

                        // Check for dictionary types.
                        var genericDictionaryTypeElement = predefinedType.GenericIDictionary.GetTypeElement();
                        if (genericDictionaryTypeElement != null &&
                            typeElement.IsDescendantOf(genericDictionaryTypeElement))
                        {
                            var keyTypeParameter = genericDictionaryTypeElement.TypeParameters[0];
                            var valueTypeParameter = genericDictionaryTypeElement.TypeParameters[1];

                            foreach (var ancestorSubstitution in typeElement.GetAncestorSubstitution(
                                genericDictionaryTypeElement))
                            {
                                // Define a case when inner class override one TKey or TValue, e.g.
                                // class MyType<T> : IDictionary<int, T> {}
                                var effectiveSubstitution = ancestorSubstitution.Apply(substitution);

                                var keyType = effectiveSubstitution.Apply(keyTypeParameter);
                                if (!IsCustomDataType(keyType, typesUnderProcess)) return false;

                                var valueType = effectiveSubstitution.Apply(valueTypeParameter);
                                if (!IsCustomDataType(valueType, typesUnderProcess)) return false;
                            }

                            return true;
                        }

                        // Check for collection types.
                        var elementTypes =
                            CollectionTypeUtil.GetElementTypesForGenericType(
                                declaredType, predefinedType.GenericIEnumerable, 0)
                            ?? CollectionTypeUtil.GetElementTypesForGenericType(
                                declaredType, predefinedType.GenericIList, 0);

                        if (elementTypes != null)
                        {
                            return elementTypes.All(elementType => IsCustomDataType(elementType, typesUnderProcess));
                        }

                        // Check non-generic collection and map types
                        // assuming that value is of type Object and is always valid option.
                        if (declaredType.IsSubtypeOf(predefinedType.IEnumerable))
                        {
                            return true;
                        }

                        // Check for POCO types
                        switch (typeElement)
                        {
                            case IClass classTypeElement:
                            {
                                var superClass = classTypeElement.GetBaseClassType();
                                if (!superClass.IsObject()) return false;

                                return classTypeElement.CanInstantiateWithPublicDefaultConstructor() &&
                                       CheckMemberTypes(classTypeElement.GetMembers(), substitution, typesUnderProcess);
                            }
                            case IStruct structTypeElement:
                                return CheckMemberTypes(structTypeElement.GetMembers(), substitution, typesUnderProcess);
                        }

                        break;
                    }
                }

                return false;
            }

            // Check all fields and properties inside a class or struct for a custom data type
            bool CheckMemberTypes(IEnumerable<ITypeMember> members, ISubstitution substitution, HashSet<IType> typesUnderProcess)
            {
                var typeMembers = members.AsArray();

                if (ourLogger.IsTraceEnabled())
                    ourLogger.Trace("Verify members: {0}", string.Join(", ", typeMembers.Select(member => member.ShortName)));

                foreach (var typeMember in typeMembers)
                {
                    if (typeMember.IsStatic) continue;

                    switch (typeMember)
                    {
                        case IField field when field.IsField:
                        {
                            var fieldType = substitution.Apply(field.Type);
                            if (!IsCustomDataType(fieldType, typesUnderProcess)) return false;
                            break;
                        }
                        case IProperty property when !property.IsDefault:
                        {
                            var propertyType = substitution.Apply(property.Type);
                            if (!IsCustomDataType(propertyType, typesUnderProcess)) return false;
                            break;
                        }
                    }
                }

                return true;
            }
        }