Skip to content

Commit

Permalink
feat: Add UIView.Dispose detection analyzer
Browse files Browse the repository at this point in the history
  • Loading branch information
jeromelaban committed Nov 30, 2023
1 parent 1359637 commit 4df839e
Show file tree
Hide file tree
Showing 7 changed files with 496 additions and 10 deletions.
24 changes: 20 additions & 4 deletions doc/articles/uno-build-error-codes.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,42 @@
---
uid: Build.Solution.error-codes
---
# Uno Build error codes
# Uno error codes

## UNOB0001: Cannot build with both Uno.WinUI and Uno.UI NuGet packages referenced
## MSBuild Errors

### UNOB0001: Cannot build with both Uno.WinUI and Uno.UI NuGet packages referenced

This error code means that a project has determined what both `Uno.WinUI` and `Uno.UI` packages are referenced.

To fix this issue, you may be explicitly referencing `Uno.UI` and `Uno.WinUI` in your `csproj`, or you may be referencing a NuGet package that is incompatible with your current project's configuration.

For instance, if your project references `Uno.WinUI`, and you try to reference `SkiaSharp.View.Uno`, you will get this error. To fix it, you'll need to reference `SkiaSharp.View.Uno.WinUI` instead.

## UNOB0002: Project XX contains a reference to Uno Platform but does not contain a WinAppSDK compatible target framework.
### UNOB0002: Project XX contains a reference to Uno Platform but does not contain a WinAppSDK compatible target framework.

This error code means that a WinAppSDK project is referencing a project in your solution which is not providing a `net7.0-windows10.xx` TargetFramework.

This can happen if a project contains only a `net7.0` TargetFramework and has a NuGet reference to `Uno.WinUI`.

To fix this, it is best to start from a `Cross Platform Library` project template provided by the Uno Platform [visual studio extension](xref:Guide.HowTo.Create-Control-Library), or using [`dotnet new`](xref:Uno.GetStarted.dotnet-new).

## UNOB0003: Ignoring resource XX, could not determine the language
### UNOB0003: Ignoring resource XX, could not determine the language

This error may occur during resources (`.resw`) analysis if the framework does not recognize the specified language code.

For instance, the language code `zh-CN` is not recognized and `zh-Hans` should be used instead.

## Compiler Errors

### UNO0001

A member is not implemented, see [this page](xref:Uno.Development.NotImplemented) for more details.

### UNO0002

**Do not call Dispose() on XXX**

On iOS and Catalyst, calling `Dispose()` or `Dispose(bool)` on a type inheriting directly from `UIKit.UIView` can lead to unstable results. It is not needed to call `Dispose` as the runtime will do so automatically during garbage collection.

