diff --git a/src/Microsoft.Unity.Analyzers.Tests/GetComponentIncorrectTypeTests.cs b/src/Microsoft.Unity.Analyzers.Tests/GetComponentIncorrectTypeTests.cs index 942c6bb..f633702 100644 --- a/src/Microsoft.Unity.Analyzers.Tests/GetComponentIncorrectTypeTests.cs +++ b/src/Microsoft.Unity.Analyzers.Tests/GetComponentIncorrectTypeTests.cs @@ -99,7 +99,7 @@ private void Start() } [Fact] - public async Task GetComponentLegacyTest() + public async Task GetComponentLegacyInconclusiveTest() { const string test = @" using UnityEngine; @@ -108,6 +108,7 @@ class Camera : MonoBehaviour { private void Start() { + // we only check for the generic overload. var hello = GetComponent(""Hello""); } } @@ -133,7 +134,43 @@ private void Method() where T : Component } [Fact] - public async Task GetGenericMethodComponentIncorrectTest() + public async Task GetGenericClassComponentCorrectTest() + { + const string test = @" +using UnityEngine; + +class Camera : MonoBehaviour where T : Component +{ + private void Method() + { + var hello = GetComponent(); + } +} +"; + await VerifyCSharpDiagnosticAsync(test); + } + + [Fact] + public async Task GetGenericClassComponentInconclusiveTest() + { + const string test = @" +using UnityEngine; + +class Camera : MonoBehaviour +{ + private void Method() + { + // We need to infer on usages to be able to support this. For now, we don't. + GetComponent(); + } +} +"; + + await VerifyCSharpDiagnosticAsync(test); + } + + [Fact] + public async Task GetGenericMethodInconclusiveTest() { const string test = @" using UnityEngine; @@ -142,28 +179,55 @@ class Camera : MonoBehaviour { private void Method() { + // We need to infer on usages to be able to support this. For now, we don't. GetComponent(); } } "; - var diagnostic = ExpectDiagnostic() - .WithLocation(8, 9) - .WithArguments("T"); - await VerifyCSharpDiagnosticAsync(test, diagnostic); + await VerifyCSharpDiagnosticAsync(test); } [Fact] - public async Task GetGenericClassComponentCorrectTest() + public async Task GetGenericMethodExplicitInterfaceCorrectTest() { const string test = @" using UnityEngine; -class Camera : MonoBehaviour where T : Component +class Camera : MonoBehaviour { - private void Method() + private void Start() { - var hello = GetComponent(); + Test(); + } + + private void Test() where T : IMyInterface + { + gameObject.GetComponent(); + } + + private interface IMyInterface { } +} +"; + await VerifyCSharpDiagnosticAsync(test); + } + + [Fact] + public async Task GetGenericMethodExplicitTypeCorrectTest() + { + const string test = @" +using UnityEngine; + +class Camera : MonoBehaviour +{ + private void Start() + { + Test(); + } + + private void Test() where T : Component + { + gameObject.GetComponent(); } } "; @@ -171,23 +235,58 @@ private void Method() } [Fact] - public async Task GetGenericClassComponentIncorrectTest() + public async Task GetGenericMethodExplicitTypeIncorrectTest() { const string test = @" +using System; using UnityEngine; -class Camera : MonoBehaviour +class Camera : MonoBehaviour { - private void Method() + private void Start() { - GetComponent(); + Test(); + } + + private void Test() where T : Exception + { + gameObject.GetComponent(); } } "; var diagnostic = ExpectDiagnostic() - .WithLocation(8, 9) + .WithLocation(14, 9) .WithArguments("T"); await VerifyCSharpDiagnosticAsync(test, diagnostic); } + + [Fact] + public async Task GetGenericMethodTypeReferenceConstraintInconclusiveTest() + { + const string test = @" +using UnityEngine; + +class Camera : MonoBehaviour +{ + private void Start() + { + // this one is valid, + Test(); + + // this one is not, but we need to infer on usages to be able to support this. For now, we don't. + Test(); + } + + private void Test() where T : class + { + gameObject.GetComponent(); + } + + private interface IMyInterface { } +} +"; + + await VerifyCSharpDiagnosticAsync(test); + } } diff --git a/src/Microsoft.Unity.Analyzers/GetComponentIncorrectType.cs b/src/Microsoft.Unity.Analyzers/GetComponentIncorrectType.cs index 9affd20..94ad995 100644 --- a/src/Microsoft.Unity.Analyzers/GetComponentIncorrectType.cs +++ b/src/Microsoft.Unity.Analyzers/GetComponentIncorrectType.cs @@ -68,16 +68,25 @@ private static bool HasInvalidTypeArgument(IMethodSymbol method, out string? ide if (argumentType == null) return false; - if (argumentType.Extends(typeof(UnityEngine.Component)) || argumentType.TypeKind == TypeKind.Interface) + if (IsComponentOrInterface(argumentType)) return false; if (argumentType.TypeKind == TypeKind.TypeParameter && argumentType is ITypeParameterSymbol typeParameter) { - if (typeParameter.ConstraintTypes.Any(t => t.Extends(typeof(UnityEngine.Component)))) + // We need to infer the effective generic tye given usage, but we don't do that yet. + if (typeParameter.ConstraintTypes.IsEmpty) + return false; + + if (typeParameter.ConstraintTypes.Any(IsComponentOrInterface)) return false; } identifier = argumentType.Name; return true; } + + private static bool IsComponentOrInterface(ITypeSymbol argumentType) + { + return argumentType.Extends(typeof(UnityEngine.Component)) || argumentType.TypeKind == TypeKind.Interface; + } }