Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trim warnings #10417

Draft
wants to merge 20 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<TargetFrameworks>net7.0;$(NetFrameworkMinimum);$(NetCoreAppMinimum);netstandard2.0;</TargetFrameworks>
<!-- Suppress warning that is caused by Nuget.Frameworks code that we copy. -->
<NoWarn>$(NoWarn);SYSLIB0051</NoWarn>
<PublishTrimmed Condition="'$(TargetFramework)'=='net7.0' OR '$(TargetFramework)'=='$(NetCoreAppMinimum)'">true</PublishTrimmed>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self: Check if IsAotCompatible or EnableTrimAnalyzer is a better option here

</PropertyGroup>

<PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ public class DiaSession : INavigationSession
/// <param name="binaryPath">
/// The binary path.
/// </param>
#if NET7_0_OR_GREATER
[RequiresUnreferencedCode("Uses CacheSymbols which is not trimmer friendly (specifically, the PortableSymbolReader impl)")]
#endif
public DiaSession(string binaryPath)
: this(binaryPath, null)
{
Expand All @@ -49,11 +52,17 @@ public DiaSession(string binaryPath)
/// <param name="searchPath">
/// search path.
/// </param>
#if NET7_0_OR_GREATER
[RequiresUnreferencedCode("Uses CacheSymbols which is not trimmer friendly (specifically, the PortableSymbolReader impl)")]
#endif
public DiaSession(string binaryPath, string? searchPath)
: this(binaryPath, searchPath, GetSymbolReader(binaryPath))
{
}

#if NET7_0_OR_GREATER
[RequiresUnreferencedCode("Uses CacheSymbols which is not trimmer friendly (specifically, the PortableSymbolReader impl)")]
#endif
internal DiaSession(string binaryPath, string? searchPath, ISymbolReader symbolReader)
{
_symbolReader = symbolReader;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

using System;
using System.Collections.Generic;
#if NET7_0_OR_GREATER
using System.Diagnostics.CodeAnalysis;
#endif
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
Expand Down Expand Up @@ -58,6 +61,11 @@ public void Dispose()
/// <param name="searchPath">
/// search path.
/// </param>
#if NET7_0_OR_GREATER
// This is actually okay and doesn't need the attribute, but it must be added because it's there in the interface.
// And it's in the interface because PortableSymbolReader implementation of CacheSymbols needs it.
[RequiresUnreferencedCode("Uses Assembly.Load which is not trimmer friendly")]
#endif
public void CacheSymbols(string binaryPath, string? searchPath)
{
try
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
#if NET7_0_OR_GREATER
using System.Diagnostics.CodeAnalysis;
#endif

namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.Navigation;

Expand All @@ -19,6 +22,10 @@ internal interface ISymbolReader : IDisposable
/// <param name="searchPath">
/// search path.
/// </param>
#if NET7_0_OR_GREATER
// NOTE: Not all implementations are trimmer unfriendly, but if one implementation is, we are required to add the attribute in the interface.
[RequiresUnreferencedCode("Uses Assembly.Load which is not trimmer friendly")]
#endif
void CacheSymbols(string binaryPath, string? searchPath);

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

using System;
using System.Collections.Generic;
#if NET7_0_OR_GREATER
using System.Diagnostics.CodeAnalysis;
#endif
using System.IO;
using System.Reflection;
using System.Reflection.PortableExecutable;
Expand Down Expand Up @@ -33,6 +36,9 @@ internal class PortableSymbolReader : ISymbolReader
/// <param name="searchPath">
/// The search path.
/// </param>
#if NET7_0_OR_GREATER
[RequiresUnreferencedCode("Uses Assembly.Load which is not trimmer friendly")]
#endif
public void CacheSymbols(string binaryPath, string? searchPath)
{
PopulateCacheForTypeAndMethodSymbols(binaryPath);
Expand Down Expand Up @@ -84,6 +90,9 @@ public void Dispose()
/// <param name="binaryPath">
/// The binary path.
/// </param>
#if NET7_0_OR_GREATER
[RequiresUnreferencedCode("Uses Assembly.Load which is not trimmer friendly")]
#endif
private void PopulateCacheForTypeAndMethodSymbols(string binaryPath)
{
try
Expand Down
200 changes: 200 additions & 0 deletions src/Microsoft.TestPlatform.ObjectModel/TestObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,46 @@ public abstract class TestObject
private static readonly CustomKeyValueConverter KeyValueConverter = new();
private static readonly CustomStringArrayConverter StringArrayConverter = new();

// https://github.com/dotnet/runtime/blob/36bcc2c96045c6793dcfe3151c51a0597537917a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs#L154-L180
// TODO: Validate if some types are not needed and remove them.
private static readonly BooleanConverter BooleanConverter = new();
private static readonly ByteConverter ByteConverter = new();
private static readonly SByteConverter SByteConverter = new();
private static readonly CharConverter CharConverter = new();
private static readonly DoubleConverter DoubleConverter = new();
private static readonly StringConverter StringConverter = new();
private static readonly Int32Converter IntConverter = new();
#if NET7_0_OR_GREATER
private static readonly Int128Converter Int128Converter = new();
#endif
private static readonly Int16Converter Int16Converter = new();
private static readonly Int64Converter Int64Converter = new();
private static readonly SingleConverter SingleConverter = new();
#if NET7_0_OR_GREATER
private static readonly HalfConverter HalfConverter = new();
private static readonly UInt128Converter UInt128Converter = new();
#endif
private static readonly UInt16Converter UInt16Converter = new();
private static readonly UInt32Converter UInt32Converter = new();
private static readonly UInt64Converter UInt64Converter = new();
private static readonly TypeConverter TypeConverter = new();
private static readonly CultureInfoConverter CultureInfoConverter = new();
#if NET7_0_OR_GREATER
private static readonly DateOnlyConverter DateOnlyConverter = new();
#endif
private static readonly DateTimeConverter DateTimeConverter = new();
private static readonly DateTimeOffsetConverter DateTimeOffsetConverter = new();
private static readonly DecimalConverter DecimalConverter = new();
#if NET7_0_OR_GREATER
private static readonly TimeOnlyConverter TimeOnlyConverter = new();
#endif
private static readonly TimeSpanConverter TimeSpanConverter = new();
private static readonly GuidConverter GuidConverter = new();
private static readonly UriTypeConverter UriTypeConverter = new();
#if NET7_0_OR_GREATER
private static readonly VersionConverter VersionConverter = new();
#endif

/// <summary>
/// The store for all the properties registered.
/// </summary>
Expand Down Expand Up @@ -239,6 +279,12 @@ protected virtual void ProtectedSetPropertyValue(TestProperty property, object?
/// Convert passed in value from TestProperty's specified value type.
/// </summary>
/// <returns>Converted object</returns>
#if NET7_0_OR_GREATER
[UnconditionalSuppressMessage(
"Trimming",
"IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code",
Justification = "Awaiting more evidence that we need to take action. If this caused issues, we should still be able to special case some specific types instead of relying on TypeDescriptor")]
#endif
private static object? ConvertPropertyFrom<T>(TestProperty property, CultureInfo culture, object? value)
{
ValidateArg.NotNull(property, nameof(property));
Expand All @@ -265,6 +311,157 @@ protected virtual void ProtectedSetPropertyValue(TestProperty property, object?
return StringArrayConverter.ConvertFrom(null, culture, (string?)value);
}

// These are already handled by TypeDescriptor.GetConverter, but it's not trimmer safe and
// we want to make sure they are guaranteed to work.
if (valueType == typeof(bool))
{
return BooleanConverter.ConvertFrom(null, culture, value ?? string.Empty);
}

if (valueType == typeof(byte))
{
return ByteConverter.ConvertFrom(null, culture, value ?? string.Empty);
}

if (valueType == typeof(sbyte))
{
return SByteConverter.ConvertFrom(null, culture, value ?? string.Empty);
}

if (valueType == typeof(char))
{
return CharConverter.ConvertFrom(null, culture, value ?? string.Empty);
}

if (valueType == typeof(double))
{
return DoubleConverter.ConvertFrom(null, culture, value ?? string.Empty);
}

if (valueType == typeof(string))
{
return StringConverter.ConvertFrom(null, culture, value ?? string.Empty);
}

if (valueType == typeof(int))
{
return IntConverter.ConvertFrom(null, culture, value ?? string.Empty);
}

#if NET7_0_OR_GREATER
if (valueType == typeof(Int128))
{
return Int128Converter.ConvertFrom(null, culture, value ?? string.Empty);
}
#endif

if (valueType == typeof(short))
{
return Int16Converter.ConvertFrom(null, culture, value ?? string.Empty);
}

if (valueType == typeof(long))
{
return Int64Converter.ConvertFrom(null, culture, value ?? string.Empty);
}

if (valueType == typeof(float))
{
return SingleConverter.ConvertFrom(null, culture, value ?? string.Empty);
}

#if NET7_0_OR_GREATER
if (valueType == typeof(Half))
{
return HalfConverter.ConvertFrom(null, culture, value ?? string.Empty);
}

if (valueType == typeof(UInt128))
{
return UInt128Converter.ConvertFrom(null, culture, value ?? string.Empty);
}
#endif

if (valueType == typeof(ushort))
{
return UInt16Converter.ConvertFrom(null, culture, value ?? string.Empty);
}

if (valueType == typeof(uint))
{
return UInt32Converter.ConvertFrom(null, culture, value ?? string.Empty);
}

if (valueType == typeof(ulong))
{
return UInt64Converter.ConvertFrom(null, culture, value ?? string.Empty);
}

if (valueType == typeof(Type))
{
return TypeConverter.ConvertFrom(null, culture, value ?? string.Empty);
}

if (valueType == typeof(CultureInfo))
{
return CultureInfoConverter.ConvertFrom(null, culture, value ?? string.Empty);
}

#if NET7_0_OR_GREATER
if (valueType == typeof(DateOnly))
{
return DateOnlyConverter.ConvertFrom(null, culture, value ?? string.Empty);
}
#endif

if (valueType == typeof(DateTime))
{
return DateTimeConverter.ConvertFrom(null, culture, value ?? string.Empty);
}

if (valueType == typeof(DateTimeOffset))
{
return DateTimeOffsetConverter.ConvertFrom(null, culture, value ?? string.Empty);
}

if (valueType == typeof(decimal))
{
return DecimalConverter.ConvertFrom(null, culture, value ?? string.Empty);
}

#if NET7_0_OR_GREATER
if (valueType == typeof(TimeOnly))
{
return TimeOnlyConverter.ConvertFrom(null, culture, value ?? string.Empty);
}
#endif

if (valueType == typeof(TimeSpan))
{
return TimeSpanConverter.ConvertFrom(null, culture, value ?? string.Empty);
}

if (valueType == typeof(Guid))
{
return GuidConverter.ConvertFrom(null, culture, value ?? string.Empty);
}

if (valueType == typeof(Uri))
{
return UriTypeConverter.ConvertFrom(null, culture, value ?? string.Empty);
}

#if NET7_0_OR_GREATER
if (valueType == typeof(Version))
{
return VersionConverter.ConvertFrom(null, culture, value ?? string.Empty);
}
#endif

// TODO: Consider detecting that we are in source gen mode (or in NativeAOT mode) and avoid calling TypeDescriptor altogether?
// Each of the approaches has pros and cons.
// Ignoring this trimmer unfriendly code when in NativeAOT will help catch issues earlier, and have more deterministic behavior.
// Keeping this trimmer unfriendly code even in NativeAOT will allow us to still have the possibility to work in case we fall in this path.
TPDebug.Assert(valueType is not null, "valueType is null");
TypeConverter converter = TypeDescriptor.GetConverter(valueType);
if (converter == null)
Expand Down Expand Up @@ -292,6 +489,9 @@ protected virtual void ProtectedSetPropertyValue(TestProperty property, object?
/// </summary>
/// <returns>Converted object</returns>
[return: NotNullIfNotNull("value")]
#if NET7_0_OR_GREATER
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
#endif
private static T? ConvertPropertyTo<T>(TestProperty property, CultureInfo culture, object? value)
{
ValidateArg.NotNull(property, nameof(property));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,28 @@
using System.IO;
using System.Linq;
using System.Text;
#if NET7_0_OR_GREATER
using System.Text.Json.Serialization;
#endif
#if !NET7_0_OR_GREATER
using System.Runtime.Serialization.Json;
#endif

namespace Microsoft.VisualStudio.TestPlatform.ObjectModel;

/// <summary>
/// Converts a json representation of <see cref="KeyValuePair{String,String}"/> to an object.
/// </summary>
internal class CustomKeyValueConverter : TypeConverter
internal partial class CustomKeyValueConverter : TypeConverter
{
#if NET7_0_OR_GREATER
[JsonSerializable(typeof(TraitObject[]))]
private partial class TraitObjectSerializerContext : JsonSerializerContext
{
}
#else
private readonly DataContractJsonSerializer _serializer = new(typeof(TraitObject[]));
#endif

/// <inheritdoc/>
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
Expand All @@ -44,7 +56,11 @@ public override bool CanConvertFrom(ITypeDescriptorContext? context, Type source

using var stream = new MemoryStream(Encoding.Unicode.GetBytes(data));
// Converting Json data to array of KeyValuePairs with duplicate keys.
#if NET7_0_OR_GREATER
var listOfTraitObjects = System.Text.Json.JsonSerializer.Deserialize(stream, TraitObjectSerializerContext.Default.TraitObjectArray);
#else
var listOfTraitObjects = _serializer.ReadObject(stream) as TraitObject[];
#endif
return listOfTraitObjects?.Select(trait => new KeyValuePair<string?, string?>(trait.Key, trait.Value)).ToArray() ?? [];
}

Expand Down
Loading