Invocations to `Dispose` can cause the application to crash in `__NSObject_Disposer drain`, cause `ObjectDisposedException` exception to be thrown. More information can be found in [xamarin/xamarin-macios#19493](https://github.com/xamarin/xamarin-macios/issues/19493).
Original file line number Diff line number Diff line change
Expand Up @@ -569,9 +569,21 @@ private void WriteDispose(INamedTypeSymbol typeSymbol, IndentedStringBuilder bui
[SuppressMessage(
""Microsoft.Usage"",
""CA2215:DisposeMethodsShouldCallBaseClassDispose"",
Justification = ""The dispose is re-scheduled using the ValidateDispose method"")]
Justification = ""The dispose is re-scheduled in order to properly remove children from their parent"")]
protected sealed override void Dispose(bool disposing)
{{
// This method is present in order to ensure for faster collection of a disposed visual tree
// as well as ensure that instances can be properly returned to the FrameworkTemplatePool.
//
// This method can be called by the finalizer, in which case the object is re-registered
// for finalization, and the Dispose method is invoked explicitly during a idle dispatch.
//
// This operation can fail if Dispose() is called by user code on UIView instances.
// The Roslyn analyzer UnoDoNotDisposeNativeViews will warn the developer if this is the case.
//
// For native exceptions that may be raised if Disposed is called incorrectly, see
// https://github.com/xamarin/xamarin-macios/issues/19493.
if(_isDisposed)
{{
base.Dispose(disposing);
Expand Down
2 changes: 1 addition & 1 deletion src/Uno.Analyzers.Tests/Uno.Analyzers.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<IsPackable>false</IsPackable>
<OutputPath>..\Build\Tests\bin\$(Configuration)_$(Platform)\</OutputPath>

<CodeAnalysisVersionForAnalyzersTests>3.8.0</CodeAnalysisVersionForAnalyzersTests>
<CodeAnalysisVersionForAnalyzersTests>4.0.0</CodeAnalysisVersionForAnalyzersTests>
</PropertyGroup>

<ItemGroup>
Expand Down
308 changes: 308 additions & 0 deletions src/Uno.Analyzers.Tests/UnoDoNotDisposeNativeViewsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,308 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.CodeAnalysis.Testing;
using Uno.Analyzers.Tests.Verifiers;
using System.Threading.Tasks;
using System.Collections.Generic;

namespace Uno.Analyzers.Tests
{
using Verify = CSharpCodeFixVerifier<UnoDoNotDisposeNativeViews, EmptyCodeFixProvider>;

[TestClass]
public class UnoDoNotDisposeNativeViewsTests
{
private static string UnoUIViewClass =
"""
namespace UIKit
{
public class UIView : IDisposable
{
public void Dispose() { }
public void Dispose(bool disposing) { }
}
}
""";

private static async Task TestWithPreprocessorDirective(string testCode, IEnumerable<string> preprocessorSymbols)
{
await new Verify.Test
{
TestCode = testCode,
FixedCode = testCode,
PreprocessorSymbols = preprocessorSymbols,
}.RunAsync();
}

[TestMethod]
public async Task Nothing()
{
var test = @"";

await Verify.VerifyAnalyzerAsync(test);
}

[TestMethod]
public async Task When_InheritedCallsDispose()
{
var test =
"""
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
namespace ConsoleApplication1
{
public class MyInherited : UIKit.UIView { }
public class TypeName
{
public static void Test()
{
var b = new MyInherited();
[|b.Dispose()|];
}
}
}
""" + UnoUIViewClass;

await Verify.VerifyAnalyzerAsync(test);
}

[TestMethod]
public async Task When_InheritedCallsDisposeBool()
{
var test =
"""
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
namespace ConsoleApplication1
{
public class MyInherited : UIKit.UIView { }
public class TypeName
{
public static void Test()
{
var b = new MyInherited();
[|b.Dispose(true)|];
}
}
}
""" + UnoUIViewClass;

await Verify.VerifyAnalyzerAsync(test);
}

[TestMethod]
public async Task When_InheritedCallsUsingBlock()
{
var test =
"""
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
namespace ConsoleApplication1
{
public class MyInherited : UIKit.UIView { }
public class TypeName
{
public static void Test()
{
using(var [|b|] = new MyInherited())
{
}
}
}
}
""" + UnoUIViewClass;

await Verify.VerifyAnalyzerAsync(test);
}


[TestMethod]
public async Task When_InheritedCallsUsingBlockMultipleDeclarators()
{
var test =
"""
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
namespace ConsoleApplication1
{
public class MyInherited : UIKit.UIView { }
public class TypeName
{
public static void Test()
{
using(MyInherited [|b|] = new MyInherited(), [|b2|] = new MyInherited())
{
}
}
}
}
""" + UnoUIViewClass;

await Verify.VerifyAnalyzerAsync(test);
}

[TestMethod]
public async Task When_InheritedCallsUsingStatement()
{
var test =
"""
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
namespace ConsoleApplication1
{
public class MyInherited : UIKit.UIView { }
public class TypeName
{
public static void Test()
{
using var [|b = new MyInherited()|];
}
}
}
""" + UnoUIViewClass;

await Verify.VerifyAnalyzerAsync(test);
}

[TestMethod]
public async Task When_InheritedCallsUsingStatementMultipleDeclarators()
{
var test =
"""
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
namespace ConsoleApplication1
{
public class MyInherited : UIKit.UIView { }
public class TypeName
{
public static void Test()
{
using MyInherited [|b = new MyInherited()|], [|b2 = new MyInherited()|];
}
}
}
""" + UnoUIViewClass;

await Verify.VerifyAnalyzerAsync(test);
}

[TestMethod]
public async Task When_InheritedCallsUsingStatementDiscard()
{
var test =
"""
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
namespace ConsoleApplication1
{
public class MyInherited : UIKit.UIView { }
public class TypeName
{
public static void Test()
{
using var [|_ = new MyInherited()|];
}
}
}
""" + UnoUIViewClass;

await Verify.VerifyAnalyzerAsync(test);
}

[TestMethod]
public async Task When_DirectCallsDispose()
{
var test =
"""
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
namespace ConsoleApplication1
{
public class TypeName
{
public static void Test()
{
[|new UIKit.UIView().Dispose()|];
}
}
}
""" + UnoUIViewClass;

await Verify.VerifyAnalyzerAsync(test);
}

[TestMethod]
public async Task When_DirectCallsDisposeBool()
{
var test =
"""
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
namespace ConsoleApplication1
{
public class TypeName
{
public static void Test()
{
[|new UIKit.UIView().Dispose(true)|];
}
}
}
""" + UnoUIViewClass;

await Verify.VerifyAnalyzerAsync(test);
}
}
}
2 changes: 1 addition & 1 deletion src/Uno.Analyzers/Uno.Analyzers.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.3.1" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.0" />
</ItemGroup>

</Project>
Loading

0 comments on commit 4df839e

Please sign in to comment.