Skip to content

Commit

Permalink
Migrate to nullable ref types
Browse files Browse the repository at this point in the history
  • Loading branch information
tom-englert committed Apr 14, 2020
1 parent 9e240e0 commit f3444e8
Show file tree
Hide file tree
Showing 39 changed files with 581 additions and 726 deletions.
7 changes: 3 additions & 4 deletions AssemblyToProcess/AssemblyToProcess.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="Fody" Version="6.0.0" PrivateAssets="All" />
<PackageReference Include="PropertyChanged.Fody" Version="3.1.3" PrivateAssets="All" />
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-19367-01" PrivateAssets="All" />
<PackageReference Include="Fody" Version="6.1.1" PrivateAssets="All" />
<PackageReference Include="PropertyChanged.Fody" Version="3.2.8" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.0-beta-20204-02" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.ComponentModel.Composition" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using AutoProperties;
// ReSharper disable CheckNamespace
#pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.

public class ClassWithAutoPropertiesInitializedInSeparateMethod : ObservableObject
{
Expand Down
8 changes: 6 additions & 2 deletions AssemblyToProcess/ClassWithAutoPropertyInitAndGenericBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
using System.Reflection;

using AutoProperties;
// ReSharper disable CheckNamespace
// ReSharper disable UnusedMember.Global
#pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.

public class GenericBaseClass<T>
{
Expand All @@ -15,13 +18,14 @@ public void Temp(T x)
}

[SetInterceptor]
protected void SetValue<T>(string name, Type propertyType, PropertyInfo propertyInfo, object newValue, T genericNewValue, ref T refToBackingField)
// ReSharper disable once RedundantAssignment
protected void SetValue<T1>(string name, Type propertyType, PropertyInfo propertyInfo, object newValue, T1 genericNewValue, ref T1 refToBackingField)
{
refToBackingField = genericNewValue;
}

[GetInterceptor]
protected T GetValue<T>(string name, Type propertyType, PropertyInfo propertyInfo, object fieldValue, T genericFieldValue, ref T refToBackingField)
protected T1 GetValue<T1>(string name, Type propertyType, PropertyInfo propertyInfo, object fieldValue, T1 genericFieldValue, ref T1 refToBackingField)
{
return genericFieldValue;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using AutoProperties;
// ReSharper disable CheckNamespace

public class ClassWithExplicitInitializedAutoPropertiesDerivedProperDesign : ClassWithExplicitInitializedAutoProperties
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using AutoProperties;
// ReSharper disable CheckNamespace

public class ClassWithExplicitInitializedAutoPropertiesDerivedWeakDesign : ClassWithExplicitInitializedAutoProperties
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using AutoProperties;
// ReSharper disable CheckNamespace

public class ClassWithExplicitInitializedBackingFieldProperties : ObservableObject
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using AutoProperties;
// ReSharper disable CheckNamespace

public class ClassWithInlineInitializedAutoProperties : ObservableObject
{
Expand Down
10 changes: 10 additions & 0 deletions AssemblyToProcess/FodyWeavers.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@
<xs:documentation>Used to control if equality checks should use the static Equals method resolved from the base class.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="SuppressWarnings" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to turn off build warnings from this weaver.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="SuppressOnPropertyNameChangedWarning" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to turn off build warnings about mismatched On_PropertyName_Changed methods.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:all>
Expand Down
3 changes: 2 additions & 1 deletion AssemblyToProcess/InterceptorSample.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
// ReSharper disable all
using System;
using System.Configuration;
using System.Reflection;

Expand Down
8 changes: 2 additions & 6 deletions AssemblyToProcess/Interceptors.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
using System;
// ReSharper disable all
using System;
using System.Reflection;

using AutoProperties;

using TestLibrary;

// ReSharper disable UnusedMember.Local
// ReSharper disable UnusedParameter.Local
// ReSharper disable PossibleNullReferenceException
// ReSharper disable AssignNullToNotNullAttribute

public class ClassWithSimpleInterceptors
{
private int _field = 42;
Expand Down
3 changes: 2 additions & 1 deletion AssemblyToProcess/InterceptorsSpecialCases.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
// ReSharper disable all
using System;
using System.ComponentModel.Composition;
using System.Reflection;

Expand Down
1 change: 1 addition & 0 deletions AssemblyToProcess/ParentWithGenericBaseOfInt.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// ReSharper disable all
using System;
using System.Collections.Generic;
using System.Reflection;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Fody" Version="6.0.0" PrivateAssets="All" />
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-19367-01" PrivateAssets="All" />
<PackageReference Include="Fody" Version="6.1.1" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.0-beta-20204-02" PrivateAssets="All" />
</ItemGroup>

<ItemGroup>
Expand Down
9 changes: 9 additions & 0 deletions AutoProperties.Fody.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeEditing/TypingAssist/CSharpAnnotationTypingAssist/IsEnabledAfterTypeName/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeEditing/TypingAssist/CSharpAnnotationTypingAssist/IsEnabledAtOtherPositions/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=AnnotateCanBeNullParameter/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=AnnotateCanBeNullTypeMember/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=AnnotateNotNullParameter/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=AnnotateNotNullTypeMember/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/ValueAnalysisMode/@EntryValue">OPTIMISTIC</s:String>
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=Antivirus/@EntryIndexedValue">DO_NOTHING</s:String></wpf:ResourceDictionary>
6 changes: 3 additions & 3 deletions AutoProperties.Fody/AutoProperties.Fody.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FodyHelpers" Version="6.0.0" />
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-19367-01" PrivateAssets="All" />
<PackageReference Include="FodyHelpers" Version="6.1.1" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.0-beta-20204-02" PrivateAssets="All" />
</ItemGroup>

<ItemGroup>
Expand All @@ -17,6 +16,7 @@
<Compile Include="..\FodyTools\FodyTools\InstructionSequence.cs" Link="InstructionSequence.cs" />
<Compile Include="..\FodyTools\FodyTools\InstructionSequences.cs" Link="InstructionSequences.cs" />
<Compile Include="..\FodyTools\FodyTools\MemberExtensionMethods.cs" Link="MemberExtensionMethods.cs" />
<Compile Include="..\FodyTools\FodyTools\Nullable.cs" Link="Nullable.cs" />
<Compile Include="..\FodyTools\FodyTools\TypeSystemExtensionMethods.cs" Link="TypeSystemExtensionMethods.cs" />
</ItemGroup>

Expand Down
36 changes: 13 additions & 23 deletions AutoProperties.Fody/AutoPropertyToBackingFieldMap.cs
Original file line number Diff line number Diff line change
@@ -1,65 +1,55 @@
using System.Collections.Generic;
using System.Linq;

using JetBrains.Annotations;
namespace AutoProperties.Fody
{
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;

using Mono.Cecil;
using Mono.Cecil;

namespace AutoProperties.Fody
{
internal class AutoPropertyToBackingFieldMap
{
[NotNull]
private readonly TypeDefinition _classDefinition;
[CanBeNull]
private IDictionary<string, AutoPropertyInfo> _map;
private IDictionary<string, AutoPropertyInfo>? _map;

public AutoPropertyToBackingFieldMap([NotNull] TypeDefinition classDefinition)
public AutoPropertyToBackingFieldMap(TypeDefinition classDefinition)
{
_classDefinition = classDefinition;
}

[ContractAnnotation("value:notnull => true")]
public bool TryGetValue([NotNull] string propertyName, [CanBeNull] out AutoPropertyInfo value)
public bool TryGetValue(string propertyName, [NotNullWhen(true)] out AutoPropertyInfo? value)
{
var map = _map ?? (_map = CreateMap());
var map = _map ??= CreateMap();

return map.TryGetValue(propertyName, out value);
}

[NotNull]
private IDictionary<string, AutoPropertyInfo> CreateMap()
{
var fields = _classDefinition.Fields;
var properties = _classDefinition.Properties;

// ReSharper disable AssignNullToNotNullAttribute
return CreateMap(properties, fields);
// ReSharper restore AssignNullToNotNullAttribute
}

[NotNull]
private static IDictionary<string, AutoPropertyInfo> CreateMap([NotNull, ItemNotNull] ICollection<PropertyDefinition> properties, [NotNull, ItemNotNull] ICollection<FieldDefinition> fields)
private static IDictionary<string, AutoPropertyInfo> CreateMap(ICollection<PropertyDefinition> properties, ICollection<FieldDefinition> fields)
{
return properties.Select(property => new { Property = property, BackingField = property.FindAutoPropertyBackingField(fields) })
.Where(item => item.BackingField != null)
.Select(item => new AutoPropertyInfo(item.BackingField, item.Property))
.Select(item => new AutoPropertyInfo(item.BackingField!, item.Property))
.ToDictionary(item => item.Property.Name);
}
}

internal class AutoPropertyInfo
{
public AutoPropertyInfo([NotNull] FieldDefinition backingField, [NotNull] PropertyDefinition property)
public AutoPropertyInfo(FieldDefinition backingField, PropertyDefinition property)
{
BackingField = backingField;
Property = property;
}

[NotNull]
public FieldDefinition BackingField { get; }

[NotNull]
public PropertyDefinition Property { get; }
}
}
59 changes: 19 additions & 40 deletions AutoProperties.Fody/BackingFieldAccessWeaver.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
using System;
using System.Diagnostics;
using System.Linq;
using FodyTools;
using JetBrains.Annotations;
using Mono.Cecil;
using Mono.Cecil.Cil;

namespace AutoProperties.Fody
namespace AutoProperties.Fody
{
using System;
using System.Linq;

using FodyTools;

using Mono.Cecil;
using Mono.Cecil.Cil;

internal class BackingFieldAccessWeaver
{
[NotNull]
private readonly ModuleDefinition _moduleDefinition;
[NotNull]
private readonly ILogger _logger;
[CanBeNull]
private readonly ISymbolReader _symbolReader;
private readonly ISymbolReader? _symbolReader;

public BackingFieldAccessWeaver([NotNull] ModuleDefinition moduleDefinition, [NotNull] ILogger logger)
public BackingFieldAccessWeaver(ModuleDefinition moduleDefinition, ILogger logger)
{
_logger = logger;
_moduleDefinition = moduleDefinition;
Expand All @@ -28,7 +25,6 @@ internal void Execute()
{
var allTypes = _moduleDefinition.GetTypes();

// ReSharper disable once AssignNullToNotNullAttribute
var allClasses = allTypes
.Where(x => x != null && x.IsClass && (x.BaseType != null));

Expand All @@ -42,8 +38,6 @@ internal void Execute()

var autoPropertyToBackingFieldMap = new AutoPropertyToBackingFieldMap(classDefinition);

// ReSharper disable once AssignNullToNotNullAttribute
// ReSharper disable once PossibleNullReferenceException
var allMethods = classDefinition.Methods.Where(method => method.HasBody);

foreach (var method in allMethods)
Expand All @@ -64,18 +58,15 @@ internal void Execute()
}
}

private void BypassAutoPropertySetters([NotNull] MethodDefinition method, [NotNull] AutoPropertyToBackingFieldMap autoPropertyToBackingFieldMap)
private void BypassAutoPropertySetters(MethodDefinition method, AutoPropertyToBackingFieldMap autoPropertyToBackingFieldMap)
{
// ReSharper disable once PossibleNullReferenceException
var instructions = method.Body.Instructions;

// ReSharper disable once PossibleNullReferenceException
for (var index = 0; index < instructions.Count; index++)
{
var instruction = instructions[index];

// ReSharper disable once AssignNullToNotNullAttribute
if (!instruction.IsPropertySetterCall(out string propertyName))
if (!instruction.IsPropertySetterCall(out string? propertyName) || propertyName == null)
continue;

if (!autoPropertyToBackingFieldMap.TryGetValue(propertyName, out var propertyInfo))
Expand All @@ -87,50 +78,40 @@ private void BypassAutoPropertySetters([NotNull] MethodDefinition method, [NotNu
}
}

private void ProcessExtensionMethodCalls([NotNull] MethodDefinition method, [NotNull] AutoPropertyToBackingFieldMap autoPropertyToBackingFieldMap)
private void ProcessExtensionMethodCalls(MethodDefinition method, AutoPropertyToBackingFieldMap autoPropertyToBackingFieldMap)
{
var processor = new ExtensionMethodProcessor(_logger, _symbolReader, method, autoPropertyToBackingFieldMap);

// ReSharper disable PossibleNullReferenceException
processor.ProcessExtensionMethodCalls("SetBackingField", pi => Instruction.Create(OpCodes.Stfld, pi.BackingField));
processor.ProcessExtensionMethodCalls("SetProperty", pi => Instruction.Create(OpCodes.Call, pi.Property.SetMethod));
// ReSharper restore PossibleNullReferenceException
}

private class ExtensionMethodProcessor
{
[NotNull]
private readonly ILogger _logger;
[NotNull]
private readonly MethodDefinition _method;
[NotNull]
private readonly AutoPropertyToBackingFieldMap _autoPropertyToBackingFieldMap;
[NotNull, ItemNotNull]
private readonly InstructionSequences _instructionSequences;

public ExtensionMethodProcessor([NotNull] ILogger logger, [CanBeNull] ISymbolReader symbolReader, [NotNull] MethodDefinition method, [NotNull] AutoPropertyToBackingFieldMap autoPropertyToBackingFieldMap)
public ExtensionMethodProcessor(ILogger logger, ISymbolReader? symbolReader, MethodDefinition method, AutoPropertyToBackingFieldMap autoPropertyToBackingFieldMap)
{
_logger = logger;
_method = method;
_autoPropertyToBackingFieldMap = autoPropertyToBackingFieldMap;

Debug.Assert(method.Body?.Instructions != null, "method.Body.Instructions != null");

_instructionSequences = new InstructionSequences(method.Body.Instructions, method.ReadSequencePoints(symbolReader));
}

public void ProcessExtensionMethodCalls([NotNull] string extensionMethodName, [NotNull] Func<AutoPropertyInfo, Instruction> createInstruction)
public void ProcessExtensionMethodCalls(string extensionMethodName, Func<AutoPropertyInfo, Instruction> createInstruction)
{
foreach (var sequence in _instructionSequences)
{
Debug.Assert(sequence != null, "sequence != null");

if (!ProcessSequence(sequence, extensionMethodName, createInstruction))
return;
}
}

private bool ProcessSequence([NotNull, ItemNotNull] InstructionSequence sequence, [NotNull] string extensionMethodName, [NotNull] Func<AutoPropertyInfo, Instruction> createInstruction)
private bool ProcessSequence(InstructionSequence sequence, string extensionMethodName, Func<AutoPropertyInfo, Instruction> createInstruction)
{
for (var index = 0; index < sequence.Count; index++)
{
Expand All @@ -141,9 +122,8 @@ private bool ProcessSequence([NotNull, ItemNotNull] InstructionSequence sequence

if (sequence.Count < 4
|| sequence[0].OpCode != OpCodes.Ldarg_0
|| !sequence[1].IsPropertyGetterCall(out string propertyName)
|| !sequence[1].IsPropertyGetterCall(out string? propertyName)
|| sequence.Skip(index + 1).Any(inst => inst?.OpCode != OpCodes.Nop)
// ReSharper disable once AssignNullToNotNullAttribute
|| !_autoPropertyToBackingFieldMap.TryGetValue(propertyName, out var propertyInfo))
{
var message = $"Invalid usage of extension method '{extensionMethodName}()': '{extensionMethodName}()' is only valid on auto-properties of class {_method.DeclaringType?.Name}";
Expand All @@ -153,8 +133,7 @@ private bool ProcessSequence([NotNull, ItemNotNull] InstructionSequence sequence

_logger.LogInfo($"Replace {extensionMethodName}() on property {propertyName} in method {_method}.");

// ReSharper disable once AssignNullToNotNullAttribute
sequence[index] = createInstruction(propertyInfo);
sequence[index] = createInstruction(propertyInfo!);
sequence.RemoveAt(1);
return true;
}
Expand Down
Loading

0 comments on commit f3444e8

Please sign in to comment.