Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
26 changes: 25 additions & 1 deletion src/coreclr/tools/Common/Compiler/Logging/MessageOrigin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Text;

using Internal.IL;
using Internal.TypeSystem;

namespace ILCompiler.Logging
Expand Down Expand Up @@ -35,6 +36,29 @@ public MessageOrigin(TypeSystemEntity memberDefinition, string fileName = null,
SourceColumn = sourceColumn;
}

public MessageOrigin(MethodIL origin, int ilOffset)
{
string document = null;
int? lineNumber = null;

IEnumerable<ILSequencePoint> sequencePoints = origin.GetDebugInfo()?.GetSequencePoints();
if (sequencePoints != null)
{
foreach (var sequencePoint in sequencePoints)
{
if (sequencePoint.Offset <= ilOffset)
{
document = sequencePoint.Document;
lineNumber = sequencePoint.LineNumber;
}
}
}
FileName = document;
MemberDefinition = origin.OwningMethod;
SourceLine = lineNumber;
SourceColumn = null;
}

public override string ToString()
{
if (FileName == null)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection.Metadata;
using ILCompiler.Logging;
using ILLink.Shared;
using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;

Expand Down Expand Up @@ -44,40 +50,147 @@ internal static string GetGenericParameterDeclaringMemberDisplayName(GenericPara
return ((TypeDesc)parent).GetDisplayName();
}

internal static string GetRequiresAttributeMessage(MethodDesc method, string requiresAttributeName)
internal static bool TryGetRequiresAttribute(TypeSystemEntity member, string requiresAttributeName, [NotNullWhen(returnValue: true)] out CustomAttributeValue<TypeDesc>? attribute)
{
var ecmaMethod = method.GetTypicalMethodDefinition() as EcmaMethod;
if (ecmaMethod == null)
return null;
attribute = default;
CustomAttributeValue<TypeDesc>? decoded = default;
switch (member)
{
case MethodDesc method:
var ecmaMethod = method.GetTypicalMethodDefinition() as EcmaMethod;
if (ecmaMethod == null)
return false;
decoded = ecmaMethod.GetDecodedCustomAttribute("System.Diagnostics.CodeAnalysis", requiresAttributeName);
break;
case MetadataType type:
var ecmaType = type as EcmaType;
if (ecmaType == null)
return false;
decoded = ecmaType.GetDecodedCustomAttribute("System.Diagnostics.CodeAnalysis", requiresAttributeName);
break;
case PropertyPseudoDesc property:
decoded = property.GetDecodedCustomAttribute("System.Diagnostics.CodeAnalysis", requiresAttributeName);
break;
default:
Debug.Fail("Trying to operate with unsupported TypeSystemEntity " + member.GetType().ToString());
break;
}
if (!decoded.HasValue)
return false;

attribute = decoded.Value;
return true;
}

var decoded = ecmaMethod.GetDecodedCustomAttribute("System.Diagnostics.CodeAnalysis", requiresAttributeName);
if (decoded == null)
public static CustomAttributeValue<TypeDesc>? GetDecodedCustomAttribute(this PropertyPseudoDesc prop, string attributeNamespace, string attributeName)
{
var ecmaType = prop.OwningType as EcmaType;
var metadataReader = ecmaType.MetadataReader;

var attributeHandle = metadataReader.GetCustomAttributeHandle(prop.GetCustomAttributes,
attributeNamespace, attributeName);

if (attributeHandle.IsNil)
return null;

var decodedValue = decoded.Value;
return metadataReader.GetCustomAttribute(attributeHandle).DecodeValue(new CustomAttributeTypeProvider(ecmaType.EcmaModule));
}

if (decodedValue.FixedArguments.Length != 0)
return (string)decodedValue.FixedArguments[0].Value;
internal static string GetRequiresAttributeMessage(CustomAttributeValue<TypeDesc> attribute)
{
if (attribute.FixedArguments.Length != 0)
return (string)attribute.FixedArguments[0].Value;

return null;
}

internal static string GetRequiresAttributeUrl(MethodDesc method, string requiresAttributeName)
internal static string GetRequiresAttributeUrl(CustomAttributeValue<TypeDesc> attribute)
{
var ecmaMethod = method.GetTypicalMethodDefinition() as EcmaMethod;
if (ecmaMethod == null)
return null;
if (attribute.NamedArguments.Length != 0 && attribute.NamedArguments[0].Name == "Url")
return (string)attribute.NamedArguments[0].Value;

var decoded = ecmaMethod.GetDecodedCustomAttribute("System.Diagnostics.CodeAnalysis", requiresAttributeName);
if (decoded == null)
return null;
return null;
}

/// <summary>
/// Determines if method is within a declared Requires scope - this typically means that trim analysis
/// warnings should be suppressed in such a method.
/// </summary>
/// <remarks>Unlike <see cref="DoesMemberRequire(TypeSystemEntity, string, out CustomAttributeValue{TypeDesc}?)"/>
/// if a declaring type has Requires, all methods in that type are considered "in scope" of that Requires. So this includes also
/// instance methods (not just statics and .ctors).</remarks>
internal static bool IsInRequiresScope(this MethodDesc method, string requiresAttribute) =>
method.IsInRequiresScope(requiresAttribute, true);

/// <summary>
/// True if member of a call is considered to be annotated with the Requires... attribute.
/// Doesn't check the associated symbol for overrides and virtual methods because we should warn on mismatched between the property AND the accessors
/// </summary>
/// <param name="method">
/// MethodDesc that is either an overriding member or an overriden/virtual member
/// </param>
internal static bool IsOverrideInRequiresScope(this MethodDesc method, string requiresAttribute) =>
method.IsInRequiresScope(requiresAttribute, false);

private static bool IsInRequiresScope(this MethodDesc method, string requiresAttribute, bool checkAssociatedSymbol)
{
if (method.HasCustomAttribute("System.Diagnostics.CodeAnalysis", requiresAttribute) && !method.IsStaticConstructor)
return true;

var decodedValue = decoded.Value;
if (method.OwningType is TypeDesc type && TryGetRequiresAttribute(type, requiresAttribute, out _))
return true;

if (decodedValue.NamedArguments.Length != 0 && decodedValue.NamedArguments[0].Name == "Url")
return (string)decodedValue.NamedArguments[0].Value;
if (checkAssociatedSymbol && method.GetPropertyForAccessor() is PropertyPseudoDesc property && TryGetRequiresAttribute(property, requiresAttribute, out _))
return true;

return null;
return false;
}

internal static bool DoesMethodRequire(this MethodDesc method, string requiresAttribute, [NotNullWhen(returnValue: true)] out CustomAttributeValue<TypeDesc>? attribute)
{
attribute = null;
if (method.IsStaticConstructor)
return false;

if (TryGetRequiresAttribute(method, requiresAttribute, out attribute))
return true;

if ((method.Signature.IsStatic || method.IsConstructor) && method.OwningType is TypeDesc owningType &&
!owningType.IsArray && TryGetRequiresAttribute(owningType, requiresAttribute, out attribute))
return true;

return false;
}

internal static bool DoesFieldRequire(this FieldDesc field, string requiresAttribute, [NotNullWhen(returnValue: true)] out CustomAttributeValue<TypeDesc>? attribute)
{
if (!field.IsStatic || field.OwningType is not TypeDesc owningType || owningType.IsArray)
{
attribute = null;
return false;
}

return TryGetRequiresAttribute(field.OwningType, requiresAttribute, out attribute);
}

internal static bool DoesPropertyRequire(this PropertyPseudoDesc property, string requiresAttribute, [NotNullWhen(returnValue: true)] out CustomAttributeValue<TypeDesc>? attribute) =>
TryGetRequiresAttribute(property, requiresAttribute, out attribute);

/// <summary>
/// Determines if member requires (and thus any usage of such method should be warned about).
/// </summary>
/// <remarks>Unlike <see cref="IsInRequiresScope(MethodDesc, string)"/> only static methods
/// and .ctors are reported as requires when the declaring type has Requires on it.</remarks>
internal static bool DoesMemberRequire(this TypeSystemEntity member, string requiresAttribute, [NotNullWhen(returnValue: true)] out CustomAttributeValue<TypeDesc>? attribute)
{
attribute = null;
return member switch
{
MethodDesc method => DoesMethodRequire(method, requiresAttribute, out attribute),
FieldDesc field => DoesFieldRequire(field, requiresAttribute, out attribute),
PropertyPseudoDesc property => DoesPropertyRequire(property, requiresAttribute, out attribute),
_ => false
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ public static PropertyPseudoDesc GetProperty(this MetadataType mdType, string na

public static PropertyPseudoDesc GetPropertyForAccessor(this MethodDesc accessor)
{
var ecmaAccessor = (EcmaMethod)accessor.GetTypicalMethodDefinition();
if (accessor.GetTypicalMethodDefinition() is not EcmaMethod ecmaAccessor)
return null;
var type = (EcmaType)ecmaAccessor.OwningType;
var reader = type.MetadataReader;
var module = type.EcmaModule;
Expand Down
Loading