// Unity ships annotations, but they're out of date. Specifically, the [PublicAPI] attribute is
        // defined with [MeansImplicitUse] instead of [MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)]
        // So if applied to a class, only the class is marked as in use, while the members aren't. This
        // provider will apply [UsedImplicitly(ImplicitUseTargetFlags.WithMembers)] to any type that
        // has the old [PublicAPI] applied
        private static bool GetPublicAPIImplicitlyUsedAttribute(IClrDeclaredElement element,
                                                                out ICollection <IAttributeInstance> collection)
        {
            collection = EmptyList <IAttributeInstance> .InstanceList;

            if (!(element is ITypeElement type) || !element.IsFromUnityProject())
            {
                return(false);
            }

            foreach (var attributeInstance in type.GetAttributeInstances(ourPublicAPIAttribute, false))
            {
                var attributeType = attributeInstance.GetAttributeType();
                if (!(attributeType.Resolve().DeclaredElement is ITypeElement typeElement))
                {
                    continue;
                }

                var meansImplicitUse = typeElement.GetAttributeInstances(ourMeansImplicitUseAttribute, false)
                                       .FirstOrDefault();
                if (meansImplicitUse?.Constructor == null || !meansImplicitUse.Constructor.IsDefault)
                {
                    continue;
                }

                var flagsType = TypeFactory.CreateTypeByCLRName(ourImplicitUseTargetFlags, element.Module);
                collection = new[]
                {
                    new SpecialAttributeInstance(
                        ourUsedImplicitlyAttributeFullName, element.Module, () => new[]
                        { new AttributeValue(new ConstantValue(3, flagsType)) }
                        )
                };
                return(true);
            }

            return(false);
        }
        // Treat Unity's RangeAttribute as ReSharper's ValueRangeAttribute annotation
        private bool GetValueRangeAttribute(IClrDeclaredElement element,
                                            out ICollection <IAttributeInstance> collection)
        {
            collection = EmptyList <IAttributeInstance> .InstanceList;

            if (!(element is IField field) || !element.IsFromUnityProject())
            {
                return(false);
            }

            if (!myUnityApi.IsSerialisedField(field))
            {
                return(false);
            }

            // Integer value analysis only works on integers, but it will make use of annotations applied to values that
            // are convertible to int, such as byte/sbyte and short/ushort. It doesn't currently use values applied to
            // uint, or long/ulong, but it is planned, so we'll apply to all sizes of integer.
            var predefinedType = myPredefinedTypeCache.GetOrCreatePredefinedType(element.Module);

            if (!Equals(field.Type, predefinedType.Int) && !Equals(field.Type, predefinedType.Uint) &&
                !Equals(field.Type, predefinedType.Long) && !Equals(field.Type, predefinedType.Ulong) &&
                !Equals(field.Type, predefinedType.Short) && !Equals(field.Type, predefinedType.Ushort) &&
                !Equals(field.Type, predefinedType.Byte) && !Equals(field.Type, predefinedType.Sbyte))
            {
                return(false);
            }

            foreach (var attributeInstance in field.GetAttributeInstances(KnownTypes.RangeAttribute, false))
            {
                // Values are floats, but applied to an integer field. Convert to integer values
                var unityFrom = attributeInstance.PositionParameter(0);
                var unityTo   = attributeInstance.PositionParameter(1);

                if (!unityFrom.IsConstant || !unityFrom.ConstantValue.IsFloat() ||
                    !unityTo.IsConstant || !unityTo.ConstantValue.IsFloat())
                {
                    continue;
                }

                // The check above means this is not null. We take the floor, because that's how Unity works.
                // E.g. Unity's Inspector treats [Range(1.7f, 10.9f)] as between 1 and 10 inclusive
                var from = Convert.ToInt64(Math.Floor((float)unityFrom.ConstantValue.Value.NotNull()));
                var to   = Convert.ToInt64(Math.Floor((float)unityTo.ConstantValue.Value.NotNull()));

                collection = CreateRangeAttributeInstance(element, predefinedType, from, to);
                return(true);
            }

            foreach (var attributeInstance in field.GetAttributeInstances(KnownTypes.MinAttribute, false))
            {
                var unityMinValue = attributeInstance.PositionParameter(0);

                if (!unityMinValue.IsConstant || !unityMinValue.ConstantValue.IsFloat())
                {
                    continue;
                }

                // Even though the constructor for ValueRange takes long, it only works with int.MaxValue
                var min = Convert.ToInt64(Math.Floor((float)unityMinValue.ConstantValue.Value.NotNull()));
                var max = int.MaxValue;

                collection = CreateRangeAttributeInstance(element, predefinedType, min, max);
                return(true);
            }

            return(false);
        }