diff --git a/Directory.Build.props b/Directory.Build.props index 3e7fe9f..6c371ea 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,10 +1,14 @@  latest + true $(DefineConstants);HAS_ASYNC_ENUMERATOR + + + true diff --git a/src/Ben.Demystifier/Ben.Demystifier.csproj b/src/Ben.Demystifier/Ben.Demystifier.csproj index 71db297..46781c5 100644 --- a/src/Ben.Demystifier/Ben.Demystifier.csproj +++ b/src/Ben.Demystifier/Ben.Demystifier.csproj @@ -12,6 +12,7 @@ true embedded true + enable diff --git a/src/Ben.Demystifier/EnhancedStackFrame.cs b/src/Ben.Demystifier/EnhancedStackFrame.cs index bb48548..96661af 100644 --- a/src/Ben.Demystifier/EnhancedStackFrame.cs +++ b/src/Ben.Demystifier/EnhancedStackFrame.cs @@ -7,9 +7,9 @@ namespace System.Diagnostics { public class EnhancedStackFrame : StackFrame { - private string _fileName; - private int _lineNumber; - private int _colNumber; + private readonly string? _fileName; + private readonly int _lineNumber; + private readonly int _colNumber; public StackFrame StackFrame { get; } @@ -21,7 +21,7 @@ namespace System.Diagnostics public ResolvedMethod MethodInfo { get; } - internal EnhancedStackFrame(StackFrame stackFrame, ResolvedMethod methodInfo, string fileName, int lineNumber, int colNumber) + internal EnhancedStackFrame(StackFrame stackFrame, ResolvedMethod methodInfo, string? fileName, int lineNumber, int colNumber) : base(fileName, lineNumber, colNumber) { StackFrame = stackFrame; @@ -32,7 +32,7 @@ namespace System.Diagnostics _colNumber = colNumber; } - internal bool IsEquivalent(ResolvedMethod methodInfo, string fileName, int lineNumber, int colNumber) + internal bool IsEquivalent(ResolvedMethod methodInfo, string? fileName, int lineNumber, int colNumber) { return _lineNumber == lineNumber && _colNumber == colNumber && @@ -59,7 +59,7 @@ namespace System.Diagnostics /// This information is typically extracted from the debugging symbols for the executable. /// /// The file name, or null if the file name cannot be determined. - public override string GetFileName() => _fileName; + public override string? GetFileName() => _fileName; /// /// Gets the offset from the start of the Microsoft intermediate language (MSIL) diff --git a/src/Ben.Demystifier/EnhancedStackTrace.Frames.cs b/src/Ben.Demystifier/EnhancedStackTrace.Frames.cs index 8e6c6a7..30ed8b2 100644 --- a/src/Ben.Demystifier/EnhancedStackTrace.Frames.cs +++ b/src/Ben.Demystifier/EnhancedStackTrace.Frames.cs @@ -44,8 +44,8 @@ namespace System.Diagnostics return frames; } - EnhancedStackFrame lastFrame = null; - PortablePdbReader portablePdbReader = null; + EnhancedStackFrame? lastFrame = null; + PortablePdbReader? portablePdbReader = null; try { for (var i = 0; i < stackFrames.Length; i++) @@ -70,6 +70,12 @@ namespace System.Diagnostics (portablePdbReader ??= new PortablePdbReader()).PopulateStackFrame(frame, method, frame.GetILOffset(), out fileName, out row, out column); } + if (method is null) + { + // Method can't be null + continue; + } + var resolvedMethod = GetMethodDisplayString(method); if (lastFrame?.IsEquivalent(resolvedMethod, fileName, row, column) ?? false) { @@ -85,11 +91,7 @@ namespace System.Diagnostics } finally { - if (portablePdbReader is not null) - { - portablePdbReader.Dispose(); - } - + portablePdbReader?.Dispose(); } return frames; @@ -97,12 +99,6 @@ namespace System.Diagnostics public static ResolvedMethod GetMethodDisplayString(MethodBase originMethod) { - // Special case: no method available - if (originMethod == null) - { - return null; - } - var method = originMethod; var methodDisplayInfo = new ResolvedMethod @@ -174,7 +170,7 @@ namespace System.Diagnostics if (value is Delegate d) { if (ReferenceEquals(d.Method, originMethod) && - d.Target.ToString() == originMethod.DeclaringType.ToString()) + d.Target.ToString() == originMethod.DeclaringType?.ToString()) { methodDisplayInfo.Name = field.Name; methodDisplayInfo.IsLambda = false; @@ -208,11 +204,10 @@ namespace System.Diagnostics } else if (mi.ReturnType != null) { - methodDisplayInfo.ReturnParameter = new ResolvedParameter + methodDisplayInfo.ReturnParameter = new ResolvedParameter(mi.ReturnType) { Prefix = "", Name = "", - ResolvedType = mi.ReturnType, }; } } @@ -278,7 +273,7 @@ namespace System.Diagnostics return false; } - private static bool TryResolveGeneratedName(ref MethodBase method, out Type type, out string methodName, out string subMethodName, out GeneratedNameKind kind, out int? ordinal) + private static bool TryResolveGeneratedName(ref MethodBase method, out Type? type, out string methodName, out string? subMethodName, out GeneratedNameKind kind, out int? ordinal) { kind = GeneratedNameKind.None; type = method.DeclaringType; @@ -364,15 +359,18 @@ namespace System.Diagnostics return false; } - private static bool TryResolveSourceMethod(IEnumerable candidateMethods, GeneratedNameKind kind, string matchHint, ref MethodBase method, ref Type type, out int? ordinal) + private static bool TryResolveSourceMethod(IEnumerable candidateMethods, GeneratedNameKind kind, string? matchHint, ref MethodBase method, ref Type type, out int? ordinal) { ordinal = null; foreach (var candidateMethod in candidateMethods) { - var methodBody = candidateMethod.GetMethodBody(); + if (candidateMethod.GetMethodBody() is not { } methodBody) + { + continue; + } if (kind == GeneratedNameKind.LambdaMethod) { - foreach (var v in EnumerableIList.Create(methodBody?.LocalVariables)) + foreach (var v in EnumerableIList.Create(methodBody.LocalVariables)) { if (v.LocalType == type) { @@ -387,14 +385,13 @@ namespace System.Diagnostics try { - var rawIL = methodBody?.GetILAsByteArray(); - if (rawIL == null) continue; - var reader = new ILReader(rawIL); + var rawIl = methodBody.GetILAsByteArray(); + var reader = new ILReader(rawIl); while (reader.Read(candidateMethod)) { if (reader.Operand is MethodBase mb) { - if (method == mb || (matchHint != null && method.Name.Contains(matchHint))) + if (method == mb || matchHint != null && method.Name.Contains(matchHint)) { if (kind == GeneratedNameKind.LambdaMethod) { @@ -462,7 +459,7 @@ namespace System.Diagnostics } } - static string GetMatchHint(GeneratedNameKind kind, MethodBase method) + static string? GetMatchHint(GeneratedNameKind kind, MethodBase method) { var methodName = method.Name; @@ -590,28 +587,25 @@ namespace System.Diagnostics } } - if (parameterType.IsByRef) + if (parameterType.IsByRef && parameterType.GetElementType() is {} elementType) { - parameterType = parameterType.GetElementType(); + parameterType = elementType; } - return new ResolvedParameter + return new ResolvedParameter(parameterType) { Prefix = prefix, Name = parameter.Name, - ResolvedType = parameterType, IsDynamicType = parameter.IsDefined(typeof(DynamicAttribute), false) }; } private static ResolvedParameter GetValueTupleParameter(IList tupleNames, string prefix, string name, Type parameterType) { - return new ValueTupleResolvedParameter + return new ValueTupleResolvedParameter(parameterType, tupleNames) { - TupleNames = tupleNames, Prefix = prefix, Name = name, - ResolvedType = parameterType }; } @@ -650,8 +644,6 @@ namespace System.Diagnostics private static bool ShowInStackTrace(MethodBase method) { - Debug.Assert(method != null); - // Since .NET 5: // https://github.com/dotnet/runtime/blob/7c18d4d6488dab82124d475d1199def01d1d252c/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs#L348-L361 if ((method.MethodImplementationFlags & MethodImplAttributes.AggressiveInlining) != 0) diff --git a/src/Ben.Demystifier/EnhancedStackTrace.cs b/src/Ben.Demystifier/EnhancedStackTrace.cs index 02c063a..d81a9c0 100644 --- a/src/Ben.Demystifier/EnhancedStackTrace.cs +++ b/src/Ben.Demystifier/EnhancedStackTrace.cs @@ -101,12 +101,13 @@ namespace System.Diagnostics sb.Append(" at "); frame.MethodInfo.Append(sb); - - var filePath = frame.GetFileName(); - if (!string.IsNullOrEmpty(filePath)) + + if (frame.GetFileName() is {} fileName + // IsNullOrEmpty alone wasn't enough to disable the null warning + && !string.IsNullOrEmpty(fileName)) { sb.Append(" in "); - sb.Append(TryGetFullPath(filePath)); + sb.Append(TryGetFullPath(fileName)); } diff --git a/src/Ben.Demystifier/Enumerable/EnumeratorIList.cs b/src/Ben.Demystifier/Enumerable/EnumeratorIList.cs index c7bb704..a80ff8b 100644 --- a/src/Ben.Demystifier/Enumerable/EnumeratorIList.cs +++ b/src/Ben.Demystifier/Enumerable/EnumeratorIList.cs @@ -24,7 +24,7 @@ namespace System.Collections.Generic.Enumerable } public void Dispose() { } - object IEnumerator.Current => Current; + object? IEnumerator.Current => Current; public void Reset() => _index = -1; } } diff --git a/src/Ben.Demystifier/Internal/ILReader.cs b/src/Ben.Demystifier/Internal/ILReader.cs index af442a4..5222e97 100644 --- a/src/Ben.Demystifier/Internal/ILReader.cs +++ b/src/Ben.Demystifier/Internal/ILReader.cs @@ -16,7 +16,7 @@ namespace System.Diagnostics.Internal public OpCode OpCode { get; private set; } public int MetadataToken { get; private set; } - public MemberInfo Operand { get; private set; } + public MemberInfo? Operand { get; private set; } public bool Read(MethodBase methodInfo) { @@ -38,7 +38,7 @@ namespace System.Diagnostics.Internal return doubleByteOpCode[ReadByte()]; } - MemberInfo ReadOperand(OpCode code, MethodBase methodInfo) + MemberInfo? ReadOperand(OpCode code, MethodBase methodInfo) { MetadataToken = 0; int inlineLength; @@ -46,12 +46,12 @@ namespace System.Diagnostics.Internal { case OperandType.InlineMethod: MetadataToken = ReadInt(); - Type[] methodArgs = null; + Type[]? methodArgs = null; if (methodInfo.GetType() != typeof(ConstructorInfo) && !methodInfo.GetType().IsSubclassOf(typeof(ConstructorInfo))) { methodArgs = methodInfo.GetGenericArguments(); } - Type[] typeArgs = null; + Type[]? typeArgs = null; if (methodInfo.DeclaringType != null) { typeArgs = methodInfo.DeclaringType.GetGenericArguments(); diff --git a/src/Ben.Demystifier/Internal/PortablePdbReader.cs b/src/Ben.Demystifier/Internal/PortablePdbReader.cs index d92d394..534e4d1 100644 --- a/src/Ben.Demystifier/Internal/PortablePdbReader.cs +++ b/src/Ben.Demystifier/Internal/PortablePdbReader.cs @@ -68,13 +68,13 @@ namespace System.Diagnostics.Internal } } - private MetadataReader GetMetadataReader(string assemblyPath) + private MetadataReader? GetMetadataReader(string assemblyPath) { if (!_cache.TryGetValue(assemblyPath, out var provider)) { var pdbPath = GetPdbPath(assemblyPath); - if (!string.IsNullOrEmpty(pdbPath) && File.Exists(pdbPath) && IsPortable(pdbPath)) + if (!string.IsNullOrEmpty(pdbPath) && File.Exists(pdbPath) && IsPortable(pdbPath!)) { var pdbStream = File.OpenRead(pdbPath); provider = MetadataReaderProvider.FromPortablePdbStream(pdbStream); @@ -86,7 +86,7 @@ namespace System.Diagnostics.Internal return provider?.GetMetadataReader(); } - private static string GetPdbPath(string assemblyPath) + private static string? GetPdbPath(string assemblyPath) { if (string.IsNullOrEmpty(assemblyPath)) { @@ -97,16 +97,16 @@ namespace System.Diagnostics.Internal { var peStream = File.OpenRead(assemblyPath); - using (var peReader = new PEReader(peStream)) + using var peReader = new PEReader(peStream); + foreach (var entry in peReader.ReadDebugDirectory()) { - foreach (var entry in peReader.ReadDebugDirectory()) + if (entry.Type == DebugDirectoryEntryType.CodeView) { - if (entry.Type == DebugDirectoryEntryType.CodeView) - { - var codeViewData = peReader.ReadCodeViewDebugDirectoryData(entry); - var peDirectory = Path.GetDirectoryName(assemblyPath); - return Path.Combine(peDirectory, Path.GetFileName(codeViewData.Path)); - } + var codeViewData = peReader.ReadCodeViewDebugDirectoryData(entry); + var peDirectory = Path.GetDirectoryName(assemblyPath); + return peDirectory is null + ? null + : Path.Combine(peDirectory, Path.GetFileName(codeViewData.Path)); } } } @@ -116,13 +116,11 @@ namespace System.Diagnostics.Internal private static bool IsPortable(string pdbPath) { - using (var pdbStream = File.OpenRead(pdbPath)) - { - return pdbStream.ReadByte() == 'B' && - pdbStream.ReadByte() == 'S' && - pdbStream.ReadByte() == 'J' && - pdbStream.ReadByte() == 'B'; - } + using var pdbStream = File.OpenRead(pdbPath); + return pdbStream.ReadByte() == 'B' && + pdbStream.ReadByte() == 'S' && + pdbStream.ReadByte() == 'J' && + pdbStream.ReadByte() == 'B'; } public void Dispose() diff --git a/src/Ben.Demystifier/Internal/ReflectionHelper.cs b/src/Ben.Demystifier/Internal/ReflectionHelper.cs index c8467fd..354a84f 100644 --- a/src/Ben.Demystifier/Internal/ReflectionHelper.cs +++ b/src/Ben.Demystifier/Internal/ReflectionHelper.cs @@ -12,7 +12,7 @@ namespace System.Diagnostics.Internal /// public static class ReflectionHelper { - private static PropertyInfo tranformerNamesLazyPropertyInfo; + private static PropertyInfo? tranformerNamesLazyPropertyInfo; /// /// Returns true if the is a value tuple type. @@ -43,15 +43,15 @@ namespace System.Diagnostics.Internal /// To avoid compile-time depencency hell with System.ValueTuple, this method uses reflection /// instead of casting the attribute to a specific type. /// - public static IList GetTransformerNames(this Attribute attribute) + public static IList? GetTransformerNames(this Attribute attribute) { Debug.Assert(attribute.IsTupleElementNameAttribue()); var propertyInfo = GetTransformNamesPropertyInfo(attribute.GetType()); - return (IList)propertyInfo.GetValue(attribute); + return propertyInfo?.GetValue(attribute) as IList; } - private static PropertyInfo GetTransformNamesPropertyInfo(Type attributeType) + private static PropertyInfo? GetTransformNamesPropertyInfo(Type attributeType) { return LazyInitializer.EnsureInitialized(ref tranformerNamesLazyPropertyInfo, () => attributeType.GetProperty("TransformNames", BindingFlags.Instance | BindingFlags.Public)); diff --git a/src/Ben.Demystifier/ResolvedMethod.cs b/src/Ben.Demystifier/ResolvedMethod.cs index a2d31ec..a674cb5 100644 --- a/src/Ben.Demystifier/ResolvedMethod.cs +++ b/src/Ben.Demystifier/ResolvedMethod.cs @@ -9,27 +9,27 @@ namespace System.Diagnostics { public class ResolvedMethod { - public MethodBase MethodBase { get; set; } + public MethodBase? MethodBase { get; set; } - public Type DeclaringType { get; set; } + public Type? DeclaringType { get; set; } public bool IsAsync { get; set; } public bool IsLambda { get; set; } - public ResolvedParameter ReturnParameter { get; set; } + public ResolvedParameter? ReturnParameter { get; set; } - public string Name { get; set; } + public string? Name { get; set; } public int? Ordinal { get; set; } - public string GenericArguments { get; set; } + public string? GenericArguments { get; set; } - public Type[] ResolvedGenericArguments { get; set; } + public Type[]? ResolvedGenericArguments { get; set; } - public MethodBase SubMethodBase { get; set; } + public MethodBase? SubMethodBase { get; set; } - public string SubMethod { get; set; } + public string? SubMethod { get; set; } public EnumerableIList Parameters { get; set; } diff --git a/src/Ben.Demystifier/ResolvedParameter.cs b/src/Ben.Demystifier/ResolvedParameter.cs index 47c8b48..1457d92 100644 --- a/src/Ben.Demystifier/ResolvedParameter.cs +++ b/src/Ben.Demystifier/ResolvedParameter.cs @@ -7,13 +7,15 @@ namespace System.Diagnostics { public class ResolvedParameter { - public string Name { get; set; } + public string? Name { get; set; } public Type ResolvedType { get; set; } - public string Prefix { get; set; } + public string? Prefix { get; set; } public bool IsDynamicType { get; set; } + public ResolvedParameter(Type resolvedType) => ResolvedType = resolvedType; + public override string ToString() => Append(new StringBuilder()).ToString(); public StringBuilder Append(StringBuilder sb) diff --git a/src/Ben.Demystifier/ValueTupleResolvedParameter.cs b/src/Ben.Demystifier/ValueTupleResolvedParameter.cs index 6750ac9..7dacf13 100644 --- a/src/Ben.Demystifier/ValueTupleResolvedParameter.cs +++ b/src/Ben.Demystifier/ValueTupleResolvedParameter.cs @@ -9,25 +9,31 @@ namespace System.Diagnostics { public class ValueTupleResolvedParameter : ResolvedParameter { - public IList TupleNames { get; set; } + public IList TupleNames { get; } + + public ValueTupleResolvedParameter(Type resolvedType, IList tupleNames) + : base(resolvedType) + => TupleNames = tupleNames; protected override void AppendTypeName(StringBuilder sb) { - if (ResolvedType.IsValueTuple()) + if (ResolvedType is not null) { - AppendValueTupleParameterName(sb, ResolvedType); - } - else - { - // Need to unwrap the first generic argument first. - sb.Append(TypeNameHelper.GetTypeNameForGenericType(ResolvedType)); - sb.Append("<"); - AppendValueTupleParameterName(sb, ResolvedType.GetGenericArguments()[0]); - sb.Append(">"); + if (ResolvedType.IsValueTuple()) + { + AppendValueTupleParameterName(sb, ResolvedType); + } + else + { + // Need to unwrap the first generic argument first. + sb.Append(TypeNameHelper.GetTypeNameForGenericType(ResolvedType)); + sb.Append("<"); + AppendValueTupleParameterName(sb, ResolvedType.GetGenericArguments()[0]); + sb.Append(">"); + } } } - private void AppendValueTupleParameterName(StringBuilder sb, Type parameterType) { sb.Append("(");