|
// 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.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Internal.IL;
using Internal.TypeSystem;
#nullable enable
namespace ILCompiler.Logging
{
public struct MessageOrigin :
#if false
IComparable<MessageOrigin>,
#endif
IEquatable<MessageOrigin>
{
public string? FileName { get; }
public TypeSystemEntity? MemberDefinition { get; }
public int? SourceLine { get; }
public int? SourceColumn { get; }
public int? ILOffset { get; }
private const int HiddenLineNumber = 0xfeefee;
public MessageOrigin(string fileName, int? sourceLine = null, int? sourceColumn = null)
{
FileName = fileName;
SourceLine = sourceLine;
SourceColumn = sourceColumn;
MemberDefinition = null;
ILOffset = null;
}
public MessageOrigin(TypeSystemEntity memberDefinition, string? fileName = null, int? sourceLine = 0, int? sourceColumn = 0)
{
FileName = fileName;
MemberDefinition = memberDefinition;
SourceLine = sourceLine;
SourceColumn = sourceColumn;
ILOffset = null;
}
// The assembly parameter should be specified if available as it allows assigning the diagnostic
// to a an assembly (we group based on assembly).
public MessageOrigin(string fileName, int sourceLine, int sourceColumn, ModuleDesc? assembly)
{
FileName = fileName;
SourceLine = sourceLine;
SourceColumn = sourceColumn;
MemberDefinition = assembly;
ILOffset = null;
}
public MessageOrigin(MethodIL methodBody, int ilOffset)
{
ILSequencePoint? correspondingSequencePoint = null;
IEnumerable<ILSequencePoint>? sequencePoints = methodBody.GetDebugInfo()?.GetSequencePoints();
if (sequencePoints != null)
{
foreach (var sequencePoint in sequencePoints)
{
if (sequencePoint.Offset <= ilOffset)
{
correspondingSequencePoint = sequencePoint;
}
}
// If the warning comes from hidden line (compiler generated code typically)
// search for any sequence point with non-hidden line number and report that as a best effort.
if (correspondingSequencePoint?.LineNumber == HiddenLineNumber)
{
// Reset the information as we don't want to use any of the info in the hidden sequence point
correspondingSequencePoint = null;
foreach (var sequencePoint in sequencePoints)
{
if (sequencePoint.LineNumber != HiddenLineNumber)
{
correspondingSequencePoint = sequencePoint;
break;
}
}
}
}
FileName = correspondingSequencePoint?.Document;
MemberDefinition = methodBody.OwningMethod;
SourceLine = correspondingSequencePoint?.LineNumber;
SourceColumn = null;
ILOffset = ilOffset;
}
public MessageOrigin WithInstructionOffset(MethodIL methodBody, int ilOffset)
{
Debug.Assert(methodBody.OwningMethod == MemberDefinition);
return new MessageOrigin(methodBody, ilOffset);
}
public override string? ToString()
{
if (FileName == null)
return null;
StringBuilder sb = new StringBuilder(FileName);
if (SourceLine.HasValue)
{
sb.Append('(').Append(SourceLine);
if (SourceColumn.HasValue)
sb.Append(',').Append(SourceColumn);
sb.Append(')');
}
return sb.ToString();
}
public bool Equals(MessageOrigin other) =>
(FileName, MemberDefinition, SourceLine, SourceColumn, ILOffset) == (other.FileName, other.MemberDefinition, other.SourceLine, other.SourceColumn, other.ILOffset);
public override bool Equals(object? obj) => obj is MessageOrigin messageOrigin && Equals(messageOrigin);
public override int GetHashCode() => (FileName, MemberDefinition, SourceLine, SourceColumn, ILOffset).GetHashCode();
public static bool operator ==(MessageOrigin lhs, MessageOrigin rhs) => lhs.Equals(rhs);
public static bool operator !=(MessageOrigin lhs, MessageOrigin rhs) => !lhs.Equals(rhs);
#if false
public int CompareTo(MessageOrigin other)
{
if (MemberDefinition != null && other.MemberDefinition != null)
{
var thisMember = Provider as IMemberDefinition;
var otherMember = other.Provider as IMemberDefinition;
TypeDefinition? thisTypeDef = (Provider as TypeDefinition) ?? (Provider as IMemberDefinition)?.DeclaringType;
TypeDefinition? otherTypeDef = (other.Provider as TypeDefinition) ?? (other.Provider as IMemberDefinition)?.DeclaringType;
var thisAssembly = thisTypeDef?.Module.Assembly ?? Provider as AssemblyDefinition;
var otherAssembly = otherTypeDef?.Module.Assembly ?? other.Provider as AssemblyDefinition;
int result = (thisAssembly?.Name.Name, thisTypeDef?.Name, thisMember?.Name).CompareTo
((otherAssembly?.Name.Name, otherTypeDef?.Name, otherMember?.Name));
if (result != 0)
return result;
if (ILOffset != null && other.ILOffset != null)
return ILOffset.Value.CompareTo(other.ILOffset);
return ILOffset == null ? (other.ILOffset == null ? 0 : 1) : -1;
}
else if (Provider == null && other.Provider == null)
{
if (FileName != null && other.FileName != null)
{
return string.Compare(FileName, other.FileName);
}
else if (FileName == null && other.FileName == null)
{
return (SourceLine, SourceColumn).CompareTo((other.SourceLine, other.SourceColumn));
}
return (FileName == null) ? 1 : -1;
}
return (Provider == null) ? 1 : -1;
}
#endif
}
}
|