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;
}
}