From 136375727885684a1691862c066e966fb408e730 Mon Sep 17 00:00:00 2001 From: Donald Wu Date: Sun, 8 Oct 2017 13:20:25 +0800 Subject: [PATCH 01/10] Add serveral new analysis DoNotUseCameraMainInUpdate ShouldCacheDelegate Remove DoNotUseForeachInUpdate DoNotBoxWhenInvoke DoNotUseEnumTypeParameter StructShouldImplementIEquatable StructShouldOverride DoNotUseMaterialName --- .gitignore | 1 + UnityEngineAnalyzer.CLI/SolutionAnalyzer.cs | 4 +- ...DoNotUseCameraMainInUpdateAnalyzerTests.cs | 86 ++++++ .../ShouldCacheDelegateAnalyzerTests.cs | 222 ++++++++++++++ ...oNotUseFindMethodsInUpdateAnalyzerTests.cs | 36 +++ .../DoNotUseForeachInUpdateAnalyzerTests.cs | 60 ++-- .../DoNotBoxWhenInvokeAnalyzerTests.cs | 157 ++++++++++ .../DoNotGCAllocInUpdateAnalyzerTests.cs | 278 ++++++++++++++++++ .../DoNotUseEnumTypeParameterAnalyzerTests.cs | 132 +++++++++ .../Language/StructImplementAnalyzerTest.cs | 101 +++++++ ...ctOverrideEqualsGetHashCodeAnalyzerTest.cs | 72 +++++ .../StructOverrideEqualsObjectAnalyzerTest.cs | 70 +++++ .../DoNotUseMaterialNameAnalyzerTests.cs | 41 +++ .../UnityEngineAnalyzer.Test.csproj | 11 +- .../DoNotUseCameraMainInUpdateAnalyzer.cs | 157 ++++++++++ ...UseCameraMainInUpdateResources.Designer.cs | 100 +++++++ .../DoNotUseCameraMainInUpdateResources.resx | 135 +++++++++ ...seFindMethodsInUpdateResources.Designer.cs | 74 +++++ .../Delegates/ShouldCacheDelegateAnalyzer.cs | 68 +++++ .../ShouldCacheDelegateResource.Designer.cs | 91 ++++++ .../ShouldCacheDelegateResource.resx | 132 +++++++++ .../DiagnosticDescriptors.cs | 122 ++++++++ .../UnityEngineAnalyzer/DiagnosticIDs.cs | 11 +- ...seFindMethodsInUpdateResources.Designer.cs | 32 +- .../DoNotUseFindMethodsInUpdateResources.resx | 2 +- .../DoNotUseForEachInUpdate.cs | 6 + .../GCAlloc/DoNoGCAllocInUpdateAnalyzer.cs | 204 +++++++++++++ .../GCAlloc/DoNotBoxWhenInvokeAnalyzer.cs | 70 +++++ .../DoNotBoxWhenInvokeResource.Designer.cs | 91 ++++++ .../GCAlloc/DoNotBoxWhenInvokeResource.resx | 132 +++++++++ .../DoNotGCAllocInUpdateResources.Designer.cs | 100 +++++++ .../DoNotGCAllocInUpdateResources.resx | 135 +++++++++ .../DoNotUseEnumTypeParameterAnalyzer.cs | 54 ++++ ...otUseEnumTypeParameterResource.Designer.cs | 91 ++++++ .../DoNotUseEnumTypeParameterResource.resx | 132 +++++++++ .../StructAnalyzerResources.Designer.cs | 109 +++++++ .../Language/StructAnalyzerResources.resx | 138 +++++++++ .../Language/StructImplementationAnalyzer.cs | 79 +++++ .../StructOverrideEqualsObjectAnalyzer.cs | 68 +++++ .../StructOverrideGetHashCodeAnalyzer.cs | 63 ++++ .../Render/DoNotUseMaterialNameAnalyzer.cs | 74 +++++ .../DoNotUseMaterialNameResource.Designer.cs | 68 +++++ .../Render/DoNotUseMaterialNameResource.resx | 132 +++++++++ .../DoNotUseStateNameResource.Designer.cs | 68 +++++ .../UnityEngineAnalyzer.csproj | 73 +++++ 45 files changed, 4032 insertions(+), 50 deletions(-) create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Camera/DoNotUseCameraMainInUpdateAnalyzerTests.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Delegates/ShouldCacheDelegateAnalyzerTests.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer.Test/GCAlloc/DoNotBoxWhenInvokeAnalyzerTests.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer.Test/GCAlloc/DoNotGCAllocInUpdateAnalyzerTests.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Generics/DoNotUseEnumTypeParameterAnalyzerTests.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/StructImplementAnalyzerTest.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/StructOverrideEqualsGetHashCodeAnalyzerTest.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/StructOverrideEqualsObjectAnalyzerTest.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Render/DoNotUseMaterialNameAnalyzerTests.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/DoNotUseCameraMainInUpdateAnalyzer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/DoNotUseCameraMainInUpdateResources.Designer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/DoNotUseCameraMainInUpdateResources.resx create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/DoNotUseFindMethodsInUpdateResources.Designer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateAnalyzer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateResource.Designer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateResource.resx create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNoGCAllocInUpdateAnalyzer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeAnalyzer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeResource.Designer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeResource.resx create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotGCAllocInUpdateResources.Designer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotGCAllocInUpdateResources.resx create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/DoNotUseEnumTypeParameterAnalyzer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/DoNotUseEnumTypeParameterResource.Designer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/DoNotUseEnumTypeParameterResource.resx create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructAnalyzerResources.Designer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructAnalyzerResources.resx create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructImplementationAnalyzer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructOverrideEqualsObjectAnalyzer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructOverrideGetHashCodeAnalyzer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseMaterialNameAnalyzer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseMaterialNameResource.Designer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseMaterialNameResource.resx create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseStateNameResource.Designer.cs diff --git a/.gitignore b/.gitignore index 170873f..738c4ca 100644 --- a/.gitignore +++ b/.gitignore @@ -257,3 +257,4 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk +.DS_Store diff --git a/UnityEngineAnalyzer.CLI/SolutionAnalyzer.cs b/UnityEngineAnalyzer.CLI/SolutionAnalyzer.cs index 0e13ea8..52bdcda 100644 --- a/UnityEngineAnalyzer.CLI/SolutionAnalyzer.cs +++ b/UnityEngineAnalyzer.CLI/SolutionAnalyzer.cs @@ -6,7 +6,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.MSBuild; -using UnityEngineAnalyzer.ForEachInUpdate; +using UnityEngineAnalyzer.IL2CPP; namespace UnityEngineAnalyzer.CLI { @@ -27,7 +27,7 @@ private ImmutableArray GetAnalyzers() { var listBuilder = ImmutableArray.CreateBuilder(); - var assembly = typeof(DoNotUseForEachInUpdate).Assembly; + var assembly = typeof(UnsealedDerivedClassAnalyzer).Assembly; var allTypes = assembly.DefinedTypes; foreach (var type in allTypes) diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Camera/DoNotUseCameraMainInUpdateAnalyzerTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Camera/DoNotUseCameraMainInUpdateAnalyzerTests.cs new file mode 100644 index 0000000..b5bdbbf --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Camera/DoNotUseCameraMainInUpdateAnalyzerTests.cs @@ -0,0 +1,86 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.FindMethodsInUpdate; + + +//using Microsoft.CodeAnalysis.Workspaces; + +namespace UnityEngineAnalyzer.Test.FindMethodsInUpdate +{ + [TestFixture] + sealed class DoNotUseCameraMainInUpdateAnalyzerTests : AnalyzerTestFixture + { + + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new DoNotUseCameraMainInUpdateAnalyzer(); + + [Test] + public void CameraMainInUpdate() + { + var code = @" +using UnityEngine; + +class C : MonoBehaviour +{ + void Update() + { + Camera main = [|Camera.main|]; + + //var result = GameObject.Find(""param""); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.DoNotUseCameraMainInUpdate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void CameraMainInUpdateRecursive() + { + var code = @" +using UnityEngine; + +class C : MonoBehaviour +{ + void Update() + { + [|MyMethod()|]; + //var result = GameObject.Find(""param""); + } + + void MyMethod() + { + Camera main = Camera.main; + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.DoNotUseCameraMainInUpdate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Delegates/ShouldCacheDelegateAnalyzerTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Delegates/ShouldCacheDelegateAnalyzerTests.cs new file mode 100644 index 0000000..943cafa --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Delegates/ShouldCacheDelegateAnalyzerTests.cs @@ -0,0 +1,222 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.Delegates; + + +//using Microsoft.CodeAnalysis.Workspaces; + +namespace UnityEngineAnalyzer.Test.Delegates +{ + [TestFixture] + sealed class ShouldCacheDelegatesAnalyzerTests : AnalyzerTestFixture + { + + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new ShouldCacheDelegateAnalyzer(); + + [Test] + public void EventDidNotCacheDelegate() + { + var code = @" + +using System; + +class C +{ + public event EventHandler e; + void Update() + { + e += [|OnCallBack|]; + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void EventDidNotCacheDelegate2() + { + var code = @" + +using System; + +class C +{ + public event EventHandler e; + void Update() + { + e -= [|OnCallBack|]; + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void EventDidCacheDelegate() + { + var code = @" + +using System; + +class C +{ + public event EventHandler e; + private EventHandler m_cachedDelegate = OnCallBack; + + void Intialize() + { + m_cachedDelegate = OnCallBack; + } + + void Update() + { + e += [|m_cachedDelegate|]; + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void FunctionDidNotCacheDelegate() + { + var code = @" + +using System; + +class C +{ + public event EventHandler e; + void Update() + { + CallDelegate([|OnCallBack|]); + } + + private void CallDelegate(EventHandler handler) + { + + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void FunctionDidCacheDelegate() + { + var code = @" + +using System; + +class C +{ + public event EventHandler e; + private EventHandler m_cachedDelegate; + + void Intialize() + { + m_cachedDelegate = OnCallBack; + } + + void Update() + { + CallDelegate([|m_cachedDelegate|]); + } + + private void CallDelegate(EventHandler handler) + { + + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/FindMethodsInUpdate/DoNotUseFindMethodsInUpdateAnalyzerTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/FindMethodsInUpdate/DoNotUseFindMethodsInUpdateAnalyzerTests.cs index aa72bff..4a27712 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/FindMethodsInUpdate/DoNotUseFindMethodsInUpdateAnalyzerTests.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/FindMethodsInUpdate/DoNotUseFindMethodsInUpdateAnalyzerTests.cs @@ -47,6 +47,42 @@ void Update() } } + + [Test] + public void GameObjectFindInUpdateRecursive() + { + var code = @" +using UnityEngine; + +class C : MonoBehaviour +{ + void Update() + { + [|MyMethod()|]; + //var result = GameObject.Find(""param""); + } + + void MyMethod() + { + GameObject.Find(""param""); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.DoNotUseFindMethodsInUpdate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] public void GameObjectFindInStart() { diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/ForEachInUpdate/DoNotUseForeachInUpdateAnalyzerTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/ForEachInUpdate/DoNotUseForeachInUpdateAnalyzerTests.cs index 24649e0..f3353a6 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/ForEachInUpdate/DoNotUseForeachInUpdateAnalyzerTests.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/ForEachInUpdate/DoNotUseForeachInUpdateAnalyzerTests.cs @@ -9,48 +9,50 @@ //using Microsoft.CodeAnalysis.Workspaces; +/* namespace UnityEngineAnalyzer.Test.ForEachInUpdate { - [TestFixture] - sealed class DoNotUseForeachInUpdateAnalyzerTests : AnalyzerTestFixture - { +[TestFixture] +sealed class DoNotUseForeachInUpdateAnalyzerTests : AnalyzerTestFixture +{ - protected override string LanguageName => LanguageNames.CSharp; - protected override DiagnosticAnalyzer CreateAnalyzer() => new DoNotUseForEachInUpdate(); + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new DoNotUseForEachInUpdate(); - [Test] - public void ForEachInUpdate() - { - var code = @" + [Test] + public void ForEachInUpdate() + { + var code = @" using UnityEngine; class C : MonoBehaviour { - void Update() - { - var colors = new[] {""red"", ""white"", ""blue""}; - var result = string.Empty; - [|foreach|] (var color in colors) - { - result += color; - } +void Update() +{ + var colors = new[] {""red"", ""white"", ""blue""}; + var result = string.Empty; + [|foreach|] (var color in colors) + { + result += color; } + } }"; - - Document document; - TextSpan span; - if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, - out document, out span)) - { - HasDiagnostic(document, span, DiagnosticIDs.DoNotUseForEachInUpdate); - } - else - { - Assert.Fail("Could not load unit test code"); - } + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.DoNotUseForEachInUpdate); + } + else + { + Assert.Fail("Could not load unit test code"); } } } +} +*/ \ No newline at end of file diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/GCAlloc/DoNotBoxWhenInvokeAnalyzerTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/GCAlloc/DoNotBoxWhenInvokeAnalyzerTests.cs new file mode 100644 index 0000000..7182724 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/GCAlloc/DoNotBoxWhenInvokeAnalyzerTests.cs @@ -0,0 +1,157 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.GCAlloc; + + +//using Microsoft.CodeAnalysis.Workspaces; + +namespace UnityEngineAnalyzer.Test.GCAlloc +{ + [TestFixture] + sealed class DoNotBoxWhenInvokeAnalyzerTests : AnalyzerTestFixture + { + + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new DoNotBoxWhenInvokeAnalyzer(); + + [Test] + public void BoxWhenInvokeWithLiteral() + { + var code = @" + +class C +{ + private void Method(object p1, int p2) + { + } + + private void Caller() + { + Method([|234|], 2); + } +} + +"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, null, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.DoNotBoxWhenInvoke); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void BoxWhenInvokeWithIdentifier() + { + var code = @" + +class C +{ + private void Method(object p1, int p2) + { + } + + private void Caller() + { + int arg = 234; + Method([|arg|], 2); + } +} + +"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, null, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.DoNotBoxWhenInvoke); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void NoBoxWhenInvokeWithLiteral() + { + var code = @" + +class C +{ + private void Method(int p1, int p2) + { + } + + private void Caller() + { + Method([|234|], 2); + } +} + +"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, null, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.DoNotBoxWhenInvoke); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + + [Test] + public void NoBoxWhenInvokeWithIdentifier() + { + var code = @" + +class C +{ + private void Method(int p1, int p2) + { + } + + private void Caller() + { + int arg = 234; + Method([|arg|], 2); + } +} + +"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, null, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.DoNotBoxWhenInvoke); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/GCAlloc/DoNotGCAllocInUpdateAnalyzerTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/GCAlloc/DoNotGCAllocInUpdateAnalyzerTests.cs new file mode 100644 index 0000000..dd25ab1 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/GCAlloc/DoNotGCAllocInUpdateAnalyzerTests.cs @@ -0,0 +1,278 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.GCAlloc; + + +//using Microsoft.CodeAnalysis.Workspaces; + +namespace UnityEngineAnalyzer.Test.GCAlloc +{ + [TestFixture] + sealed class DoNotGCAllocInUpdateAnalyzerTests : AnalyzerTestFixture + { + + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new DoNotGCAllocInUpdateAnalyzer(); + + [Test] + public void GCAllocValueTypeInUpdate() + { + var code = @" +using UnityEngine; + +struct S {} + +class C : MonoBehaviour +{ + void Update() + { + S s = [|new S()|]; + + //var result = GameObject.Find(""param""); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.DoNotGCAllocInUpdate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void GCAllocInUpdate() + { + var code = @" +using UnityEngine; + +class B {} + +class C : MonoBehaviour +{ + void Update() + { + B b = [|new B()|]; + + //var result = GameObject.Find(""param""); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.DoNotGCAllocInUpdate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + + [Test] + public void GCAllocInBranchInUpdate() + { + var code = @" +using UnityEngine; + +class B {} + +class C : MonoBehaviour +{ + void Update() + { + B b; + if(b == null) + { + b = [|new B()|]; + }; + + //var result = GameObject.Find(""param""); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.DoNotGCAllocInUpdate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void GCAllocInUpdateRecursive() + { + var code = @" +using UnityEngine; + +class B {} + +class C : MonoBehaviour +{ + void Update() + { + [|MyMethod()|]; + //var result = GameObject.Find(""param""); + } + + void MyMethod() + { + B b = new B(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.DoNotGCAllocInUpdate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + + [Test] + public void GCAllocInUpdateRecursiveWithBranch() + { + var code = @" +using UnityEngine; + +class B {} + +class C : MonoBehaviour +{ + void Update() + { + if(true) + { + [|MyMethod()|]; + } + //var result = GameObject.Find(""param""); + } + + void MyMethod() + { + B b = new B(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.DoNotGCAllocInUpdate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void GCAllocInUpdateRecursiveWithBranch2() + { + var code = @" +using UnityEngine; + +class B {} + +class C : MonoBehaviour +{ + void Update() + { + [|MyMethod()|]; + //var result = GameObject.Find(""param""); + } + + void MyMethod() + { + if(true) + { + B b = new B(); + } + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.DoNotGCAllocInUpdate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void GCAllocValueTypeInUpdateRecursive() + { + var code = @" +using UnityEngine; + +struct S {} + +class C : MonoBehaviour +{ + void Update() + { + [|MyMethod()|]; + //var result = GameObject.Find(""param""); + } + + void MyMethod() + { + S s = new S(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.DoNotGCAllocInUpdate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Generics/DoNotUseEnumTypeParameterAnalyzerTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Generics/DoNotUseEnumTypeParameterAnalyzerTests.cs new file mode 100644 index 0000000..4a64414 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Generics/DoNotUseEnumTypeParameterAnalyzerTests.cs @@ -0,0 +1,132 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.Generics; + + +//using Microsoft.CodeAnalysis.Workspaces; + +namespace UnityEngineAnalyzer.Test.GCAlloc +{ + [TestFixture] + sealed class DoNotUseEnumTypeParameterAnalyzerTests : AnalyzerTestFixture + { + + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new DoNotUseEnumTypeParameterAnalyzer(); + + [Test] + public void UseEnumTypeParameter() + { + var code = @" + +class C +{ + public enum MyEnum { One, Two } + public Dictionary<[|MyEnum|], int> map; +} + +"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, null, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.DoNoUseEnumTypeParameter); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void UseEnumTypeParameter2() + { + var code = @" + +class C +{ + public enum MyEnum { One, Two } + public Dictionary> map; +} + +"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, null, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.DoNoUseEnumTypeParameter); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + + [Test] + public void UseEnumTypeParameter3() + { + var code = @" + +class C +{ + public enum MyEnum { One, Two } + public Dictionary>> map; +} + +"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, null, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.DoNoUseEnumTypeParameter); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void UseNoEnumTypeParameter() + { + var code = @" + +class C +{ + public enum MyEnum { One, Two } + public Dictionary<[|int|], int> map; +} + +"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, null, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.DoNoUseEnumTypeParameter); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/StructImplementAnalyzerTest.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/StructImplementAnalyzerTest.cs new file mode 100644 index 0000000..365921a --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/StructImplementAnalyzerTest.cs @@ -0,0 +1,101 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.Language; + + +//using Microsoft.CodeAnalysis.Workspaces; + +namespace UnityEngineAnalyzer.Test.Language +{ + [TestFixture] + sealed class StructImplementationAnalyzerTests : AnalyzerTestFixture + { + + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new StructImplementAnalyzer(); + + [Test] + public void StructDoNotImplementIEquatable() + { + var code = @" +using UnityEngine; + +struct [|S|] +{ +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.StructShouldImplementIEquatable); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + + [Test] + public void StructDoImplementIEquatable() + { + var code = @" +using UnityEngine; +using System; + +struct [|S|] : IEquatable +{ + public bool Equals(S other) { return false; } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.StructShouldImplementIEquatable); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void StructDoNotImplementCorrectIEquatable() + { + var code = @" +using UnityEngine; +using System; + + +struct [|S|] : IEquatable +{ + public bool Equals(int other) { return false; } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.StructShouldImplementIEquatable); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/StructOverrideEqualsGetHashCodeAnalyzerTest.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/StructOverrideEqualsGetHashCodeAnalyzerTest.cs new file mode 100644 index 0000000..034e50e --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/StructOverrideEqualsGetHashCodeAnalyzerTest.cs @@ -0,0 +1,72 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.Language; + + +//using Microsoft.CodeAnalysis.Workspaces; + +namespace UnityEngineAnalyzer.Test.Language +{ + [TestFixture] + sealed class StructOverrideGetHashCodeTests : AnalyzerTestFixture + { + + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new StructOverrideGetHashCodeAnalyzer(); + + + [Test] + public void StructDoNotOverrideAnything() + { + var code = @" +using UnityEngine; + +struct [|S|] +{ +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.StructShouldOverrideGetHashCode); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void StructOverridGetHashCode() + { + var code = @" +using UnityEngine; + +struct [|S|] +{ + public override int GetHashCode() { return 0; } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.StructShouldOverrideGetHashCode); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/StructOverrideEqualsObjectAnalyzerTest.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/StructOverrideEqualsObjectAnalyzerTest.cs new file mode 100644 index 0000000..118e55a --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/StructOverrideEqualsObjectAnalyzerTest.cs @@ -0,0 +1,70 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.Language; + + +//using Microsoft.CodeAnalysis.Workspaces; + +namespace UnityEngineAnalyzer.Test.Language +{ + [TestFixture] + sealed class StructOverrideEqualsObjectAnalyzerTests : AnalyzerTestFixture + { + + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new StructOverrideEqualsObjectAnalyzer(); + + + [Test] + public void StructDoNotOverrideAnything() + { + var code = @" +using UnityEngine; + +struct [|S|] +{ +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.StructShouldOverrideEquals); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void StructOverrideEqualsObject() + { + var code = @" +using UnityEngine; + +struct [|S|] +{ + public override bool Equals(object obj) { return false; } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.StructShouldOverrideEquals); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Render/DoNotUseMaterialNameAnalyzerTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Render/DoNotUseMaterialNameAnalyzerTests.cs new file mode 100644 index 0000000..60a7d77 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Render/DoNotUseMaterialNameAnalyzerTests.cs @@ -0,0 +1,41 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.Render; + +namespace UnityEngineAnalyzer.Test.Render +{ + + [TestFixture] + sealed class DoNotUseMaterialNameAnalyzerTests : AnalyzerTestFixture + { + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new DoNotUseMaterialNameAnalyzer(); + + [Test] + public void MaterialSetFloatStringName() + { + const string code = @" +using UnityEngine; + +class C : MonoBehaviour +{ + Material mat; + + void Start() + { + [|mat.SetFloat(""Run"", 1.2f)|]; + } +}"; + + Document document; + TextSpan span; + TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); + + HasDiagnostic(document, span, DiagnosticIDs.DoNotUseMaterialNameInMaterial); + } + + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj index cc86884..e7ded7d 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj @@ -21,7 +21,7 @@ bin\Debug\ DEBUG;TRACE prompt - 4 + 0 false @@ -130,14 +130,23 @@ + + + + + + + + + diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/DoNotUseCameraMainInUpdateAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/DoNotUseCameraMainInUpdateAnalyzer.cs new file mode 100644 index 0000000..c40790a --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/DoNotUseCameraMainInUpdateAnalyzer.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Generic; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; + +namespace UnityEngineAnalyzer.FindMethodsInUpdate +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class DoNotUseCameraMainInUpdateAnalyzer : DiagnosticAnalyzer + { + + private Dictionary _indirectCallers; + + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create( + DiagnosticDescriptors.DoNotUseCameraMainInUpdate, + DiagnosticDescriptors.DoNotUseCameraMainInUpdateRecursive); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeClassSyntax, SyntaxKind.ClassDeclaration); + } + + public void AnalyzeClassSyntax(SyntaxNodeAnalysisContext context) + { + var monoBehaviourInfo = new MonoBehaviourInfo(context); + + var searched = new Dictionary(); + _indirectCallers = new Dictionary(); + + monoBehaviourInfo.ForEachUpdateMethod((updateMethod) => + { + //Debug.WriteLine("Found an update call! " + updateMethod); + + var results = SearchForCameraMain(context, updateMethod, searched, true); + + foreach (var oneResult in results) + { + if (!_indirectCallers.ContainsKey(oneResult)) + { + var diagnostic = Diagnostic.Create(DiagnosticDescriptors.DoNotUseCameraMainInUpdate, + oneResult.GetLocation(), oneResult, monoBehaviourInfo.ClassName, updateMethod.Identifier); + context.ReportDiagnostic(diagnostic); + } + else + { + var endPoint = _indirectCallers[oneResult]; + + var diagnostic = Diagnostic.Create(DiagnosticDescriptors.DoNotUseCameraMainInUpdateRecursive, + oneResult.GetLocation(), monoBehaviourInfo.ClassName, updateMethod.Identifier, oneResult, endPoint); + context.ReportDiagnostic(diagnostic); + } + + } + }); + } + + //TODO: Try to simplify this method - it's very hard to follow + private IEnumerable SearchForCameraMain(SyntaxNodeAnalysisContext context, + MethodDeclarationSyntax method, IDictionary searchedSymbol, bool isRoot) + { + var accessExps = method.DescendantNodes().OfType(); + foreach (var oneAccessExp in accessExps) + { + SymbolInfo oneSymbolInfo; + if (!context.TryGetSymbolInfo(oneAccessExp, out oneSymbolInfo)) + { + continue; + } + + var propertySymbol = oneSymbolInfo.Symbol as IPropertySymbol; + if (propertySymbol != null) + { + if (searchedSymbol.ContainsKey(propertySymbol)) + { + if (searchedSymbol[propertySymbol]) + { + yield return (ExpressionSyntax)oneAccessExp; + } + } + else + { + if (propertySymbol.Name == "main" && + propertySymbol.ContainingSymbol.ToString() == "UnityEngine.Camera") + { + searchedSymbol.Add(propertySymbol, true); + yield return oneAccessExp; + } + } + } + } + + + var invocationExps = method.DescendantNodes().OfType(); + foreach (var oneInvocationExp in invocationExps) + { + SymbolInfo oneSymbolInfo; + if (!context.TryGetSymbolInfo(oneInvocationExp, out oneSymbolInfo)) + { + continue; + } + + var methodSymbol = oneSymbolInfo.Symbol as IMethodSymbol; + + if (methodSymbol != null) + { + if (searchedSymbol.ContainsKey(methodSymbol)) + { + if (searchedSymbol[methodSymbol]) + { + yield return oneInvocationExp; + } + } + else + { + var methodDeclarations = methodSymbol.DeclaringSyntaxReferences; + searchedSymbol.Add(methodSymbol, false); //let's assume there won't be any calls + + foreach (var methodDeclaration in methodDeclarations) + { + var theMethodSyntax = methodDeclaration.GetSyntax() as MethodDeclarationSyntax; + + if (theMethodSyntax != null) + { + var childResults = SearchForCameraMain(context, theMethodSyntax, searchedSymbol, false); + + if (childResults != null && childResults.Any()) + { + searchedSymbol[methodSymbol] = true; //update the searched dictionary with new info + + if (isRoot) + { + _indirectCallers.Add(oneInvocationExp, childResults.First()); + } + + yield return oneInvocationExp; + break; + } + } + } + } + } + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/DoNotUseCameraMainInUpdateResources.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/DoNotUseCameraMainInUpdateResources.Designer.cs new file mode 100644 index 0000000..7ba68ec --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/DoNotUseCameraMainInUpdateResources.Designer.cs @@ -0,0 +1,100 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace UnityEngineAnalyzer.Camera { + using System; + using System.Reflection; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class DoNotUseCameraMainInUpdateResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal DoNotUseCameraMainInUpdateResources() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityEngineAnalyzer.Camera.DoNotUseCameraMainInUpdateResources", typeof(DoNotUseCameraMainInUpdateResources).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 使用此强类型资源类,为所有资源查找 + /// 重写当前线程的 CurrentUICulture 属性。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 Using Camera.main in Update methods can impact performance. Cache Camera.main on Start or Awake methods 的本地化字符串。 + /// + internal static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + /// + /// 查找类似 The method {0} is called from {1}.{2} which could cause performance problems. Cache the result from {0} in Start or Awake instead. 的本地化字符串。 + /// + internal static string MessageFormat { + get { + return ResourceManager.GetString("MessageFormat", resourceCulture); + } + } + + /// + /// 查找类似 The method {0}.{1} calls {2} which eventually calls {3} whose Camera.main could impact performance. Cache Camera.main from {3} in Start or Awake instead. 的本地化字符串。 + /// + internal static string MessageFormatRecursive { + get { + return ResourceManager.GetString("MessageFormatRecursive", resourceCulture); + } + } + + /// + /// 查找类似 Cache the result of Find or Camera.main in Start or Awake. 的本地化字符串。 + /// + internal static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/DoNotUseCameraMainInUpdateResources.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/DoNotUseCameraMainInUpdateResources.resx new file mode 100644 index 0000000..5bc9e47 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/DoNotUseCameraMainInUpdateResources.resx @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Using Camera.main in Update methods can impact performance. Cache Camera.main on Start or Awake methods + An optional longer localizable description of the diagnostic. + + + The method {0} is called from {1}.{2} which could cause performance problems. Cache the result from {0} in Start or Awake instead. + The format-able message the diagnostic displays. + + + The method {0}.{1} calls {2} which eventually calls {3} whose Camera.main could impact performance. Cache Camera.main from {3} in Start or Awake instead. + + + Cache the result of Find or Camera.main in Start or Awake. + The title of the diagnostic. + + \ No newline at end of file diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/DoNotUseFindMethodsInUpdateResources.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/DoNotUseFindMethodsInUpdateResources.Designer.cs new file mode 100644 index 0000000..1b7dc35 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/DoNotUseFindMethodsInUpdateResources.Designer.cs @@ -0,0 +1,74 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace UnityEngineAnalyzer.Camera { + using System; + using System.Reflection; + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [System.Diagnostics.DebuggerNonUserCodeAttribute()] + [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class DoNotUseFindMethodsInUpdateResources { + + private static System.Resources.ResourceManager resourceMan; + + private static System.Globalization.CultureInfo resourceCulture; + + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal DoNotUseFindMethodsInUpdateResources() { + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + internal static System.Resources.ResourceManager ResourceManager { + get { + if (object.Equals(null, resourceMan)) { + System.Resources.ResourceManager temp = new System.Resources.ResourceManager("UnityEngineAnalyzer.Camera.DoNotUseFindMethodsInUpdateResources", typeof(DoNotUseFindMethodsInUpdateResources).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + internal static System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + internal static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + internal static string MessageFormat { + get { + return ResourceManager.GetString("MessageFormat", resourceCulture); + } + } + + internal static string MessageFormatRecursive { + get { + return ResourceManager.GetString("MessageFormatRecursive", resourceCulture); + } + } + + internal static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateAnalyzer.cs new file mode 100644 index 0000000..6ea363f --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateAnalyzer.cs @@ -0,0 +1,68 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using System.Collections.Immutable; +using System.Linq; + +namespace UnityEngineAnalyzer.Delegates +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class ShouldCacheDelegateAnalyzer : DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.ShouldCacheDelegate); + + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeAssignmentNode, SyntaxKind.AddAssignmentExpression); + context.RegisterSyntaxNodeAction(AnalyzeAssignmentNode, SyntaxKind.SubtractAssignmentExpression); + context.RegisterSyntaxNodeAction(AnalyzeInvocationNode, SyntaxKind.InvocationExpression); + } + + private static void AnalyzeAssignmentNode(SyntaxNodeAnalysisContext context) + { + var checkSyntax = context.Node; + + foreach (var oneIdSyntax in checkSyntax.DescendantNodes().OfType()) + { + var oneIdSymbol = context.SemanticModel.GetSymbolInfo(oneIdSyntax); + if (oneIdSymbol.Symbol != null) + { + if (oneIdSymbol.Symbol is IMethodSymbol) + { + var diagnostic = Diagnostic.Create(DiagnosticDescriptors.ShouldCacheDelegate, + oneIdSyntax.GetLocation(), oneIdSyntax.ToString()); + context.ReportDiagnostic(diagnostic); + } + } + } + } + + private static void AnalyzeInvocationNode(SyntaxNodeAnalysisContext context) + { + var checkSyntax = context.Node as InvocationExpressionSyntax; + if(null == checkSyntax) + { + return; + } + + var argumentSyntax = checkSyntax.DescendantNodes().OfType(); + foreach (var oneArgSyntax in argumentSyntax) + { + foreach (var oneIdSyntax in oneArgSyntax.DescendantNodes().OfType()) + { + var oneIdSymbol = context.SemanticModel.GetSymbolInfo(oneIdSyntax); + if (oneIdSymbol.Symbol != null) + { + if (oneIdSymbol.Symbol is IMethodSymbol) + { + var diagnostic = Diagnostic.Create(DiagnosticDescriptors.ShouldCacheDelegate, + oneIdSyntax.GetLocation(), oneIdSyntax.ToString()); + context.ReportDiagnostic(diagnostic); + } + } + } + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateResource.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateResource.Designer.cs new file mode 100644 index 0000000..dc6d158 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateResource.Designer.cs @@ -0,0 +1,91 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace UnityEngineAnalyzer.Delegates { + using System; + using System.Reflection; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class ShouldCacheDelegateResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal ShouldCacheDelegateResource() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityEngineAnalyzer.Delegates.ShouldCacheDelegateResource", typeof(ShouldCacheDelegateResource).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 使用此强类型资源类,为所有资源查找 + /// 重写当前线程的 CurrentUICulture 属性。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 Should use variable to cache delegate for future access. 的本地化字符串。 + /// + internal static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + /// + /// 查找类似 Each time you access delegate "{0}" will cause gc allocation. You should use variable to cache delegate "{0}" for future access. 的本地化字符串。 + /// + internal static string MessageFormat { + get { + return ResourceManager.GetString("MessageFormat", resourceCulture); + } + } + + /// + /// 查找类似 Should cache delegate 的本地化字符串。 + /// + internal static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateResource.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateResource.resx new file mode 100644 index 0000000..eaf8ccc --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateResource.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Should use variable to cache delegate for future access. + An optional longer localizable description of the diagnostic. + + + Each time you access delegate "{0}" will cause gc allocation. You should use variable to cache delegate "{0}" for future access. + The format-able message the diagnostic displays. + + + Should cache delegate + The title of the diagnostic. + + \ No newline at end of file diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs index 0ac196e..f69b5d5 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs @@ -9,6 +9,12 @@ using UnityEngineAnalyzer.IL2CPP; using UnityEngineAnalyzer.OnGUI; using UnityEngineAnalyzer.StringMethods; +using UnityEngineAnalyzer.Render; +using UnityEngineAnalyzer.Camera; +using UnityEngineAnalyzer.Language; +using UnityEngineAnalyzer.GCAlloc; +using UnityEngineAnalyzer.Generics; +using UnityEngineAnalyzer.Delegates; namespace UnityEngineAnalyzer { @@ -146,6 +152,122 @@ static class DiagnosticDescriptors defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true, description: new LocalizableResourceString(nameof(DoNotUseStateNameResource.Description), DoNotUseStateNameResource.ResourceManager, typeof(DoNotUseStateNameResource)) + ); + + public static readonly DiagnosticDescriptor DoNotUseMaterialName = new DiagnosticDescriptor( + id: DiagnosticIDs.DoNotUseMaterialNameInMaterial, + title: new LocalizableResourceString(nameof(DoNotUseMaterialName.Title), DoNotUseMaterialNameResource.ResourceManager, typeof(DoNotUseMaterialNameResource)), + messageFormat: new LocalizableResourceString(nameof(DoNotUseMaterialNameResource.MessageFormat), DoNotUseMaterialNameResource.ResourceManager, typeof(DoNotUseMaterialNameResource)), + category: DiagnosticCategories.Performance, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: new LocalizableResourceString(nameof(DoNotUseMaterialNameResource.Description), DoNotUseMaterialNameResource.ResourceManager, typeof(DoNotUseMaterialNameResource)) + ); + + public static readonly DiagnosticDescriptor DoNotUseCameraMainInUpdate = new DiagnosticDescriptor( + id: DiagnosticIDs.DoNotUseCameraMainInUpdate, + title: new LocalizableResourceString(nameof(DoNotUseCameraMainInUpdate.Title), DoNotUseCameraMainInUpdateResources.ResourceManager, typeof(DoNotUseCameraMainInUpdateResources)), + messageFormat: new LocalizableResourceString(nameof(DoNotUseCameraMainInUpdateResources.MessageFormat), DoNotUseCameraMainInUpdateResources.ResourceManager, typeof(DoNotUseCameraMainInUpdateResources)), + category: DiagnosticCategories.Performance, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: new LocalizableResourceString(nameof(DoNotUseCameraMainInUpdateResources.Description), DoNotUseCameraMainInUpdateResources.ResourceManager, typeof(DoNotUseCameraMainInUpdateResources)) + ); + + + public static readonly DiagnosticDescriptor DoNotUseCameraMainInUpdateRecursive = new DiagnosticDescriptor( + id: DiagnosticIDs.DoNotUseCameraMainInUpdate, + title: new LocalizableResourceString(nameof(DoNotUseCameraMainInUpdateResources.Title), DoNotUseCameraMainInUpdateResources.ResourceManager, typeof(DoNotUseCameraMainInUpdateResources)), + messageFormat: new LocalizableResourceString(nameof(DoNotUseCameraMainInUpdateResources.MessageFormatRecursive), DoNotUseCameraMainInUpdateResources.ResourceManager, typeof(DoNotUseCameraMainInUpdateResources)), + category: DiagnosticCategories.Performance, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: new LocalizableResourceString(nameof(DoNotUseCameraMainInUpdateResources.Description), DoNotUseCameraMainInUpdateResources.ResourceManager, typeof(DoNotUseCameraMainInUpdateResources))); + + + public static readonly DiagnosticDescriptor StructShouldImplementIEquatable = new DiagnosticDescriptor( + id: DiagnosticIDs.StructShouldImplementIEquatable, + title: new LocalizableResourceString(nameof(StructAnalyzerResources.Title), StructAnalyzerResources.ResourceManager, typeof(StructAnalyzerResources)), + messageFormat: new LocalizableResourceString(nameof(StructAnalyzerResources.ShouldImplmentIEquatable), StructAnalyzerResources.ResourceManager, typeof(StructAnalyzerResources)), + category: DiagnosticCategories.Performance, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: new LocalizableResourceString(nameof(StructAnalyzerResources.Description), StructAnalyzerResources.ResourceManager, typeof(StructAnalyzerResources)) + ); + + public static readonly DiagnosticDescriptor StructShouldOverrideEquals = new DiagnosticDescriptor( + id: DiagnosticIDs.StructShouldOverrideEquals, + title: new LocalizableResourceString(nameof(StructAnalyzerResources.Title), StructAnalyzerResources.ResourceManager, typeof(StructAnalyzerResources)), + messageFormat: new LocalizableResourceString(nameof(StructAnalyzerResources.ShouldOverrideEquals), StructAnalyzerResources.ResourceManager, typeof(StructAnalyzerResources)), + category: DiagnosticCategories.Performance, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: new LocalizableResourceString(nameof(StructAnalyzerResources.Description), StructAnalyzerResources.ResourceManager, typeof(StructAnalyzerResources)) + ); + + public static readonly DiagnosticDescriptor StructShouldOverrideGetHashCode = new DiagnosticDescriptor( + id: DiagnosticIDs.StructShouldOverrideGetHashCode, + title: new LocalizableResourceString(nameof(StructAnalyzerResources.Title), StructAnalyzerResources.ResourceManager, typeof(StructAnalyzerResources)), + messageFormat: new LocalizableResourceString(nameof(StructAnalyzerResources.ShouldOverrideGetHashCode), StructAnalyzerResources.ResourceManager, typeof(StructAnalyzerResources)), + category: DiagnosticCategories.Performance, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: new LocalizableResourceString(nameof(StructAnalyzerResources.Description), StructAnalyzerResources.ResourceManager, typeof(StructAnalyzerResources)) + ); + + + public static readonly DiagnosticDescriptor DoNotGCAllocnInUpdate = new DiagnosticDescriptor( + id: DiagnosticIDs.DoNotGCAllocInUpdate, + title: new LocalizableResourceString(nameof(DoNotGCAllocInUpdateResources.Title), DoNotGCAllocInUpdateResources.ResourceManager, typeof(DoNotGCAllocInUpdateResources)), + messageFormat: new LocalizableResourceString(nameof(DoNotGCAllocInUpdateResources.MessageFormat), DoNotGCAllocInUpdateResources.ResourceManager, typeof(DoNotGCAllocInUpdateResources)), + category: DiagnosticCategories.Performance, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: new LocalizableResourceString(nameof(DoNotGCAllocInUpdateResources.Description), DoNotGCAllocInUpdateResources.ResourceManager, typeof(DoNotGCAllocInUpdateResources)) + ); + + + public static readonly DiagnosticDescriptor DoNotGCAllocnInUpdateRecursive = new DiagnosticDescriptor( + id: DiagnosticIDs.DoNotGCAllocInUpdate, + title: new LocalizableResourceString(nameof(DoNotGCAllocInUpdateResources.Title), DoNotGCAllocInUpdateResources.ResourceManager, typeof(DoNotGCAllocInUpdateResources)), + messageFormat: new LocalizableResourceString(nameof(DoNotGCAllocInUpdateResources.MessageFormatRecursive), DoNotGCAllocInUpdateResources.ResourceManager, typeof(DoNotGCAllocInUpdateResources)), + category: DiagnosticCategories.Performance, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: new LocalizableResourceString(nameof(DoNotGCAllocInUpdateResources.Description), DoNotGCAllocInUpdateResources.ResourceManager, typeof(DoNotGCAllocInUpdateResources))); + + + + public static readonly DiagnosticDescriptor DoNotBoxWhenInvoke = new DiagnosticDescriptor( + id: DiagnosticIDs.DoNotBoxWhenInvoke, + title: new LocalizableResourceString(nameof(DoNotBoxWhenInvokeResource.Title), DoNotBoxWhenInvokeResource.ResourceManager, typeof(DoNotBoxWhenInvokeResource)), + messageFormat: new LocalizableResourceString(nameof(DoNotBoxWhenInvokeResource.MessageFormat), DoNotBoxWhenInvokeResource.ResourceManager, typeof(DoNotBoxWhenInvokeResource)), + category: DiagnosticCategories.Performance, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: new LocalizableResourceString(nameof(DoNotBoxWhenInvokeResource.Description), DoNotBoxWhenInvokeResource.ResourceManager, typeof(DoNotBoxWhenInvokeResource)) + ); + + + public static readonly DiagnosticDescriptor DoNotUseEnumTypeParameter = new DiagnosticDescriptor( + id: DiagnosticIDs.DoNoUseEnumTypeParameter, + title: new LocalizableResourceString(nameof(DoNotUseEnumTypeParameterResource.Title), DoNotUseEnumTypeParameterResource.ResourceManager, typeof(DoNotUseEnumTypeParameterResource)), + messageFormat: new LocalizableResourceString(nameof(DoNotUseEnumTypeParameterResource.MessageFormat), DoNotUseEnumTypeParameterResource.ResourceManager, typeof(DoNotUseEnumTypeParameterResource)), + category: DiagnosticCategories.Performance, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: new LocalizableResourceString(nameof(DoNotUseEnumTypeParameterResource.Description), DoNotUseEnumTypeParameterResource.ResourceManager, typeof(DoNotUseEnumTypeParameterResource)) + ); + + + public static readonly DiagnosticDescriptor ShouldCacheDelegate = new DiagnosticDescriptor( + id: DiagnosticIDs.ShouldCacheDelegate, + title: new LocalizableResourceString(nameof(ShouldCacheDelegateResource.Title), ShouldCacheDelegateResource.ResourceManager, typeof(ShouldCacheDelegateResource)), + messageFormat: new LocalizableResourceString(nameof(ShouldCacheDelegateResource.MessageFormat), ShouldCacheDelegateResource.ResourceManager, typeof(ShouldCacheDelegateResource)), + category: DiagnosticCategories.Performance, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: new LocalizableResourceString(nameof(ShouldCacheDelegateResource.Description), ShouldCacheDelegateResource.ResourceManager, typeof(ShouldCacheDelegateResource)) ); } } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs index 5d99f94..a607bc5 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs @@ -13,12 +13,21 @@ public static class DiagnosticIDs public const string UnsealedDerivedClass = "UEA0008"; public const string InvokeFunctionMissing = "UEA0009"; public const string DoNotUseStateNameInAnimator = "UEA0010"; + public const string DoNotUseMaterialNameInMaterial = "UEA0011"; + public const string DoNotUseCameraMainInUpdate = "UEA0012"; + public const string DoNotGCAllocInUpdate = "UEA0013"; //NOTES: These should probably be on their own analyzer - as they are not specific to Unity public const string DoNotUseRemoting = "AOT0001"; public const string DoNotUseReflectionEmit = "AOT0002"; public const string TypeGetType = "AOT0003"; + public const string StructShouldImplementIEquatable = "AOT0004"; + public const string StructShouldOverrideEquals = "AOT0005"; + public const string StructShouldOverrideGetHashCode = "AOT0006"; + public const string DoNotBoxWhenInvoke = "AOT0007"; + public const string DoNoUseEnumTypeParameter = "AOT0008"; + public const string ShouldCacheDelegate = "AOT0009"; + - } } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/FindMethodsInUpdate/DoNotUseFindMethodsInUpdateResources.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/FindMethodsInUpdate/DoNotUseFindMethodsInUpdateResources.Designer.cs index 7f9db7c..e18cf8c 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/FindMethodsInUpdate/DoNotUseFindMethodsInUpdateResources.Designer.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/FindMethodsInUpdate/DoNotUseFindMethodsInUpdateResources.Designer.cs @@ -1,10 +1,10 @@ //------------------------------------------------------------------------------ // -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 // -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 // //------------------------------------------------------------------------------ @@ -14,12 +14,12 @@ namespace UnityEngineAnalyzer.FindMethodsInUpdate { /// - /// A strongly-typed resource class, for looking up localized strings, etc. + /// 一个强类型的资源类,用于查找本地化的字符串等。 /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] @@ -34,7 +34,7 @@ internal DoNotUseFindMethodsInUpdateResources() { } /// - /// Returns the cached ResourceManager instance used by this class. + /// 返回此类使用的缓存的 ResourceManager 实例。 /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { @@ -48,8 +48,8 @@ internal DoNotUseFindMethodsInUpdateResources() { } /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. + /// 使用此强类型资源类,为所有资源查找 + /// 重写当前线程的 CurrentUICulture 属性。 /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { @@ -62,7 +62,7 @@ internal DoNotUseFindMethodsInUpdateResources() { } /// - /// Looks up a localized string similar to Using Find or GetComponent in Update methods can impact performance. Cache the result on Start or Awake methods. + /// 查找类似 Using Find or GetComponent in Update methods can impact performance. Cache the result on Start or Awake methods 的本地化字符串。 /// internal static string Description { get { @@ -71,7 +71,7 @@ internal static string Description { } /// - /// Looks up a localized string similar to The method {0} is called from {1}.{2} which could cause performance problems. Cache the result from {0} in Start or Awake instead.. + /// 查找类似 The method {0} is called from {1}.{2} which could cause performance problems. Cache the result from {0} in Start or Awake instead. 的本地化字符串。 /// internal static string MessageFormat { get { @@ -80,7 +80,7 @@ internal static string MessageFormat { } /// - /// Looks up a localized string similar to The method {0}.{1} calls {2} which eventually calls {3} which could impact performance. Cache the result from {3} in Start or Awake instead.. + /// 查找类似 The method {0}.{1} calls {2} which eventually calls {3} which could impact performance. Cache the result from {3}'s {2} in Start or Awake instead. 的本地化字符串。 /// internal static string MessageFormatRecursive { get { @@ -89,7 +89,7 @@ internal static string MessageFormatRecursive { } /// - /// Looks up a localized string similar to Cache the result of Find or GetComponent in Start or Awake.. + /// 查找类似 Cache the result of Find or GetComponent in Start or Awake. 的本地化字符串。 /// internal static string Title { get { diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/FindMethodsInUpdate/DoNotUseFindMethodsInUpdateResources.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/FindMethodsInUpdate/DoNotUseFindMethodsInUpdateResources.resx index f263459..980a5ed 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/FindMethodsInUpdate/DoNotUseFindMethodsInUpdateResources.resx +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/FindMethodsInUpdate/DoNotUseFindMethodsInUpdateResources.resx @@ -126,7 +126,7 @@ The format-able message the diagnostic displays. - The method {0}.{1} calls {2} which eventually calls {3} which could impact performance. Cache the result from {3} in Start or Awake instead. + The method {0}.{1} calls {2} which eventually calls {3} which could impact performance. Cache the result from {3}'s {2} in Start or Awake instead. Cache the result of Find or GetComponent in Start or Awake. diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/ForEachInUpdate/DoNotUseForEachInUpdate.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/ForEachInUpdate/DoNotUseForEachInUpdate.cs index 06c9321..7fe1a3d 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/ForEachInUpdate/DoNotUseForEachInUpdate.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/ForEachInUpdate/DoNotUseForEachInUpdate.cs @@ -9,6 +9,9 @@ namespace UnityEngineAnalyzer.ForEachInUpdate { + /* + * In unity 5.5.0 or newer, foreach is not neccessary to avoid. + * [DiagnosticAnalyzer(LanguageNames.CSharp)] public sealed class DoNotUseForEachInUpdate : DiagnosticAnalyzer { @@ -23,6 +26,8 @@ public static void AnalyzeClassSyntax(SyntaxNodeAnalysisContext context) { var monoBehaviourInfo = new MonoBehaviourInfo(context); + + var searched = new Dictionary(); monoBehaviourInfo.ForEachUpdateMethod((updateMethod) => { @@ -52,4 +57,5 @@ private static IEnumerable SearchForForEach(SyntaxNodeAn //TODO: Keep Searching recurively to other methods... } } + */ } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNoGCAllocInUpdateAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNoGCAllocInUpdateAnalyzer.cs new file mode 100644 index 0000000..0c33c87 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNoGCAllocInUpdateAnalyzer.cs @@ -0,0 +1,204 @@ +using System; +using System.Collections.Generic; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; + +namespace UnityEngineAnalyzer.GCAlloc +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + /// DoNotGCAllocInUpdateAnalyzer currently is conservative, + /// which means, if the ObjectCreationExpressionSyntax is inside a branch (if/switch), + /// then this analyzer will NOT report diagnose. + public sealed class DoNotGCAllocInUpdateAnalyzer : DiagnosticAnalyzer + { + + private Dictionary _indirectCallers; + + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create( + DiagnosticDescriptors.DoNotGCAllocnInUpdate, + DiagnosticDescriptors.DoNotGCAllocnInUpdateRecursive); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeClassSyntax, SyntaxKind.ClassDeclaration); + } + + public void AnalyzeClassSyntax(SyntaxNodeAnalysisContext context) + { + var monoBehaviourInfo = new MonoBehaviourInfo(context); + + var searched = new Dictionary(); + _indirectCallers = new Dictionary(); + + monoBehaviourInfo.ForEachUpdateMethod((updateMethod) => + { + //Debug.WriteLine("Found an update call! " + updateMethod); + + var results = SearchForTargetExpression(context, updateMethod, searched, true); + + foreach (var oneResult in results) + { + if (!_indirectCallers.ContainsKey(oneResult)) + { + var diagnostic = Diagnostic.Create(DiagnosticDescriptors.DoNotGCAllocnInUpdate, + oneResult.GetLocation(), oneResult, monoBehaviourInfo.ClassName, updateMethod.Identifier); + context.ReportDiagnostic(diagnostic); + } + else + { + var endPoint = _indirectCallers[oneResult]; + + var diagnostic = Diagnostic.Create(DiagnosticDescriptors.DoNotGCAllocnInUpdateRecursive, + oneResult.GetLocation(), monoBehaviourInfo.ClassName, updateMethod.Identifier, oneResult, endPoint); + context.ReportDiagnostic(diagnostic); + } + + } + }); + } + + //TODO: Try to simplify this method - it's very hard to follow + private IEnumerable SearchForTargetExpression(SyntaxNodeAnalysisContext context, + MethodDeclarationSyntax method, IDictionary searchedSymbol, bool isRoot) + { + var targetExps = method.DescendantNodes().OfType(); + foreach (var oneTargetExp in targetExps) + { + /* + var oneTargetIdentifierExps = oneTargetExp.DescendantNodes().OfType(); + foreach(var oneTargetIdentifierExp in oneTargetIdentifierExps) + { + } + */ + + bool isContainedByBranch = false; + + SyntaxNode parent = oneTargetExp; + while (parent != method) + { + if (parent is IfStatementSyntax || parent is SwitchStatementSyntax) + { + isContainedByBranch = true; + break; + } + + parent = parent.Parent; + } + + if (isContainedByBranch) + { + continue; + } + + + SymbolInfo oneSymbolInfo; + if (!context.TryGetSymbolInfo(oneTargetExp, out oneSymbolInfo)) + { + continue; + } + + var targetSymbol = oneSymbolInfo.Symbol as IMethodSymbol; + if (targetSymbol != null) + { + if (searchedSymbol.ContainsKey(targetSymbol)) + { + if (searchedSymbol[targetSymbol]) + { + yield return (ExpressionSyntax)oneTargetExp; + } + } + else + { + if (targetSymbol.ReceiverType.IsReferenceType) + { + searchedSymbol.Add(targetSymbol, true); + yield return oneTargetExp; + } + } + } + } + + + var invocationExps = method.DescendantNodes().OfType(); + foreach (var oneInvocationExp in invocationExps) + { + SymbolInfo oneSymbolInfo; + if (!context.TryGetSymbolInfo(oneInvocationExp, out oneSymbolInfo)) + { + continue; + } + + + bool isContainedByBranch = false; + SyntaxNode parent = oneInvocationExp; + while (parent != method) + { + if (parent is IfStatementSyntax || parent is SwitchStatementSyntax) + { + isContainedByBranch = true; + break; + } + + parent = parent.Parent; + } + if (isContainedByBranch) + { + continue; + } + + var methodSymbol = oneSymbolInfo.Symbol as IMethodSymbol; + + if (methodSymbol != null) + { + if (searchedSymbol.ContainsKey(methodSymbol)) + { + if (searchedSymbol[methodSymbol]) + { + yield return oneInvocationExp; + } + } + else + { + var methodDeclarations = methodSymbol.DeclaringSyntaxReferences; + searchedSymbol.Add(methodSymbol, false); //let's assume there won't be any calls + + foreach (var methodDeclaration in methodDeclarations) + { + var theMethodSyntax = methodDeclaration.GetSyntax() as MethodDeclarationSyntax; + + if (theMethodSyntax != null) + { + var childResults = SearchForTargetExpression(context, theMethodSyntax, searchedSymbol, false); + + if (childResults != null && childResults.Any()) + { + searchedSymbol[methodSymbol] = true; //update the searched dictionary with new info + + if (isRoot) + { + _indirectCallers.Add(oneInvocationExp, childResults.First()); + } + + yield return oneInvocationExp; + break; + } + } + } + } + } + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeAnalyzer.cs new file mode 100644 index 0000000..6c4629c --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeAnalyzer.cs @@ -0,0 +1,70 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using System.Collections.Immutable; + +namespace UnityEngineAnalyzer.GCAlloc +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class DoNotBoxWhenInvokeAnalyzer : DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.DoNotBoxWhenInvoke); + + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.InvocationExpression); + } + + private static void AnalyzeNode(SyntaxNodeAnalysisContext context) + { + var invocation = context.Node as InvocationExpressionSyntax; + if (invocation == null) + { + return; + } + + if (invocation.ArgumentList == null || invocation.ArgumentList.Arguments == null || invocation.ArgumentList.Arguments.Count == 0) + { + return; + } + + var symbolInfo = context.SemanticModel.GetSymbolInfo(invocation); + var methodSymbol = symbolInfo.Symbol as IMethodSymbol; + + if(methodSymbol.Parameters == null || methodSymbol.Parameters.Length == 0) + { + return; + } + + for(int i = 0; i < methodSymbol.Parameters.Length && i < invocation.ArgumentList.Arguments.Count; ++i) + { + var oneArgSyntax = invocation.ArgumentList.Arguments[i]; + if(oneArgSyntax == null) + { + continue; + } + var oneArgType = context.SemanticModel.GetTypeInfo(oneArgSyntax.Expression); + if(oneArgType.Type == null) + { + continue; + } + + var oneParamType = methodSymbol.Parameters[i]; + + if(oneParamType.Type == null) + { + return; + } + + if(oneArgType.Type.IsValueType && oneParamType.Type.IsReferenceType) + { + var diagnostic = Diagnostic.Create(DiagnosticDescriptors.DoNotBoxWhenInvoke, oneArgSyntax.Expression.GetLocation(), + methodSymbol.ContainingType.Name, methodSymbol.Name, oneParamType.Name, oneParamType.Type.ToString(), + oneArgSyntax.Expression.ToString(), oneArgType.Type.ToString()); + context.ReportDiagnostic(diagnostic); + } + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeResource.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeResource.Designer.cs new file mode 100644 index 0000000..973c008 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeResource.Designer.cs @@ -0,0 +1,91 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace UnityEngineAnalyzer.GCAlloc { + using System; + using System.Reflection; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class DoNotBoxWhenInvokeResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal DoNotBoxWhenInvokeResource() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityEngineAnalyzer.GCAlloc.DoNotBoxWhenInvokeResource", typeof(DoNotBoxWhenInvokeResource).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 使用此强类型资源类,为所有资源查找 + /// 重写当前线程的 CurrentUICulture 属性。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 Avoid boxing when invoke 的本地化字符串。 + /// + internal static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + /// + /// 查找类似 Method {0}.{1}'s parameter "{2}"'s type {3} is reference type, but the argument "{4}"'s type {5} is value type, which makes boxing and gc allocation. 的本地化字符串。 + /// + internal static string MessageFormat { + get { + return ResourceManager.GetString("MessageFormat", resourceCulture); + } + } + + /// + /// 查找类似 Avoid boxing when invoke 的本地化字符串。 + /// + internal static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeResource.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeResource.resx new file mode 100644 index 0000000..8e39528 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeResource.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Avoid boxing when invoke + An optional longer localizable description of the diagnostic. + + + Method {0}.{1}'s parameter "{2}"'s type {3} is reference type, but the argument "{4}"'s type {5} is value type, which makes boxing and gc allocation. + The format-able message the diagnostic displays. + + + Avoid boxing when invoke + The title of the diagnostic. + + \ No newline at end of file diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotGCAllocInUpdateResources.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotGCAllocInUpdateResources.Designer.cs new file mode 100644 index 0000000..531a9c4 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotGCAllocInUpdateResources.Designer.cs @@ -0,0 +1,100 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace UnityEngineAnalyzer.GCAlloc { + using System; + using System.Reflection; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class DoNotGCAllocInUpdateResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal DoNotGCAllocInUpdateResources() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityEngineAnalyzer.GCAlloc.DoNotGCAllocInUpdateResources", typeof(DoNotGCAllocInUpdateResources).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 使用此强类型资源类,为所有资源查找 + /// 重写当前线程的 CurrentUICulture 属性。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 GC allocation in Update methods can impact performance. Use pool or data-member to cache your data. 的本地化字符串。 + /// + internal static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + /// + /// 查找类似 {0} makes GC allocation from {1}.{2} which could cause performance problems. Use pool or data-member to cache your data. 的本地化字符串。 + /// + internal static string MessageFormat { + get { + return ResourceManager.GetString("MessageFormat", resourceCulture); + } + } + + /// + /// 查找类似 The method {0}.{1} calls {2} which eventually calls {3} whose GC allocation could impact performance. Use pool or data-member to cache your data from {3} 的本地化字符串。 + /// + internal static string MessageFormatRecursive { + get { + return ResourceManager.GetString("MessageFormatRecursive", resourceCulture); + } + } + + /// + /// 查找类似 Avoid GC allocation in Update methods 的本地化字符串。 + /// + internal static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotGCAllocInUpdateResources.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotGCAllocInUpdateResources.resx new file mode 100644 index 0000000..a12743f --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotGCAllocInUpdateResources.resx @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + GC allocation in Update methods can impact performance. Use pool or data-member to cache your data. + An optional longer localizable description of the diagnostic. + + + {0} makes GC allocation from {1}.{2} which could cause performance problems. Use pool or data-member to cache your data. + The format-able message the diagnostic displays. + + + The method {0}.{1} calls {2} which eventually calls {3} whose GC allocation could impact performance. Use pool or data-member to cache your data from {3} + + + Avoid GC allocation in Update methods + The title of the diagnostic. + + \ No newline at end of file diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/DoNotUseEnumTypeParameterAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/DoNotUseEnumTypeParameterAnalyzer.cs new file mode 100644 index 0000000..35e66ef --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/DoNotUseEnumTypeParameterAnalyzer.cs @@ -0,0 +1,54 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using System.Collections.Immutable; +using System.Linq; + +namespace UnityEngineAnalyzer.Generics +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class DoNotUseEnumTypeParameterAnalyzer : DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.DoNotUseEnumTypeParameter); + + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.VariableDeclaration); + } + + private static void AnalyzeNode(SyntaxNodeAnalysisContext context) + { + var targetSyntax = context.Node as VariableDeclarationSyntax; + if (targetSyntax == null) + { + return; + } + + System.Collections.Generic.HashSet checkedIdSyntax = new System.Collections.Generic.HashSet(); + + var typeArgumentLists = targetSyntax.DescendantNodes().OfType(); + foreach(var oneTypeArgSyntax in typeArgumentLists) + { + foreach(var oneIdSyntax in oneTypeArgSyntax.DescendantNodes().OfType()) + { + if(!checkedIdSyntax.Contains(oneIdSyntax)) + { + checkedIdSyntax.Add(oneIdSyntax); + + var oneIdSymbolInfo = context.SemanticModel.GetSymbolInfo(oneIdSyntax); + var oneIdSymbol = oneIdSymbolInfo.Symbol as INamedTypeSymbol; + if (oneIdSymbol.BaseType.Name == "Enum" && + oneIdSymbol.BaseType.ContainingNamespace.Name == "System") + { + var diagnostic = Diagnostic.Create(DiagnosticDescriptors.DoNotUseEnumTypeParameter, + oneIdSyntax.GetLocation(), oneIdSyntax.ToString()); + context.ReportDiagnostic(diagnostic); + } + } + } + } + + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/DoNotUseEnumTypeParameterResource.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/DoNotUseEnumTypeParameterResource.Designer.cs new file mode 100644 index 0000000..0b1b275 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/DoNotUseEnumTypeParameterResource.Designer.cs @@ -0,0 +1,91 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace UnityEngineAnalyzer.Generics { + using System; + using System.Reflection; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class DoNotUseEnumTypeParameterResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal DoNotUseEnumTypeParameterResource() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityEngineAnalyzer.Generics.DoNotUseEnumTypeParameterResource", typeof(DoNotUseEnumTypeParameterResource).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 使用此强类型资源类,为所有资源查找 + /// 重写当前线程的 CurrentUICulture 属性。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 Prefer to use integer type (short or int) as TypeParameter, and convert it between Enum type. This can reduce code size, and you don't need to pass additional IEqualityComparer for your Enum for your container. 的本地化字符串。 + /// + internal static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + /// + /// 查找类似 Change {0} to integer type (short or int) as TypeParameter 的本地化字符串。 + /// + internal static string MessageFormat { + get { + return ResourceManager.GetString("MessageFormat", resourceCulture); + } + } + + /// + /// 查找类似 Use integer type instead of Enum as TypeParameter 的本地化字符串。 + /// + internal static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/DoNotUseEnumTypeParameterResource.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/DoNotUseEnumTypeParameterResource.resx new file mode 100644 index 0000000..869e6a7 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/DoNotUseEnumTypeParameterResource.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Prefer to use integer type (short or int) as TypeParameter, and convert it between Enum type. This can reduce code size, and you don't need to pass additional IEqualityComparer for your Enum for your container. + An optional longer localizable description of the diagnostic. + + + Change {0} to integer type (short or int) as TypeParameter + The format-able message the diagnostic displays. + + + Use integer type instead of Enum as TypeParameter + The title of the diagnostic. + + \ No newline at end of file diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructAnalyzerResources.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructAnalyzerResources.Designer.cs new file mode 100644 index 0000000..9d728bb --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructAnalyzerResources.Designer.cs @@ -0,0 +1,109 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace UnityEngineAnalyzer.Language { + using System; + using System.Reflection; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class StructAnalyzerResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal StructAnalyzerResources() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityEngineAnalyzer.Language.StructAnalyzerResources", typeof(StructAnalyzerResources).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 使用此强类型资源类,为所有资源查找 + /// 重写当前线程的 CurrentUICulture 属性。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 Inappropriate Struct implementation can impact performance 的本地化字符串。 + /// + internal static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + /// + /// 查找类似 Struct {0} should implement IEquatable to avoid boxing in case Equals(object o) is called. 的本地化字符串。 + /// + internal static string ShouldImplmentIEquatable { + get { + return ResourceManager.GetString("ShouldImplmentIEquatable", resourceCulture); + } + } + + /// + /// 查找类似 Struct {0} should override Equals(object o) because its default implementation may use reflection and is not efficient 的本地化字符串。 + /// + internal static string ShouldOverrideEquals { + get { + return ResourceManager.GetString("ShouldOverrideEquals", resourceCulture); + } + } + + /// + /// 查找类似 Struct {0} should override GetHashCode() because its default implementation may use reflection and is not efficient 的本地化字符串。 + /// + internal static string ShouldOverrideGetHashCode { + get { + return ResourceManager.GetString("ShouldOverrideGetHashCode", resourceCulture); + } + } + + /// + /// 查找类似 Inappropriate Struct implementation 的本地化字符串。 + /// + internal static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructAnalyzerResources.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructAnalyzerResources.resx new file mode 100644 index 0000000..4dace20 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructAnalyzerResources.resx @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Inappropriate Struct implementation can impact performance + An optional longer localizable description of the diagnostic. + + + Struct {0} should implement IEquatable to avoid boxing in case Equals(object o) is called. + The format-able message the diagnostic displays. + + + Struct {0} should override Equals(object o) because its default implementation may use reflection and is not efficient + + + Struct {0} should override GetHashCode() because its default implementation may use reflection and is not efficient + + + Inappropriate Struct implementation + The title of the diagnostic. + + \ No newline at end of file diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructImplementationAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructImplementationAnalyzer.cs new file mode 100644 index 0000000..32e5a04 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructImplementationAnalyzer.cs @@ -0,0 +1,79 @@ +using System.Collections.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace UnityEngineAnalyzer.Language +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class StructImplementAnalyzer : DiagnosticAnalyzer + { + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeClassSyntax, SyntaxKind.StructDeclaration); + } + + private void AnalyzeClassSyntax(SyntaxNodeAnalysisContext context) + { + CheckStructShouldImplementIEquatable(context); + } + + + private void CheckStructShouldImplementIEquatable(SyntaxNodeAnalysisContext context) + { + var declSyntax = (StructDeclarationSyntax)context.Node; + var declSymbol = context.SemanticModel.GetDeclaredSymbol(declSyntax) as INamedTypeSymbol; + + bool foundIEquatableOfStruct = false; + + if (declSyntax.BaseList != null) + { + + var baseList = declSyntax.BaseList.Types; + var baseTypesSyntax = baseList.OfType(); + + + foreach (var oneBaseTypeSyntax in baseTypesSyntax) + { + var baseTypeName = oneBaseTypeSyntax.ChildNodes(); + foreach (var oneName in baseTypeName) + { + if (oneName is GenericNameSyntax) + { + var genericSymbolInfo = context.SemanticModel.GetSymbolInfo(oneName); + var genericSymbol = genericSymbolInfo.Symbol as INamedTypeSymbol; + if (genericSymbol != null && genericSymbol.ContainingNamespace.Name == "System" && + genericSymbol.Name == "IEquatable") + { + foreach (var oneParameterType in genericSymbol.TypeArguments) + { + if (oneParameterType == declSymbol) + { + foundIEquatableOfStruct = true; + } + } + } + } + } + } + } + + if (!foundIEquatableOfStruct) + { + + var diagnostic = Diagnostic.Create(SupportedDiagnostics.First(), declSyntax.Identifier.GetLocation(), + declSyntax.Identifier.ToString()); + + context.ReportDiagnostic(diagnostic); + } + } + + + public override ImmutableArray SupportedDiagnostics => + ImmutableArray.Create( + DiagnosticDescriptors.StructShouldImplementIEquatable + ); + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructOverrideEqualsObjectAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructOverrideEqualsObjectAnalyzer.cs new file mode 100644 index 0000000..c164e45 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructOverrideEqualsObjectAnalyzer.cs @@ -0,0 +1,68 @@ +using System.Collections.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace UnityEngineAnalyzer.Language +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class StructOverrideEqualsObjectAnalyzer : DiagnosticAnalyzer + { + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeClassSyntax, SyntaxKind.StructDeclaration); + } + + private void AnalyzeClassSyntax(SyntaxNodeAnalysisContext context) + { + CheckStructShouldOverride(context); + } + + + private void CheckStructShouldOverride(SyntaxNodeAnalysisContext context) + { + var declSyntax = (StructDeclarationSyntax)context.Node; + + bool foundOverrideEquals = false; + + foreach (var oneChild in declSyntax.ChildNodes()) + { + if(oneChild is MethodDeclarationSyntax) + { + var oneMethodSymbol = context.SemanticModel.GetDeclaredSymbol(oneChild) as IMethodSymbol; + if(oneMethodSymbol.IsOverride) + { + if(oneMethodSymbol.Parameters != null && oneMethodSymbol.Parameters.Length == 1 && + oneMethodSymbol.Name == "Equals") + { + var oneParamType = oneMethodSymbol.Parameters[0].Type; + if (oneParamType.ContainingNamespace.Name == "System" && oneParamType.Name.ToLower() == "object") + { + foundOverrideEquals = true; + break; + } + } + + } + } + } + + + if(!foundOverrideEquals) + { + var diagnostic = Diagnostic.Create(SupportedDiagnostics[0], declSyntax.Identifier.GetLocation(), + declSyntax.Identifier.ToString()); + + context.ReportDiagnostic(diagnostic); + } + } + + + public override ImmutableArray SupportedDiagnostics => + ImmutableArray.Create( + DiagnosticDescriptors.StructShouldOverrideEquals + ); + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructOverrideGetHashCodeAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructOverrideGetHashCodeAnalyzer.cs new file mode 100644 index 0000000..ad7e4df --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructOverrideGetHashCodeAnalyzer.cs @@ -0,0 +1,63 @@ +using System.Collections.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace UnityEngineAnalyzer.Language +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class StructOverrideGetHashCodeAnalyzer : DiagnosticAnalyzer + { + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeClassSyntax, SyntaxKind.StructDeclaration); + } + + private void AnalyzeClassSyntax(SyntaxNodeAnalysisContext context) + { + CheckStructShouldOverride(context); + } + + + private void CheckStructShouldOverride(SyntaxNodeAnalysisContext context) + { + var declSyntax = (StructDeclarationSyntax)context.Node; + + bool foundOverridGetHashCode = false; + + foreach (var oneChild in declSyntax.ChildNodes()) + { + if(oneChild is MethodDeclarationSyntax) + { + var oneMethodSymbol = context.SemanticModel.GetDeclaredSymbol(oneChild) as IMethodSymbol; + if(oneMethodSymbol.IsOverride) + { + if ((oneMethodSymbol.Parameters == null || oneMethodSymbol.Parameters.Length == 0) && + oneMethodSymbol.Name == "GetHashCode") + { + foundOverridGetHashCode = true; + break; + } + } + } + } + + + if (!foundOverridGetHashCode) + { + var diagnostic = Diagnostic.Create(SupportedDiagnostics[0], declSyntax.Identifier.GetLocation(), + declSyntax.Identifier.ToString()); + + context.ReportDiagnostic(diagnostic); + } + } + + + public override ImmutableArray SupportedDiagnostics => + ImmutableArray.Create( + DiagnosticDescriptors.StructShouldOverrideGetHashCode + ); + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseMaterialNameAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseMaterialNameAnalyzer.cs new file mode 100644 index 0000000..18c8cc7 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseMaterialNameAnalyzer.cs @@ -0,0 +1,74 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using System.Collections.Immutable; + +namespace UnityEngineAnalyzer.Render +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class DoNotUseMaterialNameAnalyzer : DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.DoNotUseMaterialName); + + private static readonly ImmutableHashSet checkMethods = ImmutableHashSet.Create( + "GetColor", + "GetColorArray", + "GetFloat", + "GetFloatArray", + "GetInt", + "GetMatrix", + "GetMatrixArray", + "GetTexture", + "GetVector", + "HasProperty", + "SetBuffer", + "SetColor", + "SetColorArray", + "SetFloat", + "SetFloatArray", + "SetInt", + "SetMatrix", + "SetMatrixArray", + "SetTexture", + "SetTextureOffset", + "SetTextureScale", + "SetVector", + "SetVectorArray" + ); + + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.InvocationExpression); + } + + private static void AnalyzeNode(SyntaxNodeAnalysisContext context) + { + var invocation = context.Node as InvocationExpressionSyntax; + if (invocation == null) + { + return; + } + + var name = invocation.MethodName(); + + // check if any of the methods are used + if (!checkMethods.Contains(name)) { return; } + + var symbolInfo = context.SemanticModel.GetSymbolInfo(invocation); + var methodSymbol = symbolInfo.Symbol as IMethodSymbol; + + var containingClass = methodSymbol.ContainingType; + + // check if the method is the one from UnityEngine.Animator + if (containingClass.ContainingNamespace.Name.Equals("UnityEngine") && containingClass.Name.Equals("Material")) + { + if (methodSymbol.Parameters[0].Type.MetadataName == "String") + { + var diagnostic = Diagnostic.Create(DiagnosticDescriptors.DoNotUseMaterialName, invocation.GetLocation(), containingClass.Name, methodSymbol.Name); + context.ReportDiagnostic(diagnostic); + } + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseMaterialNameResource.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseMaterialNameResource.Designer.cs new file mode 100644 index 0000000..0ea5c80 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseMaterialNameResource.Designer.cs @@ -0,0 +1,68 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace UnityEngineAnalyzer.Render { + using System; + using System.Reflection; + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [System.Diagnostics.DebuggerNonUserCodeAttribute()] + [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class DoNotUseMaterialNameResource { + + private static System.Resources.ResourceManager resourceMan; + + private static System.Globalization.CultureInfo resourceCulture; + + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal DoNotUseMaterialNameResource() { + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + internal static System.Resources.ResourceManager ResourceManager { + get { + if (object.Equals(null, resourceMan)) { + System.Resources.ResourceManager temp = new System.Resources.ResourceManager("UnityEngineAnalyzer.Render.DoNotUseMaterialNameResource", typeof(DoNotUseMaterialNameResource).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + internal static System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + internal static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + internal static string MessageFormat { + get { + return ResourceManager.GetString("MessageFormat", resourceCulture); + } + } + + internal static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseMaterialNameResource.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseMaterialNameResource.resx new file mode 100644 index 0000000..35529f9 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseMaterialNameResource.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Shader.PropertyToID can convert your name to hash, it's faster than string comparison + An optional longer localizable description of the diagnostic. + + + Use nameID instead of name + The format-able message the diagnostic displays. + + + Use nameID instead of name + The title of the diagnostic. + + \ No newline at end of file diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseStateNameResource.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseStateNameResource.Designer.cs new file mode 100644 index 0000000..dcd873b --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseStateNameResource.Designer.cs @@ -0,0 +1,68 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace UnityEngineAnalyzer.Shader { + using System; + using System.Reflection; + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [System.Diagnostics.DebuggerNonUserCodeAttribute()] + [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class DoNotUseMaterialNameResource { + + private static System.Resources.ResourceManager resourceMan; + + private static System.Globalization.CultureInfo resourceCulture; + + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal DoNotUseMaterialNameResource() { + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + internal static System.Resources.ResourceManager ResourceManager { + get { + if (object.Equals(null, resourceMan)) { + System.Resources.ResourceManager temp = new System.Resources.ResourceManager("UnityEngineAnalyzer.Shader.DoNotUseMaterialNameResource", typeof(DoNotUseMaterialNameResource).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + internal static System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + internal static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + internal static string MessageFormat { + get { + return ResourceManager.GetString("MessageFormat", resourceCulture); + } + } + + internal static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj b/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj index bd0fcda..10da3ca 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj @@ -68,6 +68,12 @@ True DoNotUseCoroutinesResources.resx + + + True + True + ShouldCacheDelegateResource.resx + @@ -89,12 +95,38 @@ DoNotUseForEachInUpdateResources.resx + + + True + True + DoNotGCAllocInUpdateResources.resx + + + + True + True + DoNotBoxWhenInvokeResource.resx + + + + True + True + DoNotUseEnumTypeParameterResource.resx + True True UnsealedDerivedClassResources.resx + + + + + True + True + StructAnalyzerResources.resx + @@ -116,6 +148,18 @@ True InvokeFunctionMissingResources.resx + + + True + True + DoNotUseMaterialNameResource.resx + + + + True + True + DoNotUseCameraMainInUpdateResources.resx + @@ -138,6 +182,10 @@ ResXFileCodeGenerator UseCompareTagResources.Designer.cs + + ResXFileCodeGenerator + ShouldCacheDelegateResource.Designer.cs + ResXFileCodeGenerator EmptyMonoBehaviourMethodsResources.Designer.cs @@ -154,10 +202,26 @@ ResXFileCodeGenerator DoNotUseCoroutinesResources.Designer.cs + + ResXFileCodeGenerator + DoNotGCAllocInUpdateResources.Designer.cs + + + ResXFileCodeGenerator + DoNotBoxWhenInvokeResource.Designer.cs + + + ResXFileCodeGenerator + DoNotUseEnumTypeParameterResource.Designer.cs + ResXFileCodeGenerator UnsealedDerivedClassResources.Designer.cs + + ResXFileCodeGenerator + StructAnalyzerResources.Designer.cs + ResXFileCodeGenerator DoNotUseOnGUIResources.Designer.cs @@ -170,6 +234,14 @@ ResXFileCodeGenerator InvokeFunctionMissingResources.Designer.cs + + ResXFileCodeGenerator + DoNotUseMaterialNameResource.Designer.cs + + + ResXFileCodeGenerator + DoNotUseCameraMainInUpdateResources.Designer.cs + @@ -236,6 +308,7 @@ + From a997b856c4877daa8447c1bb2b1884a6c2c85bf5 Mon Sep 17 00:00:00 2001 From: Donald Wu Date: Mon, 9 Oct 2017 20:40:17 +0800 Subject: [PATCH 02/10] fix bugs fix normal method invocation to be delegate; method with Conditional is allow to have boxing --- .../ShouldCacheDelegateAnalyzerTests.cs | 87 +++++++++++++++++++ .../DoNotBoxWhenInvokeAnalyzerTests.cs | 38 ++++++++ .../Delegates/ShouldCacheDelegateAnalyzer.cs | 16 ++-- .../GCAlloc/DoNotBoxWhenInvokeAnalyzer.cs | 19 +++- .../DoNotBoxWhenInvokeResource.Designer.cs | 2 +- .../GCAlloc/DoNotBoxWhenInvokeResource.resx | 2 +- 6 files changed, 155 insertions(+), 9 deletions(-) diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Delegates/ShouldCacheDelegateAnalyzerTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Delegates/ShouldCacheDelegateAnalyzerTests.cs index 943cafa..cecc498 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Delegates/ShouldCacheDelegateAnalyzerTests.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Delegates/ShouldCacheDelegateAnalyzerTests.cs @@ -218,5 +218,92 @@ private void OnCallBack(object sender, EventArgs e) } } + + [Test] + public void FunctionIsNotDelegate() + { + var code = @" + +using System; + +class C +{ + public event EventHandler e; + void Update() + { + Call([|ReturnInt()|]); + } + + private void Call(int intValue) + { + + } + + private int ReturnInt() + { + return 0; + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + + [Test] + public void FunctionIsNotDelegateAndDidNotCacheDelegate() + { + var code = @" + +using System; + +class C +{ + public event EventHandler e; + void Update() + { + Call(ReturnInt(), [|OnCallBack|]); + } + + private void Call(int intValue, EventHandler h) + { + + } + + private int ReturnInt() + { + return 0; + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } } } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/GCAlloc/DoNotBoxWhenInvokeAnalyzerTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/GCAlloc/DoNotBoxWhenInvokeAnalyzerTests.cs index 7182724..668fbff 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/GCAlloc/DoNotBoxWhenInvokeAnalyzerTests.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/GCAlloc/DoNotBoxWhenInvokeAnalyzerTests.cs @@ -153,5 +153,43 @@ private void Caller() Assert.Fail("Could not load unit test code"); } } + + + [Test] + public void BoxIgnoredWhenConditionalInvokeWithLiteral() + { + var code = @" + +using System.Diagnostics; + +class C +{ + [Conditional(""SOME_CONDITION""), Conditional(""SOME_CONDITION2"")] + private void Method(object p1, int p2) + { + } + + private void Caller() + { + Method([|234|], 2); + } +} + +"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, null, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.DoNotBoxWhenInvoke); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + } } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateAnalyzer.cs index 6ea363f..29b4aa8 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateAnalyzer.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateAnalyzer.cs @@ -47,18 +47,22 @@ private static void AnalyzeInvocationNode(SyntaxNodeAnalysisContext context) } var argumentSyntax = checkSyntax.DescendantNodes().OfType(); + var invocationSytnax = checkSyntax.DescendantNodes().OfType(); foreach (var oneArgSyntax in argumentSyntax) { foreach (var oneIdSyntax in oneArgSyntax.DescendantNodes().OfType()) { - var oneIdSymbol = context.SemanticModel.GetSymbolInfo(oneIdSyntax); - if (oneIdSymbol.Symbol != null) + if(!(oneIdSyntax.Parent is InvocationExpressionSyntax)) { - if (oneIdSymbol.Symbol is IMethodSymbol) + var oneIdSymbol = context.SemanticModel.GetSymbolInfo(oneIdSyntax); + if (oneIdSymbol.Symbol != null) { - var diagnostic = Diagnostic.Create(DiagnosticDescriptors.ShouldCacheDelegate, - oneIdSyntax.GetLocation(), oneIdSyntax.ToString()); - context.ReportDiagnostic(diagnostic); + if (oneIdSymbol.Symbol is IMethodSymbol) + { + var diagnostic = Diagnostic.Create(DiagnosticDescriptors.ShouldCacheDelegate, + oneIdSyntax.GetLocation(), oneIdSyntax.ToString()); + context.ReportDiagnostic(diagnostic); + } } } } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeAnalyzer.cs index 6c4629c..d35f816 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeAnalyzer.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeAnalyzer.cs @@ -31,7 +31,24 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context) var symbolInfo = context.SemanticModel.GetSymbolInfo(invocation); var methodSymbol = symbolInfo.Symbol as IMethodSymbol; - + + bool isConditional = false; + foreach(var oneAttribute in methodSymbol.GetAttributes()) + { + if(oneAttribute.AttributeClass.Name == "ConditionalAttribute" && oneAttribute.AttributeClass.ContainingNamespace.Name == "Diagnostics") + { + isConditional = true; + break; + } + + } + + if(isConditional) + { + return; + } + + if(methodSymbol.Parameters == null || methodSymbol.Parameters.Length == 0) { return; diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeResource.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeResource.Designer.cs index 973c008..98a9267 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeResource.Designer.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeResource.Designer.cs @@ -71,7 +71,7 @@ internal static string Description { } /// - /// 查找类似 Method {0}.{1}'s parameter "{2}"'s type {3} is reference type, but the argument "{4}"'s type {5} is value type, which makes boxing and gc allocation. 的本地化字符串。 + /// 查找类似 Method {0}.{1}'s parameter "{2}"'s type "{3}" is reference type, but the argument "{4}"'s type "{5}" is value type, which causes boxing and gc allocation. 的本地化字符串。 /// internal static string MessageFormat { get { diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeResource.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeResource.resx index 8e39528..5514b0a 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeResource.resx +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeResource.resx @@ -122,7 +122,7 @@ An optional longer localizable description of the diagnostic. - Method {0}.{1}'s parameter "{2}"'s type {3} is reference type, but the argument "{4}"'s type {5} is value type, which makes boxing and gc allocation. + Method {0}.{1}'s parameter "{2}"'s type "{3}" is reference type, but the argument "{4}"'s type "{5}" is value type, which causes boxing and gc allocation. The format-able message the diagnostic displays. From 33789b9ae1c9860525a74afe90317576b73d88aa Mon Sep 17 00:00:00 2001 From: Donald Wu Date: Mon, 9 Oct 2017 21:19:17 +0800 Subject: [PATCH 03/10] bug fix and span improvement fix member accessed normal method invocation to be delegate; shrink unsealed method span to method name only, in case that there are some methods are meant to be inherited, and too much green lines will drive developer crazy! --- .../ShouldCacheDelegateAnalyzerTests.cs | 41 +++++++++++++ .../UnsealedDerivedClassAnalyzerTests.cs | 58 +++++++++++++++++++ .../UnityEngineAnalyzer.Test.csproj | 1 + .../Delegates/ShouldCacheDelegateAnalyzer.cs | 9 ++- .../IL2CPP/UnsealedDerivedClassAnalyzer.cs | 2 +- 5 files changed, 105 insertions(+), 6 deletions(-) create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer.Test/IL2CPP/UnsealedDerivedClassAnalyzerTests.cs diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Delegates/ShouldCacheDelegateAnalyzerTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Delegates/ShouldCacheDelegateAnalyzerTests.cs index cecc498..8984168 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Delegates/ShouldCacheDelegateAnalyzerTests.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Delegates/ShouldCacheDelegateAnalyzerTests.cs @@ -261,6 +261,47 @@ private int ReturnInt() + [Test] + public void FunctionIsNotDelegate2() + { + var code = @" + +using System; + +class C +{ + public event EventHandler e; + void Update() + { + Call(this.[|ReturnInt()|]); + } + + private void Call(int intValue) + { + + } + + private int ReturnInt() + { + return 0; + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] public void FunctionIsNotDelegateAndDidNotCacheDelegate() { diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/IL2CPP/UnsealedDerivedClassAnalyzerTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/IL2CPP/UnsealedDerivedClassAnalyzerTests.cs new file mode 100644 index 0000000..f98832c --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/IL2CPP/UnsealedDerivedClassAnalyzerTests.cs @@ -0,0 +1,58 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.IL2CPP; + + +//using Microsoft.CodeAnalysis.Workspaces; + +namespace UnityEngineAnalyzer.Test.GCAlloc +{ + [TestFixture] + sealed class UnsealedDerivedClassAnalyzerTests : AnalyzerTestFixture + { + + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new UnsealedDerivedClassAnalyzer(); + + [Test] + public void UnsealedOverrideMethod() + { + var code = @" + +class C +{ + protected virtual void Method() + { + } +} + + +class D : C +{ + protected override void [|Method|]() + { + } +} + +"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, null, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.UnsealedDerivedClass); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj index e7ded7d..1a3278f 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj @@ -140,6 +140,7 @@ + diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateAnalyzer.cs index 29b4aa8..af15691 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateAnalyzer.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateAnalyzer.cs @@ -46,13 +46,12 @@ private static void AnalyzeInvocationNode(SyntaxNodeAnalysisContext context) return; } - var argumentSyntax = checkSyntax.DescendantNodes().OfType(); - var invocationSytnax = checkSyntax.DescendantNodes().OfType(); - foreach (var oneArgSyntax in argumentSyntax) + var argumentListSyntax = checkSyntax.ChildNodes().OfType(); + foreach (var oneArgListSyntax in argumentListSyntax) { - foreach (var oneIdSyntax in oneArgSyntax.DescendantNodes().OfType()) + foreach(var oneArgSyntax in oneArgListSyntax.ChildNodes().OfType()) { - if(!(oneIdSyntax.Parent is InvocationExpressionSyntax)) + foreach (var oneIdSyntax in oneArgSyntax.ChildNodes().OfType()) { var oneIdSymbol = context.SemanticModel.GetSymbolInfo(oneIdSyntax); if (oneIdSymbol.Symbol != null) diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/IL2CPP/UnsealedDerivedClassAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/IL2CPP/UnsealedDerivedClassAnalyzer.cs index 0d5501f..4dd4ad2 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/IL2CPP/UnsealedDerivedClassAnalyzer.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/IL2CPP/UnsealedDerivedClassAnalyzer.cs @@ -28,7 +28,7 @@ private void AnalyzeClassSyntax(SyntaxNodeAnalysisContext context) if (method.IsOverriden() && !method.IsSealed()) { - var diagnostic = Diagnostic.Create(SupportedDiagnostics.First(), method.GetLocation(), + var diagnostic = Diagnostic.Create(SupportedDiagnostics.First(), method.Identifier.GetLocation(), method.Identifier.ToString(), classDeclaration.Identifier.ToString()); context.ReportDiagnostic(diagnostic); From c8cf67b163b2b183022ed3cd5e53fcdbb55876a7 Mon Sep 17 00:00:00 2001 From: Donald Wu Date: Tue, 17 Oct 2017 10:44:04 +0800 Subject: [PATCH 04/10] AutoAddUnityEngineAnalyzer Each time you add/remove/modify your script file path, unity will re-generate csproj files, and your UnityEngineAnalyzer settings will miss. Add AutoAddUnityEngineAnalyzer, each time unity refreshes, AutoAddUnityEngineAnalyzer will check and try to add UnityEngineAnalyzer to your csproj files --- .gitignore | 5 + .../Editor/AutoAddUnityEngineAnalyzer.cs | 68 ++ .../ProjectSettings/AudioManager.asset | 17 + .../ProjectSettings/ClusterInputManager.asset | 6 + .../ProjectSettings/DynamicsManager.asset | 19 + .../ProjectSettings/EditorBuildSettings.asset | 7 + .../ProjectSettings/EditorSettings.asset | 16 + .../ProjectSettings/GraphicsSettings.asset | 61 ++ .../ProjectSettings/InputManager.asset | 295 +++++++++ .../ProjectSettings/NavMeshAreas.asset | 89 +++ .../ProjectSettings/NetworkManager.asset | 8 + .../ProjectSettings/Physics2DSettings.asset | 36 ++ .../ProjectSettings/ProjectSettings.asset | 597 ++++++++++++++++++ .../ProjectSettings/ProjectVersion.txt | 1 + .../ProjectSettings/QualitySettings.asset | 193 ++++++ .../ProjectSettings/TagManager.asset | 43 ++ .../ProjectSettings/TimeManager.asset | 9 + .../UnityConnectSettings.asset | 34 + 18 files changed, 1504 insertions(+) create mode 100644 AutoAddUnityEngineAnalyzer/Assets/Editor/AutoAddUnityEngineAnalyzer.cs create mode 100644 AutoAddUnityEngineAnalyzer/ProjectSettings/AudioManager.asset create mode 100644 AutoAddUnityEngineAnalyzer/ProjectSettings/ClusterInputManager.asset create mode 100644 AutoAddUnityEngineAnalyzer/ProjectSettings/DynamicsManager.asset create mode 100644 AutoAddUnityEngineAnalyzer/ProjectSettings/EditorBuildSettings.asset create mode 100644 AutoAddUnityEngineAnalyzer/ProjectSettings/EditorSettings.asset create mode 100644 AutoAddUnityEngineAnalyzer/ProjectSettings/GraphicsSettings.asset create mode 100644 AutoAddUnityEngineAnalyzer/ProjectSettings/InputManager.asset create mode 100644 AutoAddUnityEngineAnalyzer/ProjectSettings/NavMeshAreas.asset create mode 100644 AutoAddUnityEngineAnalyzer/ProjectSettings/NetworkManager.asset create mode 100644 AutoAddUnityEngineAnalyzer/ProjectSettings/Physics2DSettings.asset create mode 100644 AutoAddUnityEngineAnalyzer/ProjectSettings/ProjectSettings.asset create mode 100644 AutoAddUnityEngineAnalyzer/ProjectSettings/ProjectVersion.txt create mode 100644 AutoAddUnityEngineAnalyzer/ProjectSettings/QualitySettings.asset create mode 100644 AutoAddUnityEngineAnalyzer/ProjectSettings/TagManager.asset create mode 100644 AutoAddUnityEngineAnalyzer/ProjectSettings/TimeManager.asset create mode 100644 AutoAddUnityEngineAnalyzer/ProjectSettings/UnityConnectSettings.asset diff --git a/.gitignore b/.gitignore index 738c4ca..2f2f9a3 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,9 @@ *.user *.userosscache *.sln.docstates +AutoAddUnityEngineAnalyzer/obj/ +AutoAddUnityEngineAnalyzer/Library/ +AutoAddUnityEngineAnalyzer/Temp/ # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs @@ -258,3 +261,5 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk .DS_Store +*.csproj +*.sln diff --git a/AutoAddUnityEngineAnalyzer/Assets/Editor/AutoAddUnityEngineAnalyzer.cs b/AutoAddUnityEngineAnalyzer/Assets/Editor/AutoAddUnityEngineAnalyzer.cs new file mode 100644 index 0000000..656d7ad --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/Assets/Editor/AutoAddUnityEngineAnalyzer.cs @@ -0,0 +1,68 @@ + +using UnityEditor; +using UnityEngine; +using System.IO; +using System.Xml.Linq; +using System.Linq; + +namespace UnityEngineAnalyzer +{ + + public class AutoAddUnityEngineAnalyzer : AssetPostprocessor + { + /// + /// Put your UnityEngineAnalyzer.dll in your unity project, + /// and modify this path relative to your project. + /// + public const string UnityEngineAnalyzerPath = "Tools\\VisualStudio\\UnityEngineAnalyzer\\UnityEngineAnalyzer.dll"; + + static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) + { + TryAddUnityEngineAnalyzer(); + } + + + private static void TryAddUnityEngineAnalyzer() + { + string dataPath = Application.dataPath + "/../"; + + var csprojPaths = Directory.GetFiles(dataPath, "*.csproj"); + + foreach(var oneCsProjPath in csprojPaths) + { + if(!string.IsNullOrEmpty(oneCsProjPath)) + { + XDocument doc = XDocument.Load(oneCsProjPath); + var defaultNamespace = doc.Root.GetDefaultNamespace(); + + var unityEngineAnalyzer = doc.Descendants(defaultNamespace + "Analyzer"). + Where(x => x.Attribute("Include"). + Value.Contains("UnityEngineAnalyzer.dll")). + FirstOrDefault(); + + if(unityEngineAnalyzer == null) + { + Debug.Log("can not find UnityEngineAnalyzer in oneCsProjPath=" + oneCsProjPath); + + try + { + doc.Root. + Add( + new XElement(defaultNamespace + "ItemGroup", + new XElement(defaultNamespace + "Analyzer", + new XAttribute("Include", UnityEngineAnalyzerPath)))); + + doc.Save(oneCsProjPath); + Debug.Log("did add UnityEngineAnalyzer in oneCsProjPath=" + oneCsProjPath); + } + catch (System.Exception ex) + { + Debug.LogError("exception caught in adding UnityEngineAnalyzer in oneCsProjPath=" + oneCsProjPath + "\nexception=" + ex); + } + } + } + } + } + } + +} \ No newline at end of file diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/AudioManager.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/AudioManager.asset new file mode 100644 index 0000000..da61125 --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/AudioManager.asset @@ -0,0 +1,17 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!11 &1 +AudioManager: + m_ObjectHideFlags: 0 + m_Volume: 1 + Rolloff Scale: 1 + Doppler Factor: 1 + Default Speaker Mode: 2 + m_SampleRate: 0 + m_DSPBufferSize: 0 + m_VirtualVoiceCount: 512 + m_RealVoiceCount: 32 + m_SpatializerPlugin: + m_AmbisonicDecoderPlugin: + m_DisableAudio: 0 + m_VirtualizeEffects: 1 diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/ClusterInputManager.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/ClusterInputManager.asset new file mode 100644 index 0000000..e7886b2 --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/ClusterInputManager.asset @@ -0,0 +1,6 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!236 &1 +ClusterInputManager: + m_ObjectHideFlags: 0 + m_Inputs: [] diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/DynamicsManager.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/DynamicsManager.asset new file mode 100644 index 0000000..1931946 --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/DynamicsManager.asset @@ -0,0 +1,19 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!55 &1 +PhysicsManager: + m_ObjectHideFlags: 0 + serializedVersion: 3 + m_Gravity: {x: 0, y: -9.81, z: 0} + m_DefaultMaterial: {fileID: 0} + m_BounceThreshold: 2 + m_SleepThreshold: 0.005 + m_DefaultContactOffset: 0.01 + m_DefaultSolverIterations: 6 + m_DefaultSolverVelocityIterations: 1 + m_QueriesHitBackfaces: 0 + m_QueriesHitTriggers: 1 + m_EnableAdaptiveForce: 0 + m_EnablePCM: 1 + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + m_AutoSimulation: 1 diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/EditorBuildSettings.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/EditorBuildSettings.asset new file mode 100644 index 0000000..6dc24f7 --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/EditorBuildSettings.asset @@ -0,0 +1,7 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1045 &1 +EditorBuildSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Scenes: [] diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/EditorSettings.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/EditorSettings.asset new file mode 100644 index 0000000..c0c814f --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/EditorSettings.asset @@ -0,0 +1,16 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!159 &1 +EditorSettings: + m_ObjectHideFlags: 0 + serializedVersion: 4 + m_ExternalVersionControlSupport: Visible Meta Files + m_SerializationMode: 2 + m_DefaultBehaviorMode: 1 + m_SpritePackerMode: 4 + m_SpritePackerPaddingPower: 1 + m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd + m_ProjectGenerationRootNamespace: + m_UserGeneratedProjectSuffix: + m_CollabEditorSettings: + inProgressEnabled: 1 diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/GraphicsSettings.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/GraphicsSettings.asset new file mode 100644 index 0000000..74d7b53 --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/GraphicsSettings.asset @@ -0,0 +1,61 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!30 &1 +GraphicsSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_Deferred: + m_Mode: 1 + m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0} + m_DeferredReflections: + m_Mode: 1 + m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0} + m_ScreenSpaceShadows: + m_Mode: 1 + m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0} + m_LegacyDeferred: + m_Mode: 1 + m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0} + m_DepthNormals: + m_Mode: 1 + m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0} + m_MotionVectors: + m_Mode: 1 + m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0} + m_LightHalo: + m_Mode: 1 + m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0} + m_LensFlare: + m_Mode: 1 + m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0} + m_AlwaysIncludedShaders: + - {fileID: 7, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15104, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15105, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} + m_PreloadedShaders: [] + m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, + type: 0} + m_CustomRenderPipeline: {fileID: 0} + m_TransparencySortMode: 0 + m_TransparencySortAxis: {x: 0, y: 0, z: 1} + m_DefaultRenderingPath: 1 + m_DefaultMobileRenderingPath: 1 + m_TierSettings: [] + m_LightmapStripping: 0 + m_FogStripping: 0 + m_InstancingStripping: 0 + m_LightmapKeepPlain: 1 + m_LightmapKeepDirCombined: 1 + m_LightmapKeepDynamicPlain: 1 + m_LightmapKeepDynamicDirCombined: 1 + m_LightmapKeepShadowMask: 1 + m_LightmapKeepSubtractive: 1 + m_FogKeepLinear: 1 + m_FogKeepExp: 1 + m_FogKeepExp2: 1 + m_AlbedoSwatchInfos: [] + m_LightsUseLinearIntensity: 0 + m_LightsUseColorTemperature: 0 diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/InputManager.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/InputManager.asset new file mode 100644 index 0000000..17c8f53 --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/InputManager.asset @@ -0,0 +1,295 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!13 &1 +InputManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Axes: + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: left + positiveButton: right + altNegativeButton: a + altPositiveButton: d + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: down + positiveButton: up + altNegativeButton: s + altPositiveButton: w + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left ctrl + altNegativeButton: + altPositiveButton: mouse 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left alt + altNegativeButton: + altPositiveButton: mouse 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left shift + altNegativeButton: + altPositiveButton: mouse 2 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: space + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse X + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse Y + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse ScrollWheel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 2 + joyNum: 0 + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 0 + type: 2 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 1 + type: 2 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 0 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 1 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 2 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 3 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: return + altNegativeButton: + altPositiveButton: joystick button 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: enter + altNegativeButton: + altPositiveButton: space + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Cancel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: escape + altNegativeButton: + altPositiveButton: joystick button 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/NavMeshAreas.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/NavMeshAreas.asset new file mode 100644 index 0000000..6dd520f --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/NavMeshAreas.asset @@ -0,0 +1,89 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!126 &1 +NavMeshProjectSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + areas: + - name: Walkable + cost: 1 + - name: Not Walkable + cost: 1 + - name: Jump + cost: 2 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + m_LastAgentTypeID: -887442657 + m_Settings: + - serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.75 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + m_SettingNames: + - Humanoid diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/NetworkManager.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/NetworkManager.asset new file mode 100644 index 0000000..5dc6a83 --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/NetworkManager.asset @@ -0,0 +1,8 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!149 &1 +NetworkManager: + m_ObjectHideFlags: 0 + m_DebugLevel: 0 + m_Sendrate: 15 + m_AssetToPrefab: {} diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/Physics2DSettings.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/Physics2DSettings.asset new file mode 100644 index 0000000..e3b2d0b --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/Physics2DSettings.asset @@ -0,0 +1,36 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!19 &1 +Physics2DSettings: + m_ObjectHideFlags: 0 + serializedVersion: 3 + m_Gravity: {x: 0, y: -9.81} + m_DefaultMaterial: {fileID: 0} + m_VelocityIterations: 8 + m_PositionIterations: 3 + m_VelocityThreshold: 1 + m_MaxLinearCorrection: 0.2 + m_MaxAngularCorrection: 8 + m_MaxTranslationSpeed: 100 + m_MaxRotationSpeed: 360 + m_BaumgarteScale: 0.2 + m_BaumgarteTimeOfImpactScale: 0.75 + m_TimeToSleep: 0.5 + m_LinearSleepTolerance: 0.01 + m_AngularSleepTolerance: 2 + m_DefaultContactOffset: 0.01 + m_AutoSimulation: 1 + m_QueriesHitTriggers: 1 + m_QueriesStartInColliders: 1 + m_ChangeStopsCallbacks: 0 + m_CallbacksOnDisable: 1 + m_AlwaysShowColliders: 0 + m_ShowColliderSleep: 1 + m_ShowColliderContacts: 0 + m_ShowColliderAABB: 0 + m_ContactArrowScale: 0.2 + m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412} + m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432} + m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745} + m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804} + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/ProjectSettings.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/ProjectSettings.asset new file mode 100644 index 0000000..7581a9f --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/ProjectSettings.asset @@ -0,0 +1,597 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!129 &1 +PlayerSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + productGUID: f0b4bd3f2cfa848008759759325afe07 + AndroidProfiler: 0 + defaultScreenOrientation: 4 + targetDevice: 2 + useOnDemandResources: 0 + accelerometerFrequency: 60 + companyName: DefaultCompany + productName: AutoAddUnityEngineAnalyzer + defaultCursor: {fileID: 0} + cursorHotspot: {x: 0, y: 0} + m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1} + m_ShowUnitySplashScreen: 1 + m_ShowUnitySplashLogo: 1 + m_SplashScreenOverlayOpacity: 1 + m_SplashScreenAnimation: 1 + m_SplashScreenLogoStyle: 1 + m_SplashScreenDrawMode: 0 + m_SplashScreenBackgroundAnimationZoom: 1 + m_SplashScreenLogoAnimationZoom: 1 + m_SplashScreenBackgroundLandscapeAspect: 1 + m_SplashScreenBackgroundPortraitAspect: 1 + m_SplashScreenBackgroundLandscapeUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenBackgroundPortraitUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenLogos: [] + m_SplashScreenBackgroundLandscape: {fileID: 0} + m_SplashScreenBackgroundPortrait: {fileID: 0} + m_VirtualRealitySplashScreen: {fileID: 0} + m_HolographicTrackingLossScreen: {fileID: 0} + defaultScreenWidth: 1024 + defaultScreenHeight: 768 + defaultScreenWidthWeb: 960 + defaultScreenHeightWeb: 600 + m_StereoRenderingPath: 0 + m_ActiveColorSpace: 0 + m_MTRendering: 1 + m_MobileMTRendering: 0 + m_StackTraceTypes: 010000000100000001000000010000000100000001000000 + iosShowActivityIndicatorOnLoading: -1 + androidShowActivityIndicatorOnLoading: -1 + tizenShowActivityIndicatorOnLoading: -1 + iosAppInBackgroundBehavior: 0 + displayResolutionDialog: 1 + iosAllowHTTPDownload: 1 + allowedAutorotateToPortrait: 1 + allowedAutorotateToPortraitUpsideDown: 1 + allowedAutorotateToLandscapeRight: 1 + allowedAutorotateToLandscapeLeft: 1 + useOSAutorotation: 1 + use32BitDisplayBuffer: 1 + disableDepthAndStencilBuffers: 0 + defaultIsFullScreen: 1 + defaultIsNativeResolution: 1 + runInBackground: 0 + captureSingleScreen: 0 + muteOtherAudioSources: 0 + Prepare IOS For Recording: 0 + Force IOS Speakers When Recording: 0 + submitAnalytics: 1 + usePlayerLog: 1 + bakeCollisionMeshes: 0 + forceSingleInstance: 0 + resizableWindow: 0 + useMacAppStoreValidation: 0 + macAppStoreCategory: public.app-category.games + gpuSkinning: 0 + graphicsJobs: 0 + xboxPIXTextureCapture: 0 + xboxEnableAvatar: 0 + xboxEnableKinect: 0 + xboxEnableKinectAutoTracking: 0 + xboxEnableFitness: 0 + visibleInBackground: 1 + allowFullscreenSwitch: 1 + graphicsJobMode: 0 + macFullscreenMode: 2 + d3d9FullscreenMode: 1 + d3d11FullscreenMode: 1 + xboxSpeechDB: 0 + xboxEnableHeadOrientation: 0 + xboxEnableGuest: 0 + xboxEnablePIXSampling: 0 + n3dsDisableStereoscopicView: 0 + n3dsEnableSharedListOpt: 1 + n3dsEnableVSync: 0 + ignoreAlphaClear: 0 + xboxOneResolution: 0 + xboxOneMonoLoggingLevel: 0 + xboxOneLoggingLevel: 1 + xboxOneDisableEsram: 0 + videoMemoryForVertexBuffers: 0 + psp2PowerMode: 0 + psp2AcquireBGM: 1 + wiiUTVResolution: 0 + wiiUGamePadMSAA: 1 + wiiUSupportsNunchuk: 0 + wiiUSupportsClassicController: 0 + wiiUSupportsBalanceBoard: 0 + wiiUSupportsMotionPlus: 0 + wiiUSupportsProController: 0 + wiiUAllowScreenCapture: 1 + wiiUControllerCount: 0 + m_SupportedAspectRatios: + 4:3: 1 + 5:4: 1 + 16:10: 1 + 16:9: 1 + Others: 1 + bundleVersion: 1.0 + preloadedAssets: [] + metroInputSource: 0 + m_HolographicPauseOnTrackingLoss: 1 + xboxOneDisableKinectGpuReservation: 0 + xboxOneEnable7thCore: 0 + vrSettings: + cardboard: + depthFormat: 0 + enableTransitionView: 0 + daydream: + depthFormat: 0 + useSustainedPerformanceMode: 0 + hololens: + depthFormat: 1 + protectGraphicsMemory: 0 + useHDRDisplay: 0 + targetPixelDensity: 0 + resolutionScalingMode: 0 + applicationIdentifier: {} + buildNumber: {} + AndroidBundleVersionCode: 1 + AndroidMinSdkVersion: 16 + AndroidTargetSdkVersion: 0 + AndroidPreferredInstallLocation: 1 + aotOptions: + stripEngineCode: 1 + iPhoneStrippingLevel: 0 + iPhoneScriptCallOptimization: 0 + ForceInternetPermission: 0 + ForceSDCardPermission: 0 + CreateWallpaper: 0 + APKExpansionFiles: 0 + keepLoadedShadersAlive: 0 + StripUnusedMeshComponents: 0 + VertexChannelCompressionMask: + serializedVersion: 2 + m_Bits: 238 + iPhoneSdkVersion: 988 + iOSTargetOSVersionString: + tvOSSdkVersion: 0 + tvOSRequireExtendedGameController: 0 + tvOSTargetOSVersionString: + uIPrerenderedIcon: 0 + uIRequiresPersistentWiFi: 0 + uIRequiresFullScreen: 1 + uIStatusBarHidden: 1 + uIExitOnSuspend: 0 + uIStatusBarStyle: 0 + iPhoneSplashScreen: {fileID: 0} + iPhoneHighResSplashScreen: {fileID: 0} + iPhoneTallHighResSplashScreen: {fileID: 0} + iPhone47inSplashScreen: {fileID: 0} + iPhone55inPortraitSplashScreen: {fileID: 0} + iPhone55inLandscapeSplashScreen: {fileID: 0} + iPadPortraitSplashScreen: {fileID: 0} + iPadHighResPortraitSplashScreen: {fileID: 0} + iPadLandscapeSplashScreen: {fileID: 0} + iPadHighResLandscapeSplashScreen: {fileID: 0} + appleTVSplashScreen: {fileID: 0} + tvOSSmallIconLayers: [] + tvOSLargeIconLayers: [] + tvOSTopShelfImageLayers: [] + tvOSTopShelfImageWideLayers: [] + iOSLaunchScreenType: 0 + iOSLaunchScreenPortrait: {fileID: 0} + iOSLaunchScreenLandscape: {fileID: 0} + iOSLaunchScreenBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreenFillPct: 100 + iOSLaunchScreenSize: 100 + iOSLaunchScreenCustomXibPath: + iOSLaunchScreeniPadType: 0 + iOSLaunchScreeniPadImage: {fileID: 0} + iOSLaunchScreeniPadBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreeniPadFillPct: 100 + iOSLaunchScreeniPadSize: 100 + iOSLaunchScreeniPadCustomXibPath: + iOSDeviceRequirements: [] + iOSURLSchemes: [] + iOSBackgroundModes: 0 + iOSMetalForceHardShadows: 0 + metalEditorSupport: 0 + metalAPIValidation: 1 + iOSRenderExtraFrameOnPause: 0 + appleDeveloperTeamID: + iOSManualSigningProvisioningProfileID: + tvOSManualSigningProvisioningProfileID: + appleEnableAutomaticSigning: 0 + AndroidTargetDevice: 0 + AndroidSplashScreenScale: 0 + androidSplashScreen: {fileID: 0} + AndroidKeystoreName: + AndroidKeyaliasName: + AndroidTVCompatibility: 1 + AndroidIsGame: 1 + androidEnableBanner: 1 + m_AndroidBanners: + - width: 320 + height: 180 + banner: {fileID: 0} + androidGamepadSupportLevel: 0 + resolutionDialogBanner: {fileID: 0} + m_BuildTargetIcons: [] + m_BuildTargetBatching: [] + m_BuildTargetGraphicsAPIs: [] + m_BuildTargetVRSettings: [] + openGLRequireES31: 0 + openGLRequireES31AEP: 0 + webPlayerTemplate: APPLICATION:Default + m_TemplateCustomTags: {} + wiiUTitleID: 0005000011000000 + wiiUGroupID: 00010000 + wiiUCommonSaveSize: 4096 + wiiUAccountSaveSize: 2048 + wiiUOlvAccessKey: 0 + wiiUTinCode: 0 + wiiUJoinGameId: 0 + wiiUJoinGameModeMask: 0000000000000000 + wiiUCommonBossSize: 0 + wiiUAccountBossSize: 0 + wiiUAddOnUniqueIDs: [] + wiiUMainThreadStackSize: 3072 + wiiULoaderThreadStackSize: 1024 + wiiUSystemHeapSize: 128 + wiiUTVStartupScreen: {fileID: 0} + wiiUGamePadStartupScreen: {fileID: 0} + wiiUDrcBufferDisabled: 0 + wiiUProfilerLibPath: + playModeTestRunnerEnabled: 0 + actionOnDotNetUnhandledException: 1 + enableInternalProfiler: 0 + logObjCUncaughtExceptions: 1 + enableCrashReportAPI: 0 + cameraUsageDescription: + locationUsageDescription: + microphoneUsageDescription: + switchNetLibKey: + switchSocketMemoryPoolSize: 6144 + switchSocketAllocatorPoolSize: 128 + switchSocketConcurrencyLimit: 14 + switchScreenResolutionBehavior: 2 + switchUseCPUProfiler: 0 + switchApplicationID: 0x01004b9000490000 + switchNSODependencies: + switchTitleNames_0: + switchTitleNames_1: + switchTitleNames_2: + switchTitleNames_3: + switchTitleNames_4: + switchTitleNames_5: + switchTitleNames_6: + switchTitleNames_7: + switchTitleNames_8: + switchTitleNames_9: + switchTitleNames_10: + switchTitleNames_11: + switchPublisherNames_0: + switchPublisherNames_1: + switchPublisherNames_2: + switchPublisherNames_3: + switchPublisherNames_4: + switchPublisherNames_5: + switchPublisherNames_6: + switchPublisherNames_7: + switchPublisherNames_8: + switchPublisherNames_9: + switchPublisherNames_10: + switchPublisherNames_11: + switchIcons_0: {fileID: 0} + switchIcons_1: {fileID: 0} + switchIcons_2: {fileID: 0} + switchIcons_3: {fileID: 0} + switchIcons_4: {fileID: 0} + switchIcons_5: {fileID: 0} + switchIcons_6: {fileID: 0} + switchIcons_7: {fileID: 0} + switchIcons_8: {fileID: 0} + switchIcons_9: {fileID: 0} + switchIcons_10: {fileID: 0} + switchIcons_11: {fileID: 0} + switchSmallIcons_0: {fileID: 0} + switchSmallIcons_1: {fileID: 0} + switchSmallIcons_2: {fileID: 0} + switchSmallIcons_3: {fileID: 0} + switchSmallIcons_4: {fileID: 0} + switchSmallIcons_5: {fileID: 0} + switchSmallIcons_6: {fileID: 0} + switchSmallIcons_7: {fileID: 0} + switchSmallIcons_8: {fileID: 0} + switchSmallIcons_9: {fileID: 0} + switchSmallIcons_10: {fileID: 0} + switchSmallIcons_11: {fileID: 0} + switchManualHTML: + switchAccessibleURLs: + switchLegalInformation: + switchMainThreadStackSize: 1048576 + switchPresenceGroupId: + switchLogoHandling: 0 + switchReleaseVersion: 0 + switchDisplayVersion: 1.0.0 + switchStartupUserAccount: 0 + switchTouchScreenUsage: 0 + switchSupportedLanguagesMask: 0 + switchLogoType: 0 + switchApplicationErrorCodeCategory: + switchUserAccountSaveDataSize: 0 + switchUserAccountSaveDataJournalSize: 0 + switchApplicationAttribute: 0 + switchCardSpecSize: -1 + switchCardSpecClock: -1 + switchRatingsMask: 0 + switchRatingsInt_0: 0 + switchRatingsInt_1: 0 + switchRatingsInt_2: 0 + switchRatingsInt_3: 0 + switchRatingsInt_4: 0 + switchRatingsInt_5: 0 + switchRatingsInt_6: 0 + switchRatingsInt_7: 0 + switchRatingsInt_8: 0 + switchRatingsInt_9: 0 + switchRatingsInt_10: 0 + switchRatingsInt_11: 0 + switchLocalCommunicationIds_0: + switchLocalCommunicationIds_1: + switchLocalCommunicationIds_2: + switchLocalCommunicationIds_3: + switchLocalCommunicationIds_4: + switchLocalCommunicationIds_5: + switchLocalCommunicationIds_6: + switchLocalCommunicationIds_7: + switchParentalControl: 0 + switchAllowsScreenshot: 1 + switchDataLossConfirmation: 0 + switchSupportedNpadStyles: 3 + switchSocketConfigEnabled: 0 + switchTcpInitialSendBufferSize: 32 + switchTcpInitialReceiveBufferSize: 64 + switchTcpAutoSendBufferSizeMax: 256 + switchTcpAutoReceiveBufferSizeMax: 256 + switchUdpSendBufferSize: 9 + switchUdpReceiveBufferSize: 42 + switchSocketBufferEfficiency: 4 + switchSocketInitializeEnabled: 1 + switchNetworkInterfaceManagerInitializeEnabled: 1 + switchPlayerConnectionEnabled: 1 + ps4NPAgeRating: 12 + ps4NPTitleSecret: + ps4NPTrophyPackPath: + ps4ParentalLevel: 11 + ps4ContentID: ED1633-NPXX51362_00-0000000000000000 + ps4Category: 0 + ps4MasterVersion: 01.00 + ps4AppVersion: 01.00 + ps4AppType: 0 + ps4ParamSfxPath: + ps4VideoOutPixelFormat: 0 + ps4VideoOutInitialWidth: 1920 + ps4VideoOutBaseModeInitialWidth: 1920 + ps4VideoOutReprojectionRate: 120 + ps4PronunciationXMLPath: + ps4PronunciationSIGPath: + ps4BackgroundImagePath: + ps4StartupImagePath: + ps4SaveDataImagePath: + ps4SdkOverride: + ps4BGMPath: + ps4ShareFilePath: + ps4ShareOverlayImagePath: + ps4PrivacyGuardImagePath: + ps4NPtitleDatPath: + ps4RemotePlayKeyAssignment: -1 + ps4RemotePlayKeyMappingDir: + ps4PlayTogetherPlayerCount: 0 + ps4EnterButtonAssignment: 1 + ps4ApplicationParam1: 0 + ps4ApplicationParam2: 0 + ps4ApplicationParam3: 0 + ps4ApplicationParam4: 0 + ps4DownloadDataSize: 0 + ps4GarlicHeapSize: 2048 + ps4ProGarlicHeapSize: 2560 + ps4Passcode: 5PN2qmWqBlQ9wQj99nsQzldVI5ZuGXbE + ps4pnSessions: 1 + ps4pnPresence: 1 + ps4pnFriends: 1 + ps4pnGameCustomData: 1 + playerPrefsSupport: 0 + restrictedAudioUsageRights: 0 + ps4UseResolutionFallback: 0 + ps4ReprojectionSupport: 0 + ps4UseAudio3dBackend: 0 + ps4SocialScreenEnabled: 0 + ps4ScriptOptimizationLevel: 0 + ps4Audio3dVirtualSpeakerCount: 14 + ps4attribCpuUsage: 0 + ps4PatchPkgPath: + ps4PatchLatestPkgPath: + ps4PatchChangeinfoPath: + ps4PatchDayOne: 0 + ps4attribUserManagement: 0 + ps4attribMoveSupport: 0 + ps4attrib3DSupport: 0 + ps4attribShareSupport: 0 + ps4attribExclusiveVR: 0 + ps4disableAutoHideSplash: 0 + ps4videoRecordingFeaturesUsed: 0 + ps4contentSearchFeaturesUsed: 0 + ps4attribEyeToEyeDistanceSettingVR: 0 + ps4IncludedModules: [] + monoEnv: + psp2Splashimage: {fileID: 0} + psp2NPTrophyPackPath: + psp2NPSupportGBMorGJP: 0 + psp2NPAgeRating: 12 + psp2NPTitleDatPath: + psp2NPCommsID: + psp2NPCommunicationsID: + psp2NPCommsPassphrase: + psp2NPCommsSig: + psp2ParamSfxPath: + psp2ManualPath: + psp2LiveAreaGatePath: + psp2LiveAreaBackroundPath: + psp2LiveAreaPath: + psp2LiveAreaTrialPath: + psp2PatchChangeInfoPath: + psp2PatchOriginalPackage: + psp2PackagePassword: WRK5RhRXdCdG5nG5azdNMK66MuCV6GXi + psp2KeystoneFile: + psp2MemoryExpansionMode: 0 + psp2DRMType: 0 + psp2StorageType: 0 + psp2MediaCapacity: 0 + psp2DLCConfigPath: + psp2ThumbnailPath: + psp2BackgroundPath: + psp2SoundPath: + psp2TrophyCommId: + psp2TrophyPackagePath: + psp2PackagedResourcesPath: + psp2SaveDataQuota: 10240 + psp2ParentalLevel: 1 + psp2ShortTitle: Not Set + psp2ContentID: IV0000-ABCD12345_00-0123456789ABCDEF + psp2Category: 0 + psp2MasterVersion: 01.00 + psp2AppVersion: 01.00 + psp2TVBootMode: 0 + psp2EnterButtonAssignment: 2 + psp2TVDisableEmu: 0 + psp2AllowTwitterDialog: 1 + psp2Upgradable: 0 + psp2HealthWarning: 0 + psp2UseLibLocation: 0 + psp2InfoBarOnStartup: 0 + psp2InfoBarColor: 0 + psp2ScriptOptimizationLevel: 0 + psmSplashimage: {fileID: 0} + splashScreenBackgroundSourceLandscape: {fileID: 0} + splashScreenBackgroundSourcePortrait: {fileID: 0} + spritePackerPolicy: + webGLMemorySize: 256 + webGLExceptionSupport: 1 + webGLNameFilesAsHashes: 0 + webGLDataCaching: 0 + webGLDebugSymbols: 0 + webGLEmscriptenArgs: + webGLModulesDirectory: + webGLTemplate: APPLICATION:Default + webGLAnalyzeBuildSize: 0 + webGLUseEmbeddedResources: 0 + webGLUseWasm: 0 + webGLCompressionFormat: 1 + scriptingDefineSymbols: {} + platformArchitecture: {} + scriptingBackend: {} + incrementalIl2cppBuild: {} + additionalIl2CppArgs: + scriptingRuntimeVersion: 0 + apiCompatibilityLevelPerPlatform: {} + m_RenderingPath: 1 + m_MobileRenderingPath: 1 + metroPackageName: AutoAddUnityEngineAnalyzer + metroPackageVersion: + metroCertificatePath: + metroCertificatePassword: + metroCertificateSubject: + metroCertificateIssuer: + metroCertificateNotAfter: 0000000000000000 + metroApplicationDescription: AutoAddUnityEngineAnalyzer + wsaImages: {} + metroTileShortName: + metroCommandLineArgsFile: + metroTileShowName: 0 + metroMediumTileShowName: 0 + metroLargeTileShowName: 0 + metroWideTileShowName: 0 + metroDefaultTileSize: 1 + metroTileForegroundText: 2 + metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0} + metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, + a: 1} + metroSplashScreenUseBackgroundColor: 0 + platformCapabilities: {} + metroFTAName: + metroFTAFileTypes: [] + metroProtocolName: + metroCompilationOverrides: 1 + tizenProductDescription: + tizenProductURL: + tizenSigningProfileName: + tizenGPSPermissions: 0 + tizenMicrophonePermissions: 0 + tizenDeploymentTarget: + tizenDeploymentTargetType: -1 + tizenMinOSVersion: 1 + n3dsUseExtSaveData: 0 + n3dsCompressStaticMem: 1 + n3dsExtSaveDataNumber: 0x12345 + n3dsStackSize: 131072 + n3dsTargetPlatform: 2 + n3dsRegion: 7 + n3dsMediaSize: 0 + n3dsLogoStyle: 3 + n3dsTitle: GameName + n3dsProductCode: + n3dsApplicationId: 0xFF3FF + stvDeviceAddress: + stvProductDescription: + stvProductAuthor: + stvProductAuthorEmail: + stvProductLink: + stvProductCategory: 0 + XboxOneProductId: + XboxOneUpdateKey: + XboxOneSandboxId: + XboxOneContentId: + XboxOneTitleId: + XboxOneSCId: + XboxOneGameOsOverridePath: + XboxOnePackagingOverridePath: + XboxOneAppManifestOverridePath: + XboxOnePackageEncryption: 0 + XboxOnePackageUpdateGranularity: 2 + XboxOneDescription: + XboxOneLanguage: + - enus + XboxOneCapability: [] + XboxOneGameRating: {} + XboxOneIsContentPackage: 0 + XboxOneEnableGPUVariability: 0 + XboxOneSockets: {} + XboxOneSplashScreen: {fileID: 0} + XboxOneAllowedProductIds: [] + XboxOnePersistentLocalStorageSize: 0 + xboxOneScriptCompiler: 0 + vrEditorSettings: + daydream: + daydreamIconForeground: {fileID: 0} + daydreamIconBackground: {fileID: 0} + cloudServicesEnabled: {} + facebookSdkVersion: 7.9.4 + apiCompatibilityLevel: 2 + cloudProjectId: + projectName: + organizationId: + cloudEnabled: 0 + enableNativePlatformBackendsForNewInputSystem: 0 + disableOldInputManagerSupport: 0 diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/ProjectVersion.txt b/AutoAddUnityEngineAnalyzer/ProjectSettings/ProjectVersion.txt new file mode 100644 index 0000000..4b80f92 --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/ProjectVersion.txt @@ -0,0 +1 @@ +m_EditorVersion: 2017.1.2f1 diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/QualitySettings.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/QualitySettings.asset new file mode 100644 index 0000000..86c047f --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/QualitySettings.asset @@ -0,0 +1,193 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!47 &1 +QualitySettings: + m_ObjectHideFlags: 0 + serializedVersion: 5 + m_CurrentQuality: 5 + m_QualitySettings: + - serializedVersion: 2 + name: Very Low + pixelLightCount: 0 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 15 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + blendWeights: 1 + textureQuality: 1 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 0 + lodBias: 0.3 + maximumLODLevel: 0 + particleRaycastBudget: 4 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Low + pixelLightCount: 0 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 20 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + blendWeights: 2 + textureQuality: 0 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 0 + lodBias: 0.4 + maximumLODLevel: 0 + particleRaycastBudget: 16 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Medium + pixelLightCount: 1 + shadows: 1 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 20 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + blendWeights: 2 + textureQuality: 0 + anisotropicTextures: 1 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 1 + lodBias: 0.7 + maximumLODLevel: 0 + particleRaycastBudget: 64 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: High + pixelLightCount: 2 + shadows: 2 + shadowResolution: 1 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 40 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + blendWeights: 2 + textureQuality: 0 + anisotropicTextures: 1 + antiAliasing: 0 + softParticles: 0 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 1 + maximumLODLevel: 0 + particleRaycastBudget: 256 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Very High + pixelLightCount: 3 + shadows: 2 + shadowResolution: 2 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 70 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + blendWeights: 4 + textureQuality: 0 + anisotropicTextures: 2 + antiAliasing: 2 + softParticles: 1 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 1.5 + maximumLODLevel: 0 + particleRaycastBudget: 1024 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Ultra + pixelLightCount: 4 + shadows: 2 + shadowResolution: 2 + shadowProjection: 1 + shadowCascades: 4 + shadowDistance: 150 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + blendWeights: 4 + textureQuality: 0 + anisotropicTextures: 2 + antiAliasing: 2 + softParticles: 1 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 2 + maximumLODLevel: 0 + particleRaycastBudget: 4096 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + m_PerPlatformDefaultQuality: + Android: 2 + Nintendo 3DS: 5 + Nintendo Switch: 5 + PS4: 5 + PSM: 5 + PSP2: 2 + Samsung TV: 2 + Standalone: 5 + Tizen: 2 + Web: 5 + WebGL: 3 + WiiU: 5 + Windows Store Apps: 5 + XboxOne: 5 + iPhone: 2 + tvOS: 2 diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/TagManager.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/TagManager.asset new file mode 100644 index 0000000..1c92a78 --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/TagManager.asset @@ -0,0 +1,43 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!78 &1 +TagManager: + serializedVersion: 2 + tags: [] + layers: + - Default + - TransparentFX + - Ignore Raycast + - + - Water + - UI + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + m_SortingLayers: + - name: Default + uniqueID: 0 + locked: 0 diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/TimeManager.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/TimeManager.asset new file mode 100644 index 0000000..558a017 --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/TimeManager.asset @@ -0,0 +1,9 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!5 &1 +TimeManager: + m_ObjectHideFlags: 0 + Fixed Timestep: 0.02 + Maximum Allowed Timestep: 0.33333334 + m_TimeScale: 1 + Maximum Particle Timestep: 0.03 diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/UnityConnectSettings.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/UnityConnectSettings.asset new file mode 100644 index 0000000..1cc5485 --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/UnityConnectSettings.asset @@ -0,0 +1,34 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!310 &1 +UnityConnectSettings: + m_ObjectHideFlags: 0 + m_Enabled: 0 + m_TestMode: 0 + m_TestEventUrl: + m_TestConfigUrl: + m_TestInitMode: 0 + CrashReportingSettings: + m_EventUrl: https://perf-events.cloud.unity3d.com/api/events/crashes + m_Enabled: 0 + m_CaptureEditorExceptions: 1 + UnityPurchasingSettings: + m_Enabled: 0 + m_TestMode: 0 + UnityAnalyticsSettings: + m_Enabled: 0 + m_InitializeOnStartup: 1 + m_TestMode: 0 + m_TestEventUrl: + m_TestConfigUrl: + UnityAdsSettings: + m_Enabled: 0 + m_InitializeOnStartup: 1 + m_TestMode: 0 + m_EnabledPlatforms: 4294967295 + m_IosGameId: + m_AndroidGameId: + m_GameIds: {} + m_GameId: + PerformanceReportingSettings: + m_Enabled: 0 From 05ff37cd0e18a8c5d8899a374a3be4b246c3ce16 Mon Sep 17 00:00:00 2001 From: Donald Wu Date: Mon, 23 Oct 2017 16:55:50 +0800 Subject: [PATCH 05/10] merged & tested merged & tested --- UnityEngineAnalyzer.CLI/SolutionAnalyzer.cs | 2 +- UnityEngineAnalyzer.sln | 7 +- .../DoNotUseForeachInUpdateAnalyzerTests.cs | 4 +- .../DoNotUseMaterialNameAnalyzerTests.cs | 41 ----- .../UnityEngineAnalyzer.Test.csproj | 1 - .../DoNotUseCameraMainInUpdateAnalyzer.cs | 157 ------------------ ...UseCameraMainInUpdateResources.Designer.cs | 100 ----------- .../DoNotUseCameraMainInUpdateResources.resx | 135 --------------- .../DiagnosticDescriptors.cs | 4 +- .../DoNotUseForEachInUpdate.cs | 4 - .../Render/DoNotUseMaterialNameAnalyzer.cs | 74 --------- .../DoNotUseMaterialNameResource.Designer.cs | 68 -------- .../Render/DoNotUseMaterialNameResource.resx | 132 --------------- .../DoNotUseStateNameResource.Designer.cs | 68 -------- .../UnityEngineAnalyzer.csproj | 20 --- 15 files changed, 9 insertions(+), 808 deletions(-) delete mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Render/DoNotUseMaterialNameAnalyzerTests.cs delete mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/DoNotUseCameraMainInUpdateAnalyzer.cs delete mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/DoNotUseCameraMainInUpdateResources.Designer.cs delete mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/DoNotUseCameraMainInUpdateResources.resx delete mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseMaterialNameAnalyzer.cs delete mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseMaterialNameResource.Designer.cs delete mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseMaterialNameResource.resx delete mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseStateNameResource.Designer.cs diff --git a/UnityEngineAnalyzer.CLI/SolutionAnalyzer.cs b/UnityEngineAnalyzer.CLI/SolutionAnalyzer.cs index 21704ce..2b05582 100644 --- a/UnityEngineAnalyzer.CLI/SolutionAnalyzer.cs +++ b/UnityEngineAnalyzer.CLI/SolutionAnalyzer.cs @@ -61,7 +61,7 @@ private ImmutableArray GetAnalyzers(Dictionary { var listBuilder = ImmutableArray.CreateBuilder(); - var assembly = typeof(UnsealedDerivedClassAnalyzer).Assembly; + var assembly = typeof(DoNotUseForEachInUpdate).Assembly; var allTypes = assembly.DefinedTypes; foreach (var type in allTypes) diff --git a/UnityEngineAnalyzer.sln b/UnityEngineAnalyzer.sln index c4bc4fa..52b675c 100644 --- a/UnityEngineAnalyzer.sln +++ b/UnityEngineAnalyzer.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 +# Visual Studio 15 +VisualStudioVersion = 15.0.26730.15 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityEngineAnalyzer", "UnityEngineAnalyzer\UnityEngineAnalyzer\UnityEngineAnalyzer.csproj", "{BFB2BB34-FED8-48CC-9B83-A6E38BA5666C}" EndProject @@ -37,4 +37,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {AED667A6-0E4F-4396-A379-CE20111B4D2C} + EndGlobalSection EndGlobal diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/ForEachInUpdate/DoNotUseForeachInUpdateAnalyzerTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/ForEachInUpdate/DoNotUseForeachInUpdateAnalyzerTests.cs index f3353a6..541f781 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/ForEachInUpdate/DoNotUseForeachInUpdateAnalyzerTests.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/ForEachInUpdate/DoNotUseForeachInUpdateAnalyzerTests.cs @@ -9,7 +9,6 @@ //using Microsoft.CodeAnalysis.Workspaces; -/* namespace UnityEngineAnalyzer.Test.ForEachInUpdate { [TestFixture] @@ -54,5 +53,4 @@ void Update() } } } -} -*/ \ No newline at end of file +} \ No newline at end of file diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Render/DoNotUseMaterialNameAnalyzerTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Render/DoNotUseMaterialNameAnalyzerTests.cs deleted file mode 100644 index 60a7d77..0000000 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Render/DoNotUseMaterialNameAnalyzerTests.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Text; -using NUnit.Framework; -using RoslynNUnitLight; -using UnityEngineAnalyzer.Render; - -namespace UnityEngineAnalyzer.Test.Render -{ - - [TestFixture] - sealed class DoNotUseMaterialNameAnalyzerTests : AnalyzerTestFixture - { - protected override string LanguageName => LanguageNames.CSharp; - protected override DiagnosticAnalyzer CreateAnalyzer() => new DoNotUseMaterialNameAnalyzer(); - - [Test] - public void MaterialSetFloatStringName() - { - const string code = @" -using UnityEngine; - -class C : MonoBehaviour -{ - Material mat; - - void Start() - { - [|mat.SetFloat(""Run"", 1.2f)|]; - } -}"; - - Document document; - TextSpan span; - TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); - - HasDiagnostic(document, span, DiagnosticIDs.DoNotUseMaterialNameInMaterial); - } - - } -} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj index 9b898f5..a0d6603 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj @@ -149,7 +149,6 @@ - diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/DoNotUseCameraMainInUpdateAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/DoNotUseCameraMainInUpdateAnalyzer.cs deleted file mode 100644 index c40790a..0000000 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/DoNotUseCameraMainInUpdateAnalyzer.cs +++ /dev/null @@ -1,157 +0,0 @@ -using System; -using System.Collections.Generic; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; - -namespace UnityEngineAnalyzer.FindMethodsInUpdate -{ - [DiagnosticAnalyzer(LanguageNames.CSharp)] - public sealed class DoNotUseCameraMainInUpdateAnalyzer : DiagnosticAnalyzer - { - - private Dictionary _indirectCallers; - - - public override ImmutableArray SupportedDiagnostics - { - get - { - return ImmutableArray.Create( - DiagnosticDescriptors.DoNotUseCameraMainInUpdate, - DiagnosticDescriptors.DoNotUseCameraMainInUpdateRecursive); - } - } - - public override void Initialize(AnalysisContext context) - { - context.RegisterSyntaxNodeAction(AnalyzeClassSyntax, SyntaxKind.ClassDeclaration); - } - - public void AnalyzeClassSyntax(SyntaxNodeAnalysisContext context) - { - var monoBehaviourInfo = new MonoBehaviourInfo(context); - - var searched = new Dictionary(); - _indirectCallers = new Dictionary(); - - monoBehaviourInfo.ForEachUpdateMethod((updateMethod) => - { - //Debug.WriteLine("Found an update call! " + updateMethod); - - var results = SearchForCameraMain(context, updateMethod, searched, true); - - foreach (var oneResult in results) - { - if (!_indirectCallers.ContainsKey(oneResult)) - { - var diagnostic = Diagnostic.Create(DiagnosticDescriptors.DoNotUseCameraMainInUpdate, - oneResult.GetLocation(), oneResult, monoBehaviourInfo.ClassName, updateMethod.Identifier); - context.ReportDiagnostic(diagnostic); - } - else - { - var endPoint = _indirectCallers[oneResult]; - - var diagnostic = Diagnostic.Create(DiagnosticDescriptors.DoNotUseCameraMainInUpdateRecursive, - oneResult.GetLocation(), monoBehaviourInfo.ClassName, updateMethod.Identifier, oneResult, endPoint); - context.ReportDiagnostic(diagnostic); - } - - } - }); - } - - //TODO: Try to simplify this method - it's very hard to follow - private IEnumerable SearchForCameraMain(SyntaxNodeAnalysisContext context, - MethodDeclarationSyntax method, IDictionary searchedSymbol, bool isRoot) - { - var accessExps = method.DescendantNodes().OfType(); - foreach (var oneAccessExp in accessExps) - { - SymbolInfo oneSymbolInfo; - if (!context.TryGetSymbolInfo(oneAccessExp, out oneSymbolInfo)) - { - continue; - } - - var propertySymbol = oneSymbolInfo.Symbol as IPropertySymbol; - if (propertySymbol != null) - { - if (searchedSymbol.ContainsKey(propertySymbol)) - { - if (searchedSymbol[propertySymbol]) - { - yield return (ExpressionSyntax)oneAccessExp; - } - } - else - { - if (propertySymbol.Name == "main" && - propertySymbol.ContainingSymbol.ToString() == "UnityEngine.Camera") - { - searchedSymbol.Add(propertySymbol, true); - yield return oneAccessExp; - } - } - } - } - - - var invocationExps = method.DescendantNodes().OfType(); - foreach (var oneInvocationExp in invocationExps) - { - SymbolInfo oneSymbolInfo; - if (!context.TryGetSymbolInfo(oneInvocationExp, out oneSymbolInfo)) - { - continue; - } - - var methodSymbol = oneSymbolInfo.Symbol as IMethodSymbol; - - if (methodSymbol != null) - { - if (searchedSymbol.ContainsKey(methodSymbol)) - { - if (searchedSymbol[methodSymbol]) - { - yield return oneInvocationExp; - } - } - else - { - var methodDeclarations = methodSymbol.DeclaringSyntaxReferences; - searchedSymbol.Add(methodSymbol, false); //let's assume there won't be any calls - - foreach (var methodDeclaration in methodDeclarations) - { - var theMethodSyntax = methodDeclaration.GetSyntax() as MethodDeclarationSyntax; - - if (theMethodSyntax != null) - { - var childResults = SearchForCameraMain(context, theMethodSyntax, searchedSymbol, false); - - if (childResults != null && childResults.Any()) - { - searchedSymbol[methodSymbol] = true; //update the searched dictionary with new info - - if (isRoot) - { - _indirectCallers.Add(oneInvocationExp, childResults.First()); - } - - yield return oneInvocationExp; - break; - } - } - } - } - } - } - } - } -} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/DoNotUseCameraMainInUpdateResources.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/DoNotUseCameraMainInUpdateResources.Designer.cs deleted file mode 100644 index 7ba68ec..0000000 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/DoNotUseCameraMainInUpdateResources.Designer.cs +++ /dev/null @@ -1,100 +0,0 @@ -//------------------------------------------------------------------------------ -// -// 此代码由工具生成。 -// 运行时版本:4.0.30319.42000 -// -// 对此文件的更改可能会导致不正确的行为,并且如果 -// 重新生成代码,这些更改将会丢失。 -// -//------------------------------------------------------------------------------ - -namespace UnityEngineAnalyzer.Camera { - using System; - using System.Reflection; - - - /// - /// 一个强类型的资源类,用于查找本地化的字符串等。 - /// - // 此类是由 StronglyTypedResourceBuilder - // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 - // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen - // (以 /str 作为命令选项),或重新生成 VS 项目。 - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class DoNotUseCameraMainInUpdateResources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal DoNotUseCameraMainInUpdateResources() { - } - - /// - /// 返回此类使用的缓存的 ResourceManager 实例。 - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityEngineAnalyzer.Camera.DoNotUseCameraMainInUpdateResources", typeof(DoNotUseCameraMainInUpdateResources).GetTypeInfo().Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// 使用此强类型资源类,为所有资源查找 - /// 重写当前线程的 CurrentUICulture 属性。 - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// 查找类似 Using Camera.main in Update methods can impact performance. Cache Camera.main on Start or Awake methods 的本地化字符串。 - /// - internal static string Description { - get { - return ResourceManager.GetString("Description", resourceCulture); - } - } - - /// - /// 查找类似 The method {0} is called from {1}.{2} which could cause performance problems. Cache the result from {0} in Start or Awake instead. 的本地化字符串。 - /// - internal static string MessageFormat { - get { - return ResourceManager.GetString("MessageFormat", resourceCulture); - } - } - - /// - /// 查找类似 The method {0}.{1} calls {2} which eventually calls {3} whose Camera.main could impact performance. Cache Camera.main from {3} in Start or Awake instead. 的本地化字符串。 - /// - internal static string MessageFormatRecursive { - get { - return ResourceManager.GetString("MessageFormatRecursive", resourceCulture); - } - } - - /// - /// 查找类似 Cache the result of Find or Camera.main in Start or Awake. 的本地化字符串。 - /// - internal static string Title { - get { - return ResourceManager.GetString("Title", resourceCulture); - } - } - } -} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/DoNotUseCameraMainInUpdateResources.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/DoNotUseCameraMainInUpdateResources.resx deleted file mode 100644 index 5bc9e47..0000000 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/DoNotUseCameraMainInUpdateResources.resx +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Using Camera.main in Update methods can impact performance. Cache Camera.main on Start or Awake methods - An optional longer localizable description of the diagnostic. - - - The method {0} is called from {1}.{2} which could cause performance problems. Cache the result from {0} in Start or Awake instead. - The format-able message the diagnostic displays. - - - The method {0}.{1} calls {2} which eventually calls {3} whose Camera.main could impact performance. Cache Camera.main from {3} in Start or Awake instead. - - - Cache the result of Find or Camera.main in Start or Awake. - The title of the diagnostic. - - \ No newline at end of file diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs index db90bbf..670b71a 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs @@ -15,8 +15,6 @@ using UnityEngineAnalyzer.OnGUI; using UnityEngineAnalyzer.Physics; using UnityEngineAnalyzer.StringMethods; -using UnityEngineAnalyzer.Render; -using UnityEngineAnalyzer.Camera; using UnityEngineAnalyzer.Language; using UnityEngineAnalyzer.GCAlloc; using UnityEngineAnalyzer.Generics; @@ -44,6 +42,7 @@ public static class DiagnosticDescriptors public static readonly DiagnosticDescriptor UseNonAllocMethods; public static readonly DiagnosticDescriptor CameraMainIsSlow; public static readonly DiagnosticDescriptor DoNotGCAllocnInUpdate; + public static readonly DiagnosticDescriptor DoNotGCAllocnInUpdateRecursive; public static readonly DiagnosticDescriptor DoNotBoxWhenInvoke; public static readonly DiagnosticDescriptor StructShouldImplementIEquatable; public static readonly DiagnosticDescriptor StructShouldOverrideEquals; @@ -63,6 +62,7 @@ static DiagnosticDescriptors() UseNonAllocMethods = CreateDiagnosticDescriptor(DiagnosticIDs.PhysicsUseNonAllocMethods, DiagnosticCategories.GC, DiagnosticSeverity.Warning, UnityVersion.UNITY_5_3); CameraMainIsSlow = CreateDiagnosticDescriptor(DiagnosticIDs.CameraMainIsSlow, DiagnosticCategories.GC, DiagnosticSeverity.Warning); DoNotGCAllocnInUpdate = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotGCAllocInUpdate, DiagnosticCategories.GC, DiagnosticSeverity.Warning); + DoNotGCAllocnInUpdateRecursive = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotGCAllocInUpdate, DiagnosticCategories.GC, DiagnosticSeverity.Warning); DoNotBoxWhenInvoke = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotBoxWhenInvoke, DiagnosticCategories.GC, DiagnosticSeverity.Warning); ShouldCacheDelegate = CreateDiagnosticDescriptor(DiagnosticIDs.ShouldCacheDelegate, DiagnosticCategories.GC, DiagnosticSeverity.Warning); diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/ForEachInUpdate/DoNotUseForEachInUpdate.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/ForEachInUpdate/DoNotUseForEachInUpdate.cs index 7fe1a3d..9ac340d 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/ForEachInUpdate/DoNotUseForEachInUpdate.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/ForEachInUpdate/DoNotUseForEachInUpdate.cs @@ -9,9 +9,6 @@ namespace UnityEngineAnalyzer.ForEachInUpdate { - /* - * In unity 5.5.0 or newer, foreach is not neccessary to avoid. - * [DiagnosticAnalyzer(LanguageNames.CSharp)] public sealed class DoNotUseForEachInUpdate : DiagnosticAnalyzer { @@ -57,5 +54,4 @@ private static IEnumerable SearchForForEach(SyntaxNodeAn //TODO: Keep Searching recurively to other methods... } } - */ } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseMaterialNameAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseMaterialNameAnalyzer.cs deleted file mode 100644 index 18c8cc7..0000000 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseMaterialNameAnalyzer.cs +++ /dev/null @@ -1,74 +0,0 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; -using System.Collections.Immutable; - -namespace UnityEngineAnalyzer.Render -{ - [DiagnosticAnalyzer(LanguageNames.CSharp)] - public sealed class DoNotUseMaterialNameAnalyzer : DiagnosticAnalyzer - { - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.DoNotUseMaterialName); - - private static readonly ImmutableHashSet checkMethods = ImmutableHashSet.Create( - "GetColor", - "GetColorArray", - "GetFloat", - "GetFloatArray", - "GetInt", - "GetMatrix", - "GetMatrixArray", - "GetTexture", - "GetVector", - "HasProperty", - "SetBuffer", - "SetColor", - "SetColorArray", - "SetFloat", - "SetFloatArray", - "SetInt", - "SetMatrix", - "SetMatrixArray", - "SetTexture", - "SetTextureOffset", - "SetTextureScale", - "SetVector", - "SetVectorArray" - ); - - public override void Initialize(AnalysisContext context) - { - context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.InvocationExpression); - } - - private static void AnalyzeNode(SyntaxNodeAnalysisContext context) - { - var invocation = context.Node as InvocationExpressionSyntax; - if (invocation == null) - { - return; - } - - var name = invocation.MethodName(); - - // check if any of the methods are used - if (!checkMethods.Contains(name)) { return; } - - var symbolInfo = context.SemanticModel.GetSymbolInfo(invocation); - var methodSymbol = symbolInfo.Symbol as IMethodSymbol; - - var containingClass = methodSymbol.ContainingType; - - // check if the method is the one from UnityEngine.Animator - if (containingClass.ContainingNamespace.Name.Equals("UnityEngine") && containingClass.Name.Equals("Material")) - { - if (methodSymbol.Parameters[0].Type.MetadataName == "String") - { - var diagnostic = Diagnostic.Create(DiagnosticDescriptors.DoNotUseMaterialName, invocation.GetLocation(), containingClass.Name, methodSymbol.Name); - context.ReportDiagnostic(diagnostic); - } - } - } - } -} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseMaterialNameResource.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseMaterialNameResource.Designer.cs deleted file mode 100644 index 0ea5c80..0000000 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseMaterialNameResource.Designer.cs +++ /dev/null @@ -1,68 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace UnityEngineAnalyzer.Render { - using System; - using System.Reflection; - - - [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [System.Diagnostics.DebuggerNonUserCodeAttribute()] - [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class DoNotUseMaterialNameResource { - - private static System.Resources.ResourceManager resourceMan; - - private static System.Globalization.CultureInfo resourceCulture; - - [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal DoNotUseMaterialNameResource() { - } - - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] - internal static System.Resources.ResourceManager ResourceManager { - get { - if (object.Equals(null, resourceMan)) { - System.Resources.ResourceManager temp = new System.Resources.ResourceManager("UnityEngineAnalyzer.Render.DoNotUseMaterialNameResource", typeof(DoNotUseMaterialNameResource).GetTypeInfo().Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] - internal static System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - internal static string Description { - get { - return ResourceManager.GetString("Description", resourceCulture); - } - } - - internal static string MessageFormat { - get { - return ResourceManager.GetString("MessageFormat", resourceCulture); - } - } - - internal static string Title { - get { - return ResourceManager.GetString("Title", resourceCulture); - } - } - } -} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseMaterialNameResource.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseMaterialNameResource.resx deleted file mode 100644 index 35529f9..0000000 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseMaterialNameResource.resx +++ /dev/null @@ -1,132 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Shader.PropertyToID can convert your name to hash, it's faster than string comparison - An optional longer localizable description of the diagnostic. - - - Use nameID instead of name - The format-able message the diagnostic displays. - - - Use nameID instead of name - The title of the diagnostic. - - \ No newline at end of file diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseStateNameResource.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseStateNameResource.Designer.cs deleted file mode 100644 index dcd873b..0000000 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/Render/DoNotUseStateNameResource.Designer.cs +++ /dev/null @@ -1,68 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace UnityEngineAnalyzer.Shader { - using System; - using System.Reflection; - - - [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [System.Diagnostics.DebuggerNonUserCodeAttribute()] - [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class DoNotUseMaterialNameResource { - - private static System.Resources.ResourceManager resourceMan; - - private static System.Globalization.CultureInfo resourceCulture; - - [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal DoNotUseMaterialNameResource() { - } - - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] - internal static System.Resources.ResourceManager ResourceManager { - get { - if (object.Equals(null, resourceMan)) { - System.Resources.ResourceManager temp = new System.Resources.ResourceManager("UnityEngineAnalyzer.Shader.DoNotUseMaterialNameResource", typeof(DoNotUseMaterialNameResource).GetTypeInfo().Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] - internal static System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - internal static string Description { - get { - return ResourceManager.GetString("Description", resourceCulture); - } - } - - internal static string MessageFormat { - get { - return ResourceManager.GetString("MessageFormat", resourceCulture); - } - } - - internal static string Title { - get { - return ResourceManager.GetString("Title", resourceCulture); - } - } - } -} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj b/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj index 8368c1e..93b5f25 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj @@ -166,18 +166,6 @@ True InvokeFunctionMissingResources.resx - - - True - True - DoNotUseMaterialNameResource.resx - - - - True - True - DoNotUseCameraMainInUpdateResources.resx - @@ -265,14 +253,6 @@ ResXFileCodeGenerator InvokeFunctionMissingResources.Designer.cs - - ResXFileCodeGenerator - DoNotUseMaterialNameResource.Designer.cs - - - ResXFileCodeGenerator - DoNotUseCameraMainInUpdateResources.Designer.cs - From a465ae052b50a35684143419f8f472617008e0af Mon Sep 17 00:00:00 2001 From: Donald Wu Date: Thu, 6 Dec 2018 10:02:29 +0800 Subject: [PATCH 06/10] more NB analyzer more NB analyzer --- .../UnityEngineAnalyzer.CLI.csproj | 3 + UnityEngineAnalyzer.dll | Bin 0 -> 99840 bytes UnityEngineAnalyzer.sln | 6 - .../ShouldCacheDelegateAnalyzerTests.cs | 468 +++++++++++++++++- .../EmptyMonoBehaviourMethodsCodeFixTests.cs | 47 ++ .../DoNotBoxWhenInvokeAnalyzerTests.cs | 69 ++- .../EnumShouldManualSetMemberValueTests.cs | 75 +++ .../UnsealedDerivedClassCodeFixTests.cs | 63 +++ .../DuplicatedDelegateDetectionTestCases.cs | 190 +++++++ .../Language/LambdaLocalVaribleTestCases.cs | 92 ++++ .../LogicError/InfiniteRecursiveCallTest.cs | 396 +++++++++++++++ .../MetadataReferenceHelper.cs | 4 +- .../UnityEngineAnalyzer.Test.csproj | 25 +- .../UnityEngineAnalyzer.Test/packages.config | 1 + .../UnityEngineAnalyzer.Vsix.csproj | 7 +- .../Delegates/DuplicatedDelegateDetection.cs | 121 +++++ ...catedDelegateDetectionResource.Designer.cs | 109 ++++ .../DuplicatedDelegateDetectionResource.resx | 135 +++++ .../Delegates/ShouldCacheDelegateAnalyzer.cs | 91 +++- .../DiagnosticCategories.cs | 1 + .../DiagnosticDescriptors.cs | 69 ++- .../UnityEngineAnalyzer/DiagnosticIDs.cs | 24 +- .../EmptyMonoBehaviourMethodsAnalyzer.cs | 6 +- .../EmptyMonoBehaviourMethodsCodeFixer.cs | 54 ++ .../GCAlloc/DoNotBoxWhenInvokeAnalyzer.cs | 18 +- .../DoNotUseEnumTypeParameterAnalyzer.cs | 8 +- .../EnumShouldManualSetMemberValue.cs | 48 ++ ...ldManualSetMemberValueResource.Designer.cs | 91 ++++ ...numShouldManualSetMemberValueResource.resx | 132 +++++ .../IL2CPP/UnsealedDerivedClassCodeFixer.cs | 65 +++ .../UnityEngineAnalyzer/IgnoringWithCommit.cs | 20 + .../LambdaAnalyzerResources.Designer.cs | 91 ++++ .../Lambda/LambdaAnalyzerResources.resx | 129 +++++ .../Lambda/LambdaClosureAnalyzer.cs | 40 ++ .../InfiniteRecursiveCallAnalyzer.cs | 234 +++++++++ ...InfiniteRecursiveCallResources.Designer.cs | 100 ++++ .../InfiniteRecursiveCallResources.resx | 134 +++++ .../UnityEngineAnalyzer/MonoBehaviourInfo.cs | 53 +- .../UnityEngineAnalyzer.csproj | 48 ++ 39 files changed, 3185 insertions(+), 82 deletions(-) create mode 100644 UnityEngineAnalyzer.dll create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer.Test/EmptyMonoBehaviourMethods/EmptyMonoBehaviourMethodsCodeFixTests.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Generics/EnumShouldManualSetMemberValueTests.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer.Test/IL2CPP/UnsealedDerivedClassCodeFixTests.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/DuplicatedDelegateDetectionTestCases.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/LambdaLocalVaribleTestCases.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer.Test/LogicError/InfiniteRecursiveCallTest.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/DuplicatedDelegateDetection.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/DuplicatedDelegateDetectionResource.Designer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/DuplicatedDelegateDetectionResource.resx create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/EmptyMonoBehaviourMethods/EmptyMonoBehaviourMethodsCodeFixer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/EnumShouldManualSetMemberValue.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/EnumShouldManualSetMemberValueResource.Designer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/EnumShouldManualSetMemberValueResource.resx create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/IL2CPP/UnsealedDerivedClassCodeFixer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/IgnoringWithCommit.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Lambda/LambdaAnalyzerResources.Designer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Lambda/LambdaAnalyzerResources.resx create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Lambda/LambdaClosureAnalyzer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/LogicError/InfiniteRecursiveCallAnalyzer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/LogicError/InfiniteRecursiveCallResources.Designer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/LogicError/InfiniteRecursiveCallResources.resx diff --git a/UnityEngineAnalyzer.CLI/UnityEngineAnalyzer.CLI.csproj b/UnityEngineAnalyzer.CLI/UnityEngineAnalyzer.CLI.csproj index 76e8c8d..a1ef17c 100644 --- a/UnityEngineAnalyzer.CLI/UnityEngineAnalyzer.CLI.csproj +++ b/UnityEngineAnalyzer.CLI/UnityEngineAnalyzer.CLI.csproj @@ -104,6 +104,9 @@ + + ..\..\..\..\..\..\..\Program Files\Unity2017.1.3p2\Editor\Data\Managed\UnityEngine.dll + diff --git a/UnityEngineAnalyzer.dll b/UnityEngineAnalyzer.dll new file mode 100644 index 0000000000000000000000000000000000000000..48259257645d83b435db170721193144f0175a77 GIT binary patch literal 99840 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
TOY|4g_(FPU zAm};YSI(SeGPNsgkp^E%sPYy(j+73U&f%jqL)?E{p5ThdD@=9ib zBx2lCV}&_Q%qB&iM+5u{%Moqu%`l7AFh`oLTWcmWo7;t9nI>MhFb`EDBQ8vBYE*@} z^~P10ftl54e}=h{jtP@2qy~NoO8`Ht?Zlm|c@XxfY`jM!t7lGq?wouyNBx9-)=;Le zge4gaNo>H|=9_V0n3-fI*#!=cj=cp=$*_p+dnEV|Oy#3&4Ivc0VYP&52vLJ|c}(0$i)N2FweTwSS}P74ciK7TMX9xo_S?#T5HF2 zWDn?seb}L>kRSAF`McVdak@zNxc*vji?LqSp_k5*AnB}pX;(`mTdb->iNq*X^>Z0s z%hGh{*pCUNf&QTBNSV!>=9xp2P+{4?QY=Cvm`mHmi%|a5KZgpwJ_Dka>$NC(AQvdp ze&dYAvXYKWGecFzuv^GwHlrDDCMI``L8&a~t2gKLS73EnpJ~W;+FMv81Eh`YJB`Ej z9;k3}GA9`ygyTh)FCp#iGFQNqricCVnLJ@xTTlCmLp>Yj%OHrQiSB9c4E?B4ZL^RF zLUPDHr8NM8xO#~aWp3ALk-A)}5xu2v9g9s0H1j{`g$=keu1uo3O|?msZKaM~t4BFa z^+}Yi)uok;QMwkLjp>b?8P%~!B={}Lo|B!TA{IiVDx$n*x+2Q8HItek%5OF=%t)rC zZo($9_0r{#PnrR+f?Y!ZX~OBaKmc|L5u}^g7Yl@8`Q8BjY*UT>4$6enQ@)=a-b10mZSh zT-P8=FH<+N5P?GzBYvS&)*?AQhh)AS5@v5WJ%<3lOm$;_a2)EE3=73RIT)6fiPVLH znp0t<=ge6s=t#kY!yP3p6o`I-DLt=Zx(vOO{ARsZT*Nd(LT|p%YQs7{PL7eFv(X; z^fHs4q=xrXY*x700g1=WAq<3mPgv|tdL=_MGcxXj^hk*rq{o2vb%*uB;@0F1_ZOMN zcb+(?u|(O6h&{tZh9hoTzpfKYKZX`(@EZfOTZsncwUbyv_}+zV2*fgsl}#r)TxAng zyonmEGIcks7dcQd$L?Gspk0Le{W&`tu~+I$KlK*cQSpxIMzq^IP!Ga8e}A!bwwUx3 znnL!~Tg81QP)EOrt44GAGKj)K!j`PUyK>0{4u*Jd`4ST*Q8;O$MJLYhv730{{zjjf z3~0D_ByKcG3fIRPL+i%XPvW#P`ua)m4U@CHytzjvDt_+qa}BIAAFBWr{7WfQ}WrG5$|Z2*$RWbs8=+5D6U zcNnl?Six%qMnvkjnE4YHfyo!b*vl{?(^#G~Zd77+j!|Q3z_N>HwvKFSrtaD3eVZ+N=w=$LXr+pG zn+v70bK$2|pYfRLk>UVqpOr=svPa(QrX6HbhVy*6F_sW*0mTw^Df@1osjAHdAp+Dw z70v-s<#6yo`1Olo!b>S8aX)rqPnozj{3C5^lkrkYIa6pz=XytP)HY;kIWHbn?EE%l zH{Vh=&Ee%B^TKLFz52IbZ6Cmjnw+ssUz2CwDs9ZMRBOz!#$jU){Q(;J#ynBMjmg@e zYfKO4hV8cAnh+=^=@55OX@#i=VQ_07PvrSl1LJifRW%njYWQp(J8JcX&2KyTE?*2N zK-3qsbJpNuCw}ZP>q`)C}JJk)_Ctkz@xmbokGx()6gz73{e%K2Y?{5B_2X*|2{G4rdey zY=l2I-uoEN0r7D1NC{_?_=xf3smIhdoC~rQu>fa)d(AP&SKmP|B#8S7-3VmiqAPY>mIhBzmsL6i4Z=8A7cxPHwR+76DPi ziRggecPDy$XWjE%iRT`)9xvZRIwtS}yju>VcN_=&q5>Ef(0(92!3%O2y9`ohaK;01 zOV&l(w~{_pl*-{Z>{XmAQ$X*tLM^<{hdKp(Cd{+C`$|gp0*fKkEaNi;Ub>RHC!}5; z;h11Rybpt$LS9;P7#L}R1Dw_zN9#m*21A55y%6UhbLfd`35PWJAX^5*LF75b)g~Uu zqA?~TmC&^&rIsE={X;mIVL++n$5Dn8tU}mp4g%IW#B#`urQ-rvT_TZsqit)BS~uJL zVGzB9W0>YBhXdX<&I#yinx$e`o2FxU*W2>*TI*}R3@Y&1+yM4DjA9ia!?8IJc(CiLgsKdhKDgIVgyS@IB7QYP&)`~kOQUx zLE2q2j2OjQQsk>m&C^NIv%sK?8kn9N_9Irpb57a|fNdR|TbgrSTBavO2YFA64t48X zWe=G?ABC8E3rc6vj@mI-X^z_*{5)H0Ua4OG^R$r)x<;Z;NS7>$0}#0%4IIIz0;Udt z&fI0GEd!L~WTI>Z$(GvzI)!I-WBiGwIWU|Wx@GmN4 z41v2~@SsGN9ax!s-VSmoTv1)ZJnsYQdQoZF(qzHmKeeq5zlaZ zN(-F~$r0xfJfs7sK+*Ju@`f`%nkG#+DYMY_B7M?0*`mBAEp!cnQhKCl6oJaiO|29pzjM$VO98%*M9F-%rc_E#_?tI`hV zm+I1Kamv-F#2}c`5uFs~Az?--vC)d+EHA_uWu33pV<#c|W zN@{&$S`3qgi|cwuZDjN~;|^g6Xnb)fsJ@SYL1_d84FX#|w$MoytqcwTs$LhQWe~KP zLQ@gHTE-{2-lH(00BbV+{Drq2t;Et;u|!Yxnz%_Ti%)R+*{gy~5na>R!6X ztiJbQHO&>c>S|b{El(H|YxE>)SUW6FtVY17RD`4pR%**jadTvK)0I#DN+?1F%$3sw z;9Co%D*ajjRZcIor2)`;JJj6}{!x?{MUZezX{M7-6jxNuIh3Murk9}EIB*$JB_(Em zc%hAx%+!=@g4RS@)*=KFU)XVS5Hyj}^NqTNOLx}fqPKut&75)bN}Yf8c+?wpwsRvG za!$NxiB4HXJnBiJf`zxv)gLDj^=5qP)AbYOP$I_HF!e3M8rwpNO#7YFRkJ^-(~Bo< zz>a}Z#FtA{>O{Q;B@-KaZt5S7wYFUz6kDtqu0Uwu)?gna6j`AXN7l?5<|r!IYig%OuRR8E%N z)J@Gw1n^YE%51rO8O=K|sTr&XP=-_4meH(U5}Fl|ZlyZ?8M+L;U0l`C*dh(-8{#9JP?{Kl3B_^lg>ltQGuMvOG->>Lr%P>dT~&YSxdY zdUQFaWl<`L#X+e_OCpAeaAZGnGT_ofXzfvqB_022i-y&h!}>IaxLRWGR2NHf%tAbb zRtND+VPz@6zC{?Rdsu7!GNc=A9rl_yH)8&Jl;FJNYK$T=gJbH|20}iJC(a!neNPB9 z=gGH%v5eDPkPk>b5t?fViL+489oj%=k)j&XNu>#;gql(L7$4WwIDj>*jBH)H^+@Q^ zjUZ7r=^2+yqYC0Wl-3NXt4Hk!dRYOclrE=aUXp?onY3!O2S5o8w5Zm$4o52DKh9!- zC}TMUI&v0R9*xFP=qbDsFGE|Z84qd-(bGbVC@m>d303!)3prvzj~KiYLLHr!C=1_E zF;Sb_EP7#}C272LLaE%!nwpQa2ZS6?Urn51lu|zEr)d=DI?6bXTf8o2z%D{*5G}zPdB7kP8i1j!++eY2mMii}=vH6faH7c2=6Q3~ z9Tu*FENW8M=1E=nGK*$i!iA~fK+#t;{Ooes@_5stct`W8G%k3(ZQ^V+4Jc zB1VwWr>>-RG6DPHo5hXw?Xu1Kye4ffC(URZQ9r1~lu@LCdgH=zi0X4sD0-syBCPsW z)va+SOj`2Cg|{Le(d;eyXTd&hy}8?H7qk6(_?$e1UXL4>8ui#!(Fbw{sid);0&O*~ zV6?IerPO@*#UVDqX@y??giA~8`9*H5bhP9=d8N2mh)MWOFj%mUp?>YqI39ZDQ6^X? zXnlLCs~(Cn6ri7iwx}|`c4|99(wfG4_qj^Y61L~h_D5pJ9HS{`-<0JOXCa%=T?aa;KGbU|$kg2Ry zP#ch@)^fDSue4nWg;eG1B(N6-XAiw_^wP0|KlHhK-@fLpe|%WAc*<$@9H)H|g09t! zb}(?|d9clMosRV#i}=;i#v-#{;#YTV5o)^atGj34<#f-!((2(t2&AlI%HvE~>?z4y zsTO&vyS7@_I&W&!E}4S0OKm7tbK3FT?ykgzLM?58)V9)Dl5%WPYIS$*Pn?ywl?yN> zmhxN{Rwl;*w(Tp`l2j`bSMrJ=L8rrA=`2<)9o@Arb*x|4y2R34dO~4@x-8;&)2)JB$u7cvzl4!RUxYf0@yWMBgLfJ0<*vgkO>H zZ`xBW-I?_r-MIW6*LRSjUA3<+_F9~d?hGDQd5aONy@dZAU5~i@THn!{a@KdGl#7f- z>pD`4R(JbON7v@A&2IN=+J)ecI~|>!4k+h4H*f$R$5+b|r$ZOT2yviSs&)Wb2Ou4b zQf}9I47Tc?eNw`wmaCTT8p!h$Qo7FHoLXW=^r9={**$v^|J|f~p!KfWb58dK zr`<-D3nGF-ckMdq^+n4c5+UncM5_P>{Hnmx|MC_^f-1 zcwSfpE_FLrr`lF`&A#7ZCL&KDf+%@Ur>4xePI&^*n8ISq1i326v;GFP6yRr{0wKC* zpGNxC>j z1!U0?cDVMg#pvX7LPk!mb=R)!bkGGoV3Ku%azQeXW=X28LkhZ}560;gX?7FC+M=bY zMeaHjx&c?$Ntf7fK9B=XVtRm*T{9iq92Yzxadek(FWj{%wYa<1-aY$l2m0%h4Z3Gv zi1q%JuJh7{LnzO=9hysCS_b}@eWCkg_v}mS*U7A&eIb=Q|Ob>S!xKe*smu;Pw zrK?*OLkdc{lO5=aL^--QsNU2HAoDE<1|(y)gD>zbGfIf|BA!GLcbzxsub>E-_XT_q ze}M$h^_S`xQ(wVg3oxQ&=t+ryj*`bWbe+eLL%^!FsU_XDO_UV1EufUJkm=G%8U2&t zECd_sE=ZkQo7cHMwY0l-ZFj9lk3)BDR|r+WJrWLjZIc=F+9orGwM`w@ciP#b*+2&U zIgI@cPTLX?X%n=UD>?!b8cl#U9qpL7wOvpQoH(dJBq z9zf^XUjCnb0h4c)r)Ms>3VF|gzE8TGyeyy-YI~{6q>i(K_{5$&rAU=q0;G${V$}Y4(5m-C9NI_Bq)=5&Mx5Th`vh~!o=vTCRCyaw&po{%{}WS_);nd?$c#tT%b*@3G)xtn}&E3Byx zAQ{X^qSc-ua*@(dtXf5YGP;()e##O`B5eC9>Fc&!>ITR>E#JXLFO&Yy zfafkjAnB2K#*D->-L+>h-6C#5X`K~)gB%}rp{y=-)t)6V{DC{%XiT25V7VYdeL7i0utEWW`DqJNB82(rK2c4G&OVF(s7u~@-oSW1j6%uJUP?la8Yk@8XANu} z5rER>^R`Wiu+8VCyj@hxN`&PvbyGbg`L8T}$i z_9VV(!b1*i2Es6_QM&8=G8$=+S29_En~p9)DvPh=g3{Cz;{l+!8ZaQ2Y;#@8$<7;` z<ow9o72YSn8X>@Pe)8z7$A0W%)P>l2{4Hfo=~ zK1G>Yg{_YNXzsnbKU>k6#a=5wlj`h$?fn$w7e2sIs=XhP4lW_yEAhJ!0x}@96B){h zi+Ir8d#~x9K-PV-jbQDBIei0{W%$o}wJIVvrf7^)!M%cS@)d11GK$1rxHh%Afg>Vz zc-pdqM92oP3?q)8II}s5+RxEnjW#(Oy@_RZ`OHDHZ-0+kYs{PFw z;V>X^eH9dw@NaTpTLo5YzOC!UCA-^ue&x;+&)j}wS1aG> zSx2W8-9FqJNjg0Ob^Y8wy}4#cTr2i10B)4<-HU6YSNGyS>S@z1!j9z6$r6v-RpN0+ z@^NSKaaZ#3_T=Lo$;UgBk9Wl%`RlA$yZmuh;xU00f4r8+6|AD44@uqVQR+sIQa5_6 zt1CT8hC50t2gQX0XF<8!^g8}Y_Rjs_jv$cysH-nW7Rxw!3fEU-cu3>O3g4Zt0 zgLnPht~toywvLvz?vwcB@tMNsbQ`86+#)S)@bI*?BPG+?hSl>j&Q=-+ocuC%=|Dpw zR&G#hZR=2~*1CkHw2OhYUSwjYNrWhInfMAo&oFY48AdU5MW>VEqkV2y?s{?E zwJE3jHQiVK219s6v% zEM#HPP}^cD0g#)i=81vUYR{t@+QGc&&R|}EnNSsD{6(PL!6=*?>%2vFxY+WGfFI)x zHi(6faj|KS%t=vDJ_fJbgRx4r4s}J11*_&duQd)X2J2cy%a+#RB{pPoKIb*-aT zHaW`>FKkQp9gf-_Ui;zw>eJpJu5s}0tLDA?OOso@6Zv9s#_P`v=L=^t9`5_R{Z?N3 zsnjyS?@4dtKWsxf>UHTO2S$c)vN?}x*=i0M-+N|fde>GQDK(DY(eE|iXmO-tRe1Y~ zxmbj!E36_6&jRs=t7ABLCgZ4wo8X_m-05rfD9c--D;VpK%;!yv_}%my9Cf@|3-cWJ zWe=t*-!kvDpX#WiUjOPr$BrY{7i!{0Z1qqxc=MedxIjYS2fGhC>R2=A=6nNtz-f4b zT3vdK(k6#k?^L+ITjiR^01 z)Y$i;$-3KDagjP_Gye8FYSY7yc}1MA;_dYw?pEQojyT{@36B`hOus--}P7q5rnC$8hnc zU*76X1=&)$kRO9U)OS7YHQnFpnJXCf7X4}k$F*$rhN@$@SrG?f$k{x5$L_czvn#vn z_MW?T?DV(YdDl&ijp>t(ZJRjKifGbu<=;?mKmWh$ueyHmqI&=GKmE|VGHa2%TQcDZ zL?^D{8#jDQj!U1|kK$1Tiyme*JAGml*-N`RX4LwIz0`=H0lne-H{R`zh@Tj0?m-9LjvYWHm9eakw3<36ZR zT<8Y92#L!p@jRpV($2~_2;>Gh)2?YEY?iCzfN`LLi_NR2IS#nQRE!Kvmy8GEV=Opz z^pSiXZ>8p4E!vLZ(R^OD=<{MVTggOkIj$|d^j=wL!-uq{7kJ5k2KVy#`-1?TkqdofZYd+TV%C?Pn z(k?xW?BB(Q_USwD;onEWv-8-eTcej5`|(oaG+tli&t>&%q4h744+GxrE2+R=wM1`n zz8%%~TuJp4z=I~W{W;Rd0U*YC{~M3a0~}D8xVSjYo6yHedO4i4JHazN?JTEto0Ze6 z{alzU+?eyTxSCBsU^y4y5>dlgk8L~emGw>LMruU$vpAet?79$E+UX%4`gS}IoX)#F zJ$s;H2#7!}a<8h&U9nbhS+{P+LJY!o8u}WfK`ZsutMUudtIl43j$Cx7K9rm)#5-f| zY3IsHYYj_aF%ibAyouY($iyhFcN6^x;DDfgGp3&OeJjKDn6Eay=#7{e3_YP?*U^1wvt$F`sM&Q9hbUb3ZX zA!h_gtVM6ST!OoQDrnNQhC;v%IE1$E`|6NLqgAwmD;B({Z5#kwtl)YhER;;vjwD(O zqJ&0g$9edu2znl*?rx;w_?!ciq^@W=rRZ4vXFor_=l;c^B%8k#kIoUp=K97ojsj$v zVQ$T1W3Ym-0)h{b_67AJtaCD|80q3y%y`&d;8Jsn32vC=szfwPl7m{smsb$;9#6!V+55Y-6m z1ngSDyT|RUjW8KDB89G4JnX=V3gWKpkNdbACJSE?(zkkJ)e4sCSelvZV^DHuyxJ~Z z?M$XnwN;FDr44?A74=?nkv2El=o9{a>FRi6ypMwtF|+z29-ZNVaSRE@%Et{795d)8 z7`Tu@(-^(X7^RVa4!0|9mBsTEd@K+=yi60;CSE?#^^B;JflGT%T7a2Ey(%^WM}PGu^{cXH8wlP{K{D$GhT*+5Ypa=_Qw}7mvdE2 zeMKIRcgvT_(+h zPrSYc`vHDA?ds*zAE>$bk?s&Hc(gc|quaKD<%VnSvpdN4^ z65J=^Ii)R%Tu2_js2%T8I$J0O@WrOR0lxYI@glFjl)Q{AG)3=#F-m2;LPXio=-=mfsHB}Y^?C*~i!E7c95;^T$Y@Ql+-lr3QyKb5C@3@FUO~<)jlBhz{#m0P zjOc9boAk4%uz-d;B_zlkkj0}h(DHrYp<`VXJ@`hO(Pz3}dT_YisRrMA-(^ho?7 zK9=`zCy@Hd`T1{K{NSR6+`&RAvpHVLp2SwhMiNg1i63Lm|r);5+Z zm@&(}E4|6N*>GBE3)z*fIx^dtM%eUem-A4x4fV!+&As_bfY~DVL)LeJEKUZ8S-{II zIYntO8(Hg*kLR;_^w$aIX&2sAzI(0kcrR&b)c~ zRCMTkZ>I)Zz2gupxG@>}I*lJIz!t0ECEkJl%^Ufq>&7j2J2$X|HNPQkS;8CWN0Pi0 za=CmC8vcIk>bSukzZaB127<`J!Jy*RU`o620v(mR|D(LOhRv|#O4E}Tq_w6n=9+wo2Vx5MWs zrquuXe$cb9t>v$7pW5zG+x_WR|DOME0DNqoy_44BuQ?QCm}bMcMBLmQzBT@EeEMvr zoP$W*UoK^Gu<|NlDc(Pe}t@?LATpSyPsqvPFE9GkO)Wb*iKP> zfS-D}QqAQ{&3+-d;54k;9`5(YsuRs>q^EOZ#P^@_@7I5vl+b_wlX1^JCBHu#E2q?G zU#Z+*DDW+8CBNVD{dBH?LrL-X_nQEtGg62cYVN8djqS52#~^4XC3yRCW~KM{rQ;QRFN2T@boZ`UqzpJIv!@1A(j5##kPYelFsuSbLDJDS}s% zSD}<`Z|)!QE$p~pjtdT-3E`apM*8+>9$d@69vqCkE&SlsICU+`lVbXa+amBR#IRgg zyJ@(!S;_yzCfo>ZMhHcf#;EO zKT1x@z1}DA%!>kMklGK(!+0*>VA~9yx1&cr>ULb*t7Cjy<#oU!j)&#|I^TG3Hm@?U zqXd2x8XPq^vK?Y>$AnKDS%uw2ePDm`QePf`%<}d=>xdTj$=K@qrCaTL$L`G#F^{7a?7 IztDdFKhwx-!T LanguageNames.CSharp; protected override DiagnosticAnalyzer CreateAnalyzer() => new ShouldCacheDelegateAnalyzer(); + [Test] + public void DidNotCacheDelegate() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public EventHandler e; + void Update() + { + e += [|OnCallBack|]; + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void DidNotCacheDelegateInAwake() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public EventHandler e; + void Awake() + { + e += [|OnCallBack|]; + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void DidNotCacheDelegate2() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public EventHandler e; + void Update() + { + e += this.[|OnCallBack|]; + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void DidNotCacheDelegate2InAwake() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public EventHandler e; + void Awake() + { + e += this.[|OnCallBack|]; + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + [Test] public void EventDidNotCacheDelegate() { var code = @" using System; +using UnityEngine; -class C +class C : MonoBehaviour { public event EventHandler e; void Update() @@ -52,14 +199,52 @@ private void OnCallBack(object sender, EventArgs e) } } + + [Test] + public void EventDidNotCacheDelegateInAwake() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public event EventHandler e; + void Awake() + { + e += [|OnCallBack|]; + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + [Test] public void EventDidNotCacheDelegate2() { var code = @" using System; +using UnityEngine; -class C +class C : MonoBehaviour { public event EventHandler e; void Update() @@ -87,14 +272,52 @@ private void OnCallBack(object sender, EventArgs e) } } + + [Test] + public void EventDidNotCacheDelegate2InAwake() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public event EventHandler e; + void Awake() + { + e -= [|OnCallBack|]; + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + [Test] public void EventDidCacheDelegate() { var code = @" using System; +using UnityEngine; -class C +class C : MonoBehaviour { public event EventHandler e; private EventHandler m_cachedDelegate = OnCallBack; @@ -130,14 +353,16 @@ private void OnCallBack(object sender, EventArgs e) } + [Test] public void FunctionDidNotCacheDelegate() { var code = @" using System; +using UnityEngine; -class C +class C : MonoBehaviour { public event EventHandler e; void Update() @@ -171,14 +396,56 @@ private void OnCallBack(object sender, EventArgs e) } + [Test] + public void FunctionDidNotCacheDelegateInAwake() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public event EventHandler e; + void Awake() + { + CallDelegate([|OnCallBack|]); + } + + private void CallDelegate(EventHandler handler) + { + + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + [Test] public void FunctionDidCacheDelegate() { var code = @" using System; +using UnityEngine; -class C +class C : MonoBehaviour { public event EventHandler e; private EventHandler m_cachedDelegate; @@ -225,8 +492,9 @@ public void FunctionIsNotDelegate() var code = @" using System; +using UnityEngine; -class C +class C : MonoBehaviour { public event EventHandler e; void Update() @@ -260,6 +528,48 @@ private int ReturnInt() } + [Test] + public void FunctionIsNotDelegateInAwake() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public event EventHandler e; + void Awake() + { + Call([|ReturnInt()|]); + } + + private void Call(int intValue) + { + + } + + private int ReturnInt() + { + return 0; + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] public void FunctionIsNotDelegate2() @@ -267,8 +577,9 @@ public void FunctionIsNotDelegate2() var code = @" using System; +using UnityEngine; -class C +class C : MonoBehaviour { public event EventHandler e; void Update() @@ -301,6 +612,50 @@ private int ReturnInt() } } + [Test] + public void FunctionIsNotDelegate3() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public string s; + private EventHandler m_cachedDelegate = OnCallBack; + + void Intialize() + { + m_cachedDelegate = OnCallBack; + } + + void Update() + { + s += [|ToString()|]; + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] public void FunctionIsNotDelegateAndDidNotCacheDelegate() @@ -308,8 +663,9 @@ public void FunctionIsNotDelegateAndDidNotCacheDelegate() var code = @" using System; +using UnityEngine; -class C +class C : MonoBehaviour { public event EventHandler e; void Update() @@ -346,5 +702,99 @@ private void OnCallBack(object sender, EventArgs e) Assert.Fail("Could not load unit test code"); } } + + + + [Test] + public void FunctionIsNotDelegateAndDidNotCacheDelegateInAwake() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public event EventHandler e; + void Awake() + { + Call(ReturnInt(), [|OnCallBack|]); + } + + private void Call(int intValue, EventHandler h) + { + + } + + private int ReturnInt() + { + return 0; + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void FunctionIsNotDelegateAndDidNotCacheDelegate_IgnoreStaticMethod() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public event EventHandler e; + void Update() + { + Call(ReturnInt(), [|OnCallBack|]); + } + + private void Call(int intValue, EventHandler h) + { + + } + + private int ReturnInt() + { + return 0; + } + + private static void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } } } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/EmptyMonoBehaviourMethods/EmptyMonoBehaviourMethodsCodeFixTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/EmptyMonoBehaviourMethods/EmptyMonoBehaviourMethodsCodeFixTests.cs new file mode 100644 index 0000000..6fd4ec4 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/EmptyMonoBehaviourMethods/EmptyMonoBehaviourMethodsCodeFixTests.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Text; +using NUnit.Core; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.EmptyMonoBehaviourMethods; + +namespace UnityEngineAnalyzer.Test.EmptyMonoBehaviourMethods +{ + [TestFixture] + sealed class EmptyMonoBehaviourMethodsCodeFixTests:CodeFixTestFixture + { + protected override string LanguageName => LanguageNames.CSharp; + protected override CodeFixProvider CreateProvider()=> new EmptyMonoBehaviourMethodsCodeFixer(); + + [Test] + public void RemoveEmptyMonoMethods() + { + const string rawStr = @" +using UnityEngine + +class C : MonoBehaviour +{ + [|void Update() { }|] +}"; + + const string expectStr = @" +using UnityEngine + +class C : MonoBehaviour +{ +}"; + + Document document; + TextSpan span; + TestHelpers.TryGetDocumentAndSpanFromMarkup(rawStr, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); + + TestCodeFix(document, span, expectStr, DiagnosticDescriptors.EmptyMonoBehaviourMethod); + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/GCAlloc/DoNotBoxWhenInvokeAnalyzerTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/GCAlloc/DoNotBoxWhenInvokeAnalyzerTests.cs index 668fbff..2bb8f8f 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/GCAlloc/DoNotBoxWhenInvokeAnalyzerTests.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/GCAlloc/DoNotBoxWhenInvokeAnalyzerTests.cs @@ -1,4 +1,4 @@ -using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Text; using NUnit.Framework; @@ -191,5 +191,72 @@ private void Caller() } } + // For params argument, shoule be some external process: + // if type of params array is value type: won't box; + // if type of params array is reference type: box. + + [Test] + public void NoBoxInParams() + { + var code = @" +class C +{ + private void Method(params int[] argus) + { + } + + private void Caller() + { + int arg = 234; + Method([|arg, 2|]); + } +} +"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, null, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.DoNotBoxWhenInvoke); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void BoxInParams() + { + var code = @" +class C +{ + private void Method(params C[] argus) + { + } + + private void Caller() + { + int arg = 234; + Method([|arg|], [|2|]); + } +} +"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, null, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.DoNotBoxWhenInvoke); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } } } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Generics/EnumShouldManualSetMemberValueTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Generics/EnumShouldManualSetMemberValueTests.cs new file mode 100644 index 0000000..a057255 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Generics/EnumShouldManualSetMemberValueTests.cs @@ -0,0 +1,75 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.Generics; + + +//using Microsoft.CodeAnalysis.Workspaces; + +namespace UnityEngineAnalyzer.Test.GCAlloc +{ + [TestFixture] + sealed class EnumShouldManualSetMemberValueTests : AnalyzerTestFixture + { + + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new EnumShouldManualSetMemberValue(); + + [Test] + public void EnumWithoutManualMemberValue() + { + var code = @" + +enum E +{ + [|a|], + b = 2, +} + +"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, null, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.EnumShouldManualSetMemberValue); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + + [Test] + public void EnumWithManualMemberValue() + { + var code = @" + +enum E +{ + [|a|] = 1, + b = 2, +} +"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, null, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.EnumShouldManualSetMemberValue); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/IL2CPP/UnsealedDerivedClassCodeFixTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/IL2CPP/UnsealedDerivedClassCodeFixTests.cs new file mode 100644 index 0000000..00f9b78 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/IL2CPP/UnsealedDerivedClassCodeFixTests.cs @@ -0,0 +1,63 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Text; +using NUnit.Core; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.IL2CPP; + +namespace UnityEngineAnalyzer.Test.IL2CPP +{ + [TestFixture] + sealed class UnsealedDerivedClassCodeFixTests:CodeFixTestFixture + { + protected override string LanguageName => LanguageNames.CSharp; + protected override CodeFixProvider CreateProvider()=>new UnsealedDerivedClassCodeFixer(); + + [Test] + public void AddSealedModifier() + { + const string rawStr = @" +class C +{ + protected virtual void Method() + { + } +} + + +class D : C +{ + protected override void [|Method|]() + { + } +} + +"; + + const string expectStr = @" +class C +{ + protected virtual void Method() + { + } +} + + +class D : C +{ + protected sealed override void Method() + { + } +} + +"; + + Document document; + TextSpan span; + TestHelpers.TryGetDocumentAndSpanFromMarkup(rawStr, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); + + TestCodeFix(document, span, expectStr, DiagnosticDescriptors.UnsealedDerivedClass); + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/DuplicatedDelegateDetectionTestCases.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/DuplicatedDelegateDetectionTestCases.cs new file mode 100644 index 0000000..eefa77f --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/DuplicatedDelegateDetectionTestCases.cs @@ -0,0 +1,190 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.Language; + +namespace UnityEngineAnalyzer.Test.Language +{ + [TestFixture] + class DuplicatedDelegateDetectionTestCases:AnalyzerTestFixture + { + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer()=>new DuplicatedDelegateDetection(); + + [Test] + public void DetectCustomedDelegateDeclaration() + { + var code = @" +class TestHost +{ + [|private delegate void TestDelegate(string argu1);|] +} +"; + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, + MetadataReferenceHelper.UsingUnityEngine, out document, out span)) + { + // HasDiagnostic(document, span, DiagnosticIDs.UseCommonDelegate); + HasDiagnostic(document, span, DiagnosticIDs.UseCommonDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void DetectUDTDelegateDeclaration() + { + var code = @" +class TestHost +{ + [|private delegate TestHost TestDelegate(TestHost argu1);|] +} +"; + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, + MetadataReferenceHelper.UsingUnityEngine, out document, out span)) + { + // HasDiagnostic(document, span, DiagnosticIDs.UseCommonDelegate); + HasDiagnostic(document, span, DiagnosticIDs.UseCommonDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void DetectUDTDelegateDeclarationWithOutKeyword() + { + var code = @" +class TestHost +{ + [|private delegate TestHost TestDelegate(TestHost argu1, out int arg2);|] +} +"; + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, + MetadataReferenceHelper.UsingUnityEngine, out document, out span)) + { + // HasDiagnostic(document, span, DiagnosticIDs.UseCommonDelegate); + HasDiagnostic(document, span, DiagnosticIDs.UseCommonDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void DetectDelegateAsParam() + { + var code = @" +class TestHost +{ + [|private delegate string TestDelegate(string argu);|] + + private void TestMethod([|TestDelegate argu|]) + { + } +} +"; + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, + MetadataReferenceHelper.UsingUnityEngine, out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.UseCommonDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void NoDetectDelegateAsParam() + { + var code = @" +using System; + +class TestHost +{ + private void TestMethod([|Action argu|]) + { + } +} +"; + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, + MetadataReferenceHelper.UsingUnityEngine, out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.UseCommonDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void DetectDelegateAsVariable() + { + var code = @" +using System; + +class TestHost +{ + [|private delegate void Miaow(string str);|] + + private [|Miaow|] aa; +} +"; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, + MetadataReferenceHelper.UsingUnityEngine, out var document, out var span)) + { + HasDiagnostic(document, span, DiagnosticIDs.UseCommonDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void NoDetectDelegateAsVariable() + { + var code = @" +using System; + +class TestHost +{ + [|private Action aa;|] +} +"; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, + MetadataReferenceHelper.UsingUnityEngine, out var document, out _)) + { + NoDiagnostic(document, DiagnosticIDs.UseCommonDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/LambdaLocalVaribleTestCases.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/LambdaLocalVaribleTestCases.cs new file mode 100644 index 0000000..463db4f --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/LambdaLocalVaribleTestCases.cs @@ -0,0 +1,92 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.Language; + +namespace UnityEngineAnalyzer.Test.Language +{ + [TestFixture] + class LambdaLocalVaribleTestCases:AnalyzerTestFixture + { + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer()=>new LambdaClosureAnalyzer(); + + [Test] + public void ExpressionWithConstructClosure() + { + var code = @" +using System; + +class TestHost +{ + private void QueryLambdaArgu(Action callback) + { + callback(); + } + + public void TestMethod() + { + int a; + QueryLambdaArgu(() => { + int b = 999; + int c = [|a|]; + [|a|].ToString(); + b.ToString(); + b.ToString(); + }); + } +}"; + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, + MetadataReferenceHelper.UsingUnityEngine, out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.LambdaUseLocalVariable); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void ExpressionWithoutConstructClosure() + { + var code = @" +using System; + +class TestHost +{ + private void QueryLambdaArgu(Action callback) + { + callback(); + } + + public void TestMethod() + { + int a; + QueryLambdaArgu([|() => { + int b = 999; + b.ToString(); + b.ToString(); + }|]); + } +}"; + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, + MetadataReferenceHelper.UsingUnityEngine, out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.LambdaUseLocalVariable); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/LogicError/InfiniteRecursiveCallTest.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/LogicError/InfiniteRecursiveCallTest.cs new file mode 100644 index 0000000..00e48fd --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/LogicError/InfiniteRecursiveCallTest.cs @@ -0,0 +1,396 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.Language; +using UnityEngineAnalyzer.LogicError; + + +//using Microsoft.CodeAnalysis.Workspaces; + +namespace UnityEngineAnalyzer.Test.LogicError +{ + [TestFixture] + sealed class InfiniteRecursiveCallTest : AnalyzerTestFixture + { + + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new InfiniteRecursiveCallAnalyzer(); + + [Test] + public void InfiniteRecursiveCall1() + { + var code = @" +class A +{ + void M1() + { + [|M1()|]; + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.InfiniteRecursiveCall); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void InfiniteRecursiveCall2() + { + var code = @" +class A +{ + void M1() + { + [|M2()|]; + } + void M2() + { + M1(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.InfiniteRecursiveCall); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + + [Test] + public void InfiniteRecursiveCall3() + { + var code = @" +class A +{ + void M1() + { + [|M2()|]; + } + void M2() + { + M3(); + } + void M3() + { + M1(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.InfiniteRecursiveCall); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void InfiniteRecursiveCallDouble() + { + var code = @" +class A +{ + void M1() + { + [|M2()|]; + } + void M2() + { + M1(); + } + void M3() + { + [|M3()|]; + } + +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.InfiniteRecursiveCall); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void InfiniteRecursiveCallWithBranch1() + { + var code = @" +class A +{ + private bool m_bool; + void M1() + { + if(m_bool) + { + [|M1()|]; + } + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.InfiniteRecursiveCall); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + + [Test] + public void InfiniteRecursiveCallWithBranch2() + { + var code = @" +class A +{ + private bool m_bool; + void M1() + { + switch(m_bool) + { + default: + [|M1()|]; + break; + } + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.InfiniteRecursiveCall); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void InfiniteRecursiveCallWithBranch3() + { + var code = @" +class A +{ + private bool m_bool; + void M1() + { + switch(m_bool) + { + default: + [|M1()|]; + break; + } + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.InfiniteRecursiveCall); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void InfiniteRecursiveCallWithBranch4() + { + var code = @" +class A +{ + private bool m_bool; + void M1() + { + int[] a = new int[3]; + foreach(var oneInt in a) + { + [|M1()|]; + } + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.InfiniteRecursiveCall); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void InfiniteRecursiveCallWithBranch5() + { + var code = @" +class A +{ + private bool m_bool; + void M1() + { + for(int i = 0; i < 3; ++i) + { + [|M1()|]; + } + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.InfiniteRecursiveCall); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void InfiniteRecursiveCallWithBranch6() + { + var code = @" +class A +{ + private bool m_bool; + void M1() + { + while(true) + { + [|M1()|]; + } + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.InfiniteRecursiveCall); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void InfiniteRecursiveCallWithBranch7() + { + var code = @" +class A +{ + private bool m_bool; + void M1() + { + return; + [|M1()|]; + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.InfiniteRecursiveCall); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void NotInfiniteRecursiveCall() + { + var code = @" +class A +{ + private bool m_bool; + void M1() + { + [|M2()|]; + } + + void M2() {} +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.InfiniteRecursiveCall); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/MetadataReferenceHelper.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/MetadataReferenceHelper.cs index 7f4f5fc..35aafdf 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/MetadataReferenceHelper.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/MetadataReferenceHelper.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.CodeAnalysis; using System.Collections.Immutable; using System.IO; @@ -14,7 +14,7 @@ static class MetadataReferenceHelper private static MetadataReference GetUnityMetadataReference() { - var unityEnginePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), @"Unity\Editor\Data\Managed", "UnityEngine.dll"); + var unityEnginePath = Path.Combine(@"D:\Program Files", @"Unity2017.1.3p2\Editor\Data\Managed", "UnityEngine.dll"); return MetadataReference.CreateFromFile(unityEnginePath); } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj index a0d6603..cc10cc7 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj @@ -34,6 +34,9 @@ false + + ..\..\packages\JetBrains.dotMemoryUnit.3.0.20171219.105559\lib\net35\dotMemory.Unit.dll + ..\..\packages\Microsoft.CodeAnalysis.Common.1.0.0\lib\net45\Microsoft.CodeAnalysis.dll True @@ -124,6 +127,9 @@ false + + ..\..\..\..\..\..\..\..\Program Files\Unity2017.1.3p2\Editor\Data\Managed\UnityEngine.dll + @@ -135,15 +141,21 @@ + + + + + + @@ -155,12 +167,6 @@ - - - {BFB2BB34-FED8-48CC-9B83-A6E38BA5666C} - UnityEngineAnalyzer - - @@ -168,7 +174,12 @@ - + + + {bfb2bb34-fed8-48cc-9b83-a6e38ba5666c} + UnityEngineAnalyzer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Use common generic delegates instead of customed delegate can decrease code size. + + + Use {0} replace this customed declaration delegate. + + + Use Action or Func Delegate replace {0} delegate. + + + Use Action or Func Delegate replace {0} delegate. + + + Use Action and Func delegates replace customed delegates. + + diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateAnalyzer.cs index af15691..160988e 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateAnalyzer.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateAnalyzer.cs @@ -1,7 +1,9 @@ -using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; +using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -14,21 +16,45 @@ public sealed class ShouldCacheDelegateAnalyzer : DiagnosticAnalyzer public override void Initialize(AnalysisContext context) { - context.RegisterSyntaxNodeAction(AnalyzeAssignmentNode, SyntaxKind.AddAssignmentExpression); - context.RegisterSyntaxNodeAction(AnalyzeAssignmentNode, SyntaxKind.SubtractAssignmentExpression); - context.RegisterSyntaxNodeAction(AnalyzeInvocationNode, SyntaxKind.InvocationExpression); + context.RegisterSyntaxNodeAction(AnalyzeClassSyntax, SyntaxKind.ClassDeclaration); } - private static void AnalyzeAssignmentNode(SyntaxNodeAnalysisContext context) + public static void AnalyzeClassSyntax(SyntaxNodeAnalysisContext context) { - var checkSyntax = context.Node; + var monoBehaviourInfo = new MonoBehaviourInfo(context); - foreach (var oneIdSyntax in checkSyntax.DescendantNodes().OfType()) + var searched = new Dictionary(); + monoBehaviourInfo.ForEachUpdateMethod((updateMethod) => + { + AnalyzeAssignmentNode(context, updateMethod); + AnalyzeInvocationNode(context, updateMethod); + }); + } + + public static event Action e; + private static void AnalyzeAssignmentNode(SyntaxNodeAnalysisContext context, MethodDeclarationSyntax method) + { + e += ShouldCacheDelegateAnalyzer_e; + e -= ShouldCacheDelegateAnalyzer_e; + foreach (var oneAssign in method.DescendantNodes().OfType()) + { + AnalyzeAddRemoveNode(context, oneAssign); + } + } + + private static void ShouldCacheDelegateAnalyzer_e(int obj) + { + throw new NotImplementedException(); + } + + private static void AnalyzeAddRemoveNode(SyntaxNodeAnalysisContext context, ExpressionSyntax syntax) + { + foreach (var oneIdSyntax in syntax.ChildNodes().OfType()) { var oneIdSymbol = context.SemanticModel.GetSymbolInfo(oneIdSyntax); if (oneIdSymbol.Symbol != null) { - if (oneIdSymbol.Symbol is IMethodSymbol) + if (oneIdSymbol.Symbol is IMethodSymbol methodSymbol && !methodSymbol.IsStatic) { var diagnostic = Diagnostic.Create(DiagnosticDescriptors.ShouldCacheDelegate, oneIdSyntax.GetLocation(), oneIdSyntax.ToString()); @@ -36,36 +62,51 @@ private static void AnalyzeAssignmentNode(SyntaxNodeAnalysisContext context) } } } - } - - private static void AnalyzeInvocationNode(SyntaxNodeAnalysisContext context) - { - var checkSyntax = context.Node as InvocationExpressionSyntax; - if(null == checkSyntax) + foreach (var oneAccessSyntax in syntax.ChildNodes().OfType()) { - return; + foreach (var oneIdSyntax in oneAccessSyntax.ChildNodes().OfType()) + { + var oneIdSymbol = context.SemanticModel.GetSymbolInfo(oneIdSyntax); + if (oneIdSymbol.Symbol != null) + { + if (oneIdSymbol.Symbol is IMethodSymbol methodSymbol && !methodSymbol.IsStatic) + { + var diagnostic = Diagnostic.Create(DiagnosticDescriptors.ShouldCacheDelegate, + oneIdSyntax.GetLocation(), oneIdSyntax.ToString()); + context.ReportDiagnostic(diagnostic); + } + } + } } + } - var argumentListSyntax = checkSyntax.ChildNodes().OfType(); - foreach (var oneArgListSyntax in argumentListSyntax) + + private static void AnalyzeInvocationNode(SyntaxNodeAnalysisContext context, MethodDeclarationSyntax method) + { + foreach(var invoke in method.DescendantNodes().OfType()) { - foreach(var oneArgSyntax in oneArgListSyntax.ChildNodes().OfType()) + var argumentListSyntax = invoke.ChildNodes().OfType(); + foreach (var oneArgListSyntax in argumentListSyntax) { - foreach (var oneIdSyntax in oneArgSyntax.ChildNodes().OfType()) + foreach (var oneArgSyntax in oneArgListSyntax.ChildNodes().OfType()) { - var oneIdSymbol = context.SemanticModel.GetSymbolInfo(oneIdSyntax); - if (oneIdSymbol.Symbol != null) + foreach (var oneIdSyntax in oneArgSyntax.ChildNodes().OfType()) { - if (oneIdSymbol.Symbol is IMethodSymbol) + var oneIdSymbol = context.SemanticModel.GetSymbolInfo(oneIdSyntax); + if (oneIdSymbol.Symbol != null) { - var diagnostic = Diagnostic.Create(DiagnosticDescriptors.ShouldCacheDelegate, - oneIdSyntax.GetLocation(), oneIdSyntax.ToString()); - context.ReportDiagnostic(diagnostic); + if (oneIdSymbol.Symbol is IMethodSymbol methodSymbol && !methodSymbol.IsStatic) + { + var diagnostic = Diagnostic.Create(DiagnosticDescriptors.ShouldCacheDelegate, + oneIdSyntax.GetLocation(), oneIdSyntax.ToString()); + context.ReportDiagnostic(diagnostic); + } } } } } } + } } } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticCategories.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticCategories.cs index 029cfc1..677e2cd 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticCategories.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticCategories.cs @@ -7,6 +7,7 @@ static class DiagnosticCategories public const string StringMethods = "String Methods"; public const string Miscellaneous = "Miscellaneous"; public const string Performance = "Performance"; + public const string LogicError = "Logic Error"; public const string AOT = "AOT"; } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs index 670b71a..600739b 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs @@ -19,6 +19,8 @@ using UnityEngineAnalyzer.GCAlloc; using UnityEngineAnalyzer.Generics; using UnityEngineAnalyzer.Delegates; +using UnityEngineAnalyzer.Lambda; +using UnityEngineAnalyzer.LogicError; namespace UnityEngineAnalyzer { @@ -49,6 +51,13 @@ public static class DiagnosticDescriptors public static readonly DiagnosticDescriptor StructShouldOverrideGetHashCode; public static readonly DiagnosticDescriptor DoNotUseEnumTypeParameter; public static readonly DiagnosticDescriptor ShouldCacheDelegate; + public static readonly DiagnosticDescriptor InfiniteRecursiveCall; + public static readonly DiagnosticDescriptor InfiniteRecursiveCallRecursive; + public static readonly DiagnosticDescriptor LambdaClosure; + public static readonly DiagnosticDescriptor DuplicateDelegateDetection; + public static readonly DiagnosticDescriptor DuplicateDelegateParamDetection; + public static readonly DiagnosticDescriptor DuplicateDelegateVariableDetection; + public static readonly DiagnosticDescriptor EnumShouldManualSetMemberValue; static DiagnosticDescriptors() { @@ -61,26 +70,57 @@ static DiagnosticDescriptors() UseCompareTag = CreateDiagnosticDescriptor(DiagnosticIDs.UseCompareTag, DiagnosticCategories.GC, DiagnosticSeverity.Warning); UseNonAllocMethods = CreateDiagnosticDescriptor(DiagnosticIDs.PhysicsUseNonAllocMethods, DiagnosticCategories.GC, DiagnosticSeverity.Warning, UnityVersion.UNITY_5_3); CameraMainIsSlow = CreateDiagnosticDescriptor(DiagnosticIDs.CameraMainIsSlow, DiagnosticCategories.GC, DiagnosticSeverity.Warning); - DoNotGCAllocnInUpdate = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotGCAllocInUpdate, DiagnosticCategories.GC, DiagnosticSeverity.Warning); - DoNotGCAllocnInUpdateRecursive = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotGCAllocInUpdate, DiagnosticCategories.GC, DiagnosticSeverity.Warning); + DoNotGCAllocnInUpdate = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotGCAllocInUpdate, nameof(DoNotGCAllocInUpdateResources.MessageFormat), DiagnosticCategories.GC, DiagnosticSeverity.Warning); + DoNotGCAllocnInUpdateRecursive = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotGCAllocInUpdate, nameof(DoNotGCAllocInUpdateResources.MessageFormatRecursive), DiagnosticCategories.GC, DiagnosticSeverity.Warning); DoNotBoxWhenInvoke = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotBoxWhenInvoke, DiagnosticCategories.GC, DiagnosticSeverity.Warning); ShouldCacheDelegate = CreateDiagnosticDescriptor(DiagnosticIDs.ShouldCacheDelegate, DiagnosticCategories.GC, DiagnosticSeverity.Warning); //Performance - DoNotUseFindMethodsInUpdate = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseFindMethodsInUpdate, DiagnosticCategories.Performance, DiagnosticSeverity.Warning); - DoNotUseFindMethodsInUpdateRecursive = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseFindMethodsInUpdate, DiagnosticCategories.Performance, DiagnosticSeverity.Warning); + DoNotUseFindMethodsInUpdate = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseFindMethodsInUpdate, nameof(DoNotUseFindMethodsInUpdateResources.MessageFormat), DiagnosticCategories.Performance, DiagnosticSeverity.Warning); + DoNotUseFindMethodsInUpdateRecursive = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseFindMethodsInUpdate, nameof(DoNotUseFindMethodsInUpdateResources.MessageFormatRecursive), DiagnosticCategories.Performance, DiagnosticSeverity.Warning); DoNotUseForEachInUpdate = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseForEachInUpdate, DiagnosticCategories.Performance, DiagnosticSeverity.Warning, UnityVersion.UNITY_1_0, UnityVersion.UNITY_5_5); UnsealedDerivedClass = CreateDiagnosticDescriptor(DiagnosticIDs.UnsealedDerivedClass, DiagnosticCategories.Performance, DiagnosticSeverity.Warning); InvokeFunctionMissing = CreateDiagnosticDescriptor(DiagnosticIDs.InvokeFunctionMissing, DiagnosticCategories.Performance, DiagnosticSeverity.Warning); DoNotUseStateName = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseStateNameInAnimator, DiagnosticCategories.Performance, DiagnosticSeverity.Warning); DoNotUseStringPropertyNames = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseStringPropertyNamesInMaterial, DiagnosticCategories.Performance, DiagnosticSeverity.Warning); + LambdaClosure = CreateDiagnosticDescriptor( + DiagnosticIDs.LambdaUseLocalVariable, + nameof(LambdaAnalyzerResources.LambdaClosureAnalyzer), + DiagnosticCategories.Performance, + DiagnosticSeverity.Warning); + DuplicateDelegateDetection = CreateDiagnosticDescriptor( + DiagnosticIDs.UseCommonDelegate, + nameof(DuplicatedDelegateDetectionResource.FoundDuplicateDelegate), + DiagnosticCategories.Performance, + DiagnosticSeverity.Warning + ); + DuplicateDelegateParamDetection = CreateDiagnosticDescriptor( + DiagnosticIDs.UseCommonDelegate, + nameof(DuplicatedDelegateDetectionResource.FoundDuplicateDelegateAsParam), + DiagnosticCategories.Performance, + DiagnosticSeverity.Info + ); + DuplicateDelegateVariableDetection = CreateDiagnosticDescriptor( + DiagnosticIDs.UseCommonDelegate, + nameof(DuplicatedDelegateDetectionResource.FoundDuplicateDelegateVariable), + DiagnosticCategories.Performance, + DiagnosticSeverity.Info + ); + + //Logic Error + InfiniteRecursiveCall = CreateDiagnosticDescriptor(DiagnosticIDs.InfiniteRecursiveCall, nameof(InfiniteRecursiveCallResources.MessageFormat), DiagnosticCategories.LogicError, DiagnosticSeverity.Error); + InfiniteRecursiveCallRecursive = CreateDiagnosticDescriptor(DiagnosticIDs.InfiniteRecursiveCall, nameof(InfiniteRecursiveCallResources.MessageFormatRecursive), DiagnosticCategories.LogicError, DiagnosticSeverity.Error); + + + //Miscellaneous EmptyMonoBehaviourMethod = CreateDiagnosticDescriptor(DiagnosticIDs.EmptyMonoBehaviourMethod, DiagnosticCategories.Miscellaneous, DiagnosticSeverity.Warning); - StructShouldImplementIEquatable = CreateDiagnosticDescriptor(DiagnosticIDs.StructShouldImplementIEquatable, DiagnosticCategories.Miscellaneous, DiagnosticSeverity.Warning); - StructShouldOverrideEquals = CreateDiagnosticDescriptor(DiagnosticIDs.StructShouldOverrideEquals, DiagnosticCategories.Miscellaneous, DiagnosticSeverity.Warning); - StructShouldOverrideGetHashCode = CreateDiagnosticDescriptor(DiagnosticIDs.StructShouldOverrideGetHashCode, DiagnosticCategories.Miscellaneous, DiagnosticSeverity.Warning); + StructShouldImplementIEquatable = CreateDiagnosticDescriptor(DiagnosticIDs.StructShouldImplementIEquatable, nameof(StructAnalyzerResources.ShouldImplmentIEquatable), DiagnosticCategories.Miscellaneous, DiagnosticSeverity.Warning); + StructShouldOverrideEquals = CreateDiagnosticDescriptor(DiagnosticIDs.StructShouldOverrideEquals, nameof(StructAnalyzerResources.ShouldOverrideEquals), DiagnosticCategories.Miscellaneous, DiagnosticSeverity.Warning); + StructShouldOverrideGetHashCode = CreateDiagnosticDescriptor(DiagnosticIDs.StructShouldOverrideGetHashCode, nameof(StructAnalyzerResources.ShouldOverrideGetHashCode), DiagnosticCategories.Miscellaneous, DiagnosticSeverity.Warning); DoNotUseEnumTypeParameter = CreateDiagnosticDescriptor(DiagnosticIDs.DoNoUseEnumTypeParameter, DiagnosticCategories.Miscellaneous, DiagnosticSeverity.Warning); + EnumShouldManualSetMemberValue = CreateDiagnosticDescriptor(DiagnosticIDs.EnumShouldManualSetMemberValue, DiagnosticCategories.Miscellaneous, DiagnosticSeverity.Info); //** AOT ** DoNotUseRemoting = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseRemoting, DiagnosticCategories.AOT, DiagnosticSeverity.Info); @@ -103,6 +143,21 @@ private static DiagnosticDescriptor CreateDiagnosticDescriptor(string id, str description: new LocalizableResourceString("Description", resourceManager, typeof(T))); } + private static DiagnosticDescriptor CreateDiagnosticDescriptor(string id, string messageFormat, + string category, DiagnosticSeverity severity, UnityVersion first = UnityVersion.UNITY_1_0, UnityVersion latest = UnityVersion.LATEST, bool isEnabledByDefault = true) + { + var resourceManager = new ResourceManager(typeof(T)); + + return new DiagnosticDescriptor( + id: id, + title: new LocalizableResourceString("Title", resourceManager, typeof(T)), + messageFormat: new LocalizableResourceString(messageFormat, resourceManager, typeof(T)), + category: category, + defaultSeverity: severity, + isEnabledByDefault: isEnabledByDefault, + customTags: CreateUnityVersionInfo(first, latest), + description: new LocalizableResourceString("Description", resourceManager, typeof(T))); + } private static string[] CreateUnityVersionInfo(UnityVersion start, UnityVersion end) { return new string[] { Enum.GetName(typeof(UnityVersion), start), Enum.GetName(typeof(UnityVersion), end) }; diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs index d8b4143..9a1bafa 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs @@ -1,4 +1,4 @@ - + namespace UnityEngineAnalyzer { public static class DiagnosticIDs @@ -17,17 +17,25 @@ public static class DiagnosticIDs public const string CameraMainIsSlow = "UEA0012"; public const string PhysicsUseNonAllocMethods = "UEA0013"; public const string DoNotGCAllocInUpdate = "UEA0014"; - + + // language analysis + public const string StructShouldImplementIEquatable = "UCS0001"; + public const string StructShouldOverrideEquals = "UCS0002"; + public const string StructShouldOverrideGetHashCode = "UCS0003"; + public const string DoNotBoxWhenInvoke = "UCS0004"; + public const string DoNoUseEnumTypeParameter = "UCS0005"; + public const string ShouldCacheDelegate = "UCS0006"; + public const string LambdaUseLocalVariable = "UCS0007"; + public const string UseCommonDelegate = "UCS0008"; + public const string EnumShouldManualSetMemberValue = "UCS0009"; + + // logic error analysis + public const string InfiniteRecursiveCall = "ULE0001"; + //NOTES: These should probably be on their own analyzer - as they are not specific to Unity public const string DoNotUseRemoting = "AOT0001"; public const string DoNotUseReflectionEmit = "AOT0002"; public const string TypeGetType = "AOT0003"; - public const string StructShouldImplementIEquatable = "AOT0004"; - public const string StructShouldOverrideEquals = "AOT0005"; - public const string StructShouldOverrideGetHashCode = "AOT0006"; - public const string DoNotBoxWhenInvoke = "AOT0007"; - public const string DoNoUseEnumTypeParameter = "AOT0008"; - public const string ShouldCacheDelegate = "AOT0009"; } } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/EmptyMonoBehaviourMethods/EmptyMonoBehaviourMethodsAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/EmptyMonoBehaviourMethods/EmptyMonoBehaviourMethodsAnalyzer.cs index eb742f5..731a8a2 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/EmptyMonoBehaviourMethods/EmptyMonoBehaviourMethodsAnalyzer.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/EmptyMonoBehaviourMethods/EmptyMonoBehaviourMethodsAnalyzer.cs @@ -1,4 +1,4 @@ -using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; @@ -81,7 +81,7 @@ public override void Initialize(AnalysisContext context) //NOTE: It might be more officient to find classes and then determine if they are a MonoBehaviour rather than look at every method } - private static async void AnalyzeSymbol(SymbolAnalysisContext context) + private static void AnalyzeSymbol(SymbolAnalysisContext context) { // retrieve method symbol var methodSymbol = context.Symbol as IMethodSymbol; @@ -91,7 +91,7 @@ private static async void AnalyzeSymbol(SymbolAnalysisContext context) if (methodSymbol.DeclaringSyntaxReferences.Length != 1) { return; } // retrieve the method syntax from the method symbol - var methodSyntax = await methodSymbol.DeclaringSyntaxReferences[0].GetSyntaxAsync() as MethodDeclarationSyntax; + var methodSyntax = methodSymbol.DeclaringSyntaxReferences[0].GetSyntax() as MethodDeclarationSyntax; // from the method syntax, check if there is a body and if there are statements in it if (methodSyntax?.Body?.Statements.Any() ?? true) { return; } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/EmptyMonoBehaviourMethods/EmptyMonoBehaviourMethodsCodeFixer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/EmptyMonoBehaviourMethods/EmptyMonoBehaviourMethodsCodeFixer.cs new file mode 100644 index 0000000..f5c7900 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/EmptyMonoBehaviourMethods/EmptyMonoBehaviourMethodsCodeFixer.cs @@ -0,0 +1,54 @@ +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace UnityEngineAnalyzer.EmptyMonoBehaviourMethods +{ + [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(EmptyMonoBehaviourMethodsCodeFixer)), Shared] + public class EmptyMonoBehaviourMethodsCodeFixer : CodeFixProvider + { + private const string _title = "Remove Method"; + + public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(DiagnosticIDs.EmptyMonoBehaviourMethod); + + public override FixAllProvider GetFixAllProvider() + { + return WellKnownFixAllProviders.BatchFixer; + } + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + + var diagnostic = context.Diagnostics.First(); + var diagnosticSpan = diagnostic.Location.SourceSpan; + + var declaration = root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf() + .OfType().First(); + + context.RegisterCodeFix( + CodeAction.Create( + _title, + c => RemveMethod(context.Document, declaration, c), + _title + ), + diagnostic + ); + } + + private async Task RemveMethod(Document document, MethodDeclarationSyntax declaration, CancellationToken cancellationToken) + { + var removedParent = declaration.Parent.RemoveNode(declaration, SyntaxRemoveOptions.KeepNoTrivia); + var oldRoot = await document.GetSyntaxRootAsync(cancellationToken); + var newRoot = oldRoot.ReplaceNode(declaration.Parent, removedParent); + + return document.WithSyntaxRoot(newRoot); + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeAnalyzer.cs index d35f816..8f85033 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeAnalyzer.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeAnalyzer.cs @@ -1,4 +1,4 @@ -using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; @@ -19,12 +19,8 @@ public override void Initialize(AnalysisContext context) private static void AnalyzeNode(SyntaxNodeAnalysisContext context) { var invocation = context.Node as InvocationExpressionSyntax; - if (invocation == null) - { - return; - } - - if (invocation.ArgumentList == null || invocation.ArgumentList.Arguments == null || invocation.ArgumentList.Arguments.Count == 0) + + if (invocation?.ArgumentList == null || invocation.ArgumentList.Arguments.Count == 0) { return; } @@ -48,12 +44,18 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context) return; } - if(methodSymbol.Parameters == null || methodSymbol.Parameters.Length == 0) { return; } + var lastParameter = methodSymbol.Parameters[methodSymbol.Parameters.Length - 1]; + if (lastParameter.IsParams) + { + if (lastParameter.Type is IArrayTypeSymbol arrSymbol && arrSymbol.ElementType.IsValueType) + return; + } + for(int i = 0; i < methodSymbol.Parameters.Length && i < invocation.ArgumentList.Arguments.Count; ++i) { var oneArgSyntax = invocation.ArgumentList.Arguments[i]; diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/DoNotUseEnumTypeParameterAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/DoNotUseEnumTypeParameterAnalyzer.cs index 35e66ef..0423e07 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/DoNotUseEnumTypeParameterAnalyzer.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/DoNotUseEnumTypeParameterAnalyzer.cs @@ -1,4 +1,4 @@ -using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; @@ -37,8 +37,10 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context) checkedIdSyntax.Add(oneIdSyntax); var oneIdSymbolInfo = context.SemanticModel.GetSymbolInfo(oneIdSyntax); - var oneIdSymbol = oneIdSymbolInfo.Symbol as INamedTypeSymbol; - if (oneIdSymbol.BaseType.Name == "Enum" && + var oneIdSymbolraw = oneIdSymbolInfo.Symbol as INamedTypeSymbol; + if (oneIdSymbolraw is INamedTypeSymbol oneIdSymbol && + oneIdSymbol.BaseType != null && + oneIdSymbol.BaseType.Name == "Enum" && oneIdSymbol.BaseType.ContainingNamespace.Name == "System") { var diagnostic = Diagnostic.Create(DiagnosticDescriptors.DoNotUseEnumTypeParameter, diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/EnumShouldManualSetMemberValue.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/EnumShouldManualSetMemberValue.cs new file mode 100644 index 0000000..66560eb --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/EnumShouldManualSetMemberValue.cs @@ -0,0 +1,48 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using System.Collections.Immutable; +using System.Linq; + +namespace UnityEngineAnalyzer.Generics +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class EnumShouldManualSetMemberValue : DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.EnumShouldManualSetMemberValue); + + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.EnumMemberDeclaration); + } + + + private static void AnalyzeNode(SyntaxNodeAnalysisContext context) + { + var targetSyntax = context.Node as EnumMemberDeclarationSyntax; + if (targetSyntax == null) + { + return; + } + + bool hasEqualClause = false; + foreach(var oneSyntax in targetSyntax.DescendantNodes().OfType()) + { + if(oneSyntax != null) + { + hasEqualClause = true; + break; + } + } + + if(!hasEqualClause) + { + var diagnostic = Diagnostic.Create(DiagnosticDescriptors.EnumShouldManualSetMemberValue, + targetSyntax.GetLocation(), targetSyntax.ToString()); + context.ReportDiagnostic(diagnostic); + } + + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/EnumShouldManualSetMemberValueResource.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/EnumShouldManualSetMemberValueResource.Designer.cs new file mode 100644 index 0000000..6e1ba57 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/EnumShouldManualSetMemberValueResource.Designer.cs @@ -0,0 +1,91 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace UnityEngineAnalyzer.Generics { + using System; + using System.Reflection; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class EnumShouldManualSetMemberValueResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal EnumShouldManualSetMemberValueResource() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityEngineAnalyzer.Generics.EnumShouldManualSetMemberValueResource", typeof(EnumShouldManualSetMemberValueResource).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 使用此强类型资源类,为所有资源查找 + /// 重写当前线程的 CurrentUICulture 属性。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 If this enum is used for prefab or excel, you must manually set enum member value. 的本地化字符串。 + /// + internal static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + /// + /// 查找类似 enum member "{0}" should be manually set value 的本地化字符串。 + /// + internal static string MessageFormat { + get { + return ResourceManager.GetString("MessageFormat", resourceCulture); + } + } + + /// + /// 查找类似 Enum member value required 的本地化字符串。 + /// + internal static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/EnumShouldManualSetMemberValueResource.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/EnumShouldManualSetMemberValueResource.resx new file mode 100644 index 0000000..837e742 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/EnumShouldManualSetMemberValueResource.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + If this enum is used for prefab or excel, you must manually set enum member value. + An optional longer localizable description of the diagnostic. + + + enum member "{0}" should be manually set value + The format-able message the diagnostic displays. + + + Enum member value required + The title of the diagnostic. + + \ No newline at end of file diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/IL2CPP/UnsealedDerivedClassCodeFixer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/IL2CPP/UnsealedDerivedClassCodeFixer.cs new file mode 100644 index 0000000..32b8144 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/IL2CPP/UnsealedDerivedClassCodeFixer.cs @@ -0,0 +1,65 @@ +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace UnityEngineAnalyzer.IL2CPP +{ + [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(UnsealedDerivedClassCodeFixer)), Shared] + public class UnsealedDerivedClassCodeFixer:CodeFixProvider + { + private const string _title = "Add sealed Modifier"; + + public override ImmutableArray FixableDiagnosticIds =>ImmutableArray.Create(DiagnosticIDs.UnsealedDerivedClass); + + public override FixAllProvider GetFixAllProvider() + { + return WellKnownFixAllProviders.BatchFixer; + } + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + + var diagnostic = context.Diagnostics.First(); + var diagnosticSpan = diagnostic.Location.SourceSpan; + + var declarations = root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf() + .OfType().First(); + + context.RegisterCodeFix( + CodeAction.Create( + _title, + c=>AddModifier(context.Document, declarations, c), + _title + ), + diagnostic + ); + } + + private async Task AddModifier(Document contextDocument, MethodDeclarationSyntax declaration, CancellationToken cancellationToken) + { + var firstToken = declaration.GetFirstToken(); + var leadingTrivia = firstToken.LeadingTrivia; + //var trimmedDeclaration = + // declaration.ReplaceToken(firstToken, firstToken.WithLeadingTrivia(SyntaxTriviaList.Empty)); + + var sealedToken = SyntaxFactory.Token(leadingTrivia, SyntaxKind.SealedKeyword, + SyntaxFactory.TriviaList(SyntaxFactory.ElasticMarker)); + + var newModifiers = declaration.Modifiers.Insert(declaration.Modifiers.Count >= 1 ? 1 : 0, sealedToken); + var newDecolation = declaration.WithModifiers(newModifiers); + + var oldRoot = await contextDocument.GetSyntaxRootAsync(cancellationToken); + var newRoot = oldRoot.ReplaceNode(declaration, newDecolation); + + return contextDocument.WithSyntaxRoot(newRoot); + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/IgnoringWithCommit.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/IgnoringWithCommit.cs new file mode 100644 index 0000000..c169817 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/IgnoringWithCommit.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; + +namespace UnityEngineAnalyzer +{ + internal static class IgnoringWithCommit + { + private readonly static Regex IgnoreFormatRegex = new Regex(@"//\s*Ignore CA:\s*(?[0-9a-zA-Z]+)(,\s*(?[0-9a-zA-Z]+))"); + + //public static bool ShouldIgnore(SyntaxNode node, string checkId) + //{ + + //} + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Lambda/LambdaAnalyzerResources.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Lambda/LambdaAnalyzerResources.Designer.cs new file mode 100644 index 0000000..14f300b --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Lambda/LambdaAnalyzerResources.Designer.cs @@ -0,0 +1,91 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace UnityEngineAnalyzer.Lambda { + using System; + using System.Reflection; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class LambdaAnalyzerResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal LambdaAnalyzerResources() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityEngineAnalyzer.Lambda.LambdaAnalyzerResources", typeof(LambdaAnalyzerResources).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 使用此强类型资源类,为所有资源查找 + /// 重写当前线程的 CurrentUICulture 属性。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 A reference of variables in outter scope of a lambda expression will make a gerenation of a closure. It's better to avoid this in order to decrease code size, memory and gc cost. 的本地化字符串。 + /// + internal static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + /// + /// 查找类似 A outter variable is found in a lambda expression, which may cause a closure object allocated. 的本地化字符串。 + /// + internal static string LambdaClosureAnalyzer { + get { + return ResourceManager.GetString("LambdaClosureAnalyzer", resourceCulture); + } + } + + /// + /// 查找类似 Check local variable in a lambda expression. 的本地化字符串。 + /// + internal static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Lambda/LambdaAnalyzerResources.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/Lambda/LambdaAnalyzerResources.resx new file mode 100644 index 0000000..363907a --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Lambda/LambdaAnalyzerResources.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + A reference of variables in outter scope of a lambda expression will make a gerenation of a closure. It's better to avoid this in order to decrease code size, memory and gc cost. + + + A outter variable is found in a lambda expression, which may cause a closure object allocated. + + + Check local variable in a lambda expression. + + diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Lambda/LambdaClosureAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Lambda/LambdaClosureAnalyzer.cs new file mode 100644 index 0000000..7a7fd2e --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Lambda/LambdaClosureAnalyzer.cs @@ -0,0 +1,40 @@ +using System.Collections.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace UnityEngineAnalyzer.Language +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class LambdaClosureAnalyzer : DiagnosticAnalyzer + { + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalysisLambda, SyntaxKind.SimpleLambdaExpression, SyntaxKind.ParenthesizedLambdaExpression); + } + + public override ImmutableArray SupportedDiagnostics => + ImmutableArray.Create(DiagnosticDescriptors.LambdaClosure); + + private void AnalysisLambda(SyntaxNodeAnalysisContext context) + { + var lambdaNode = (LambdaExpressionSyntax) context.Node; + var des = context.SemanticModel.GetSymbolInfo(lambdaNode).Symbol; + + var childrenNodes = + from node in lambdaNode.DescendantNodes().OfType() + let symbol = context.SemanticModel.GetSymbolInfo(node).Symbol + where symbol.Kind == SymbolKind.Local && symbol.ContainingSymbol != des + select node; + + foreach (var node in childrenNodes) + { + var diagnostic = + Diagnostic.Create(SupportedDiagnostics.First(), node.GetLocation(), node.Identifier.Text); + context.ReportDiagnostic(diagnostic); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/LogicError/InfiniteRecursiveCallAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/LogicError/InfiniteRecursiveCallAnalyzer.cs new file mode 100644 index 0000000..66bcba8 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/LogicError/InfiniteRecursiveCallAnalyzer.cs @@ -0,0 +1,234 @@ +using System; +using System.Collections.Generic; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; + +namespace UnityEngineAnalyzer.LogicError +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + /// InfiniteRecursiveCallAnalyzer currently is conservative, + /// which means, if the recursive call is inside a branch (if/switch), + /// then this analyzer will NOT report diagnose. + public sealed class InfiniteRecursiveCallAnalyzer : DiagnosticAnalyzer + { + + private Dictionary _directCallers; + private Dictionary _indirectCallers; + + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create( + DiagnosticDescriptors.InfiniteRecursiveCall, + DiagnosticDescriptors.InfiniteRecursiveCallRecursive); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeSymbol, SyntaxKind.ClassDeclaration); + } + + private void AnalyzeSymbol(SyntaxNodeAnalysisContext context) + { + var searched = new Dictionary(); + _directCallers = new Dictionary(); + _indirectCallers = new Dictionary(); + + List reported = new List(); + + foreach (var oneMethodDeclaration in context.Node.ChildNodes().OfType()) + { + var methodSymbol = context.SemanticModel.GetDeclaredSymbol(oneMethodDeclaration) as IMethodSymbol; + + + var results = SearchForTargetExpression(context, methodSymbol, oneMethodDeclaration, searched, true); + + foreach (var oneResult in results) + { + if (!reported.Contains((oneResult))) + { + if (_directCallers.ContainsKey(oneResult)) + { + var diagnostic = Diagnostic.Create(DiagnosticDescriptors.InfiniteRecursiveCall, + oneResult.GetLocation(), methodSymbol.ContainingType.Name, oneMethodDeclaration.Identifier); + context.ReportDiagnostic(diagnostic); + reported.Add(oneResult); + + } + else if (_indirectCallers.ContainsKey(oneResult)) + { + var endPoint = _indirectCallers[oneResult]; + + var diagnostic = Diagnostic.Create(DiagnosticDescriptors.InfiniteRecursiveCallRecursive, + oneResult.GetLocation(), methodSymbol.ContainingType.Name, oneMethodDeclaration.Identifier, oneResult); + context.ReportDiagnostic(diagnostic); + reported.Add(oneResult); + } + } + + } + } + + } + + + //TODO: Try to simplify this currMethodSyntax - it's very hard to follow + private IEnumerable SearchForTargetExpression(SyntaxNodeAnalysisContext context, + IMethodSymbol checkMethodSymbol, + MethodDeclarationSyntax currMethodSyntax, IDictionary searchedSymbol, bool isRoot) + { + foreach (var oneTargetExp in currMethodSyntax.DescendantNodes().OfType()) + { + /* + var oneTargetIdentifierExps = oneTargetExp.DescendantNodes().OfType(); + foreach(var oneTargetIdentifierExp in oneTargetIdentifierExps) + { + } + */ + + bool isContainedByBranch = false; + + SyntaxNode parent = oneTargetExp; + while (parent != currMethodSyntax) + { + if (parent is IfStatementSyntax || parent is SwitchStatementSyntax || parent is ForStatementSyntax || + parent is ForEachStatementSyntax || parent is WhileStatementSyntax) + { + isContainedByBranch = true; + break; + } + + parent = parent.Parent; + } + + if (isContainedByBranch) + { + continue; + } + + bool hasPrecedingReturn = false; + + foreach (var oneReturnSyntax in currMethodSyntax.DescendantNodes().OfType()) + { + if (oneReturnSyntax.SpanStart < oneTargetExp.SpanStart) + { + hasPrecedingReturn = true; + break; + } + } + if (hasPrecedingReturn) + { + continue; + } + + SymbolInfo oneSymbolInfo; + if (!context.TryGetSymbolInfo(oneTargetExp, out oneSymbolInfo)) + { + continue; + } + + var targetSymbol = oneSymbolInfo.Symbol as IMethodSymbol; + if (targetSymbol != null) + { + if (searchedSymbol.ContainsKey(targetSymbol)) + { + if (searchedSymbol[targetSymbol]) + { + yield return (ExpressionSyntax)oneTargetExp; + } + } + else + { + if (targetSymbol == checkMethodSymbol) + { + searchedSymbol.Add(targetSymbol, true); + if (isRoot) + { + _directCallers.Add(oneTargetExp, oneTargetExp); + } + yield return oneTargetExp; + } + } + } + } + + + var invocationExps = currMethodSyntax.DescendantNodes().OfType(); + foreach (var oneInvocationExp in invocationExps) + { + SymbolInfo oneSymbolInfo; + if (!context.TryGetSymbolInfo(oneInvocationExp, out oneSymbolInfo)) + { + continue; + } + + + bool isContainedByBranch = false; + SyntaxNode parent = oneInvocationExp; + while (parent != currMethodSyntax) + { + if (parent is IfStatementSyntax || parent is SwitchStatementSyntax) + { + isContainedByBranch = true; + break; + } + + parent = parent.Parent; + } + if (isContainedByBranch) + { + continue; + } + + var methodSymbol = oneSymbolInfo.Symbol as IMethodSymbol; + + if (methodSymbol != null) + { + if (searchedSymbol.ContainsKey(methodSymbol)) + { + if (searchedSymbol[methodSymbol]) + { + yield return oneInvocationExp; + } + } + else + { + var methodDeclarations = methodSymbol.DeclaringSyntaxReferences; + searchedSymbol.Add(methodSymbol, false); //let's assume there won't be any calls + + foreach (var methodDeclaration in methodDeclarations) + { + var theMethodSyntax = methodDeclaration.GetSyntax() as MethodDeclarationSyntax; + + if (theMethodSyntax != null) + { + var childResults = SearchForTargetExpression(context, checkMethodSymbol, theMethodSyntax, searchedSymbol, false); + + if (childResults != null && childResults.Any()) + { + searchedSymbol[methodSymbol] = true; //update the searched dictionary with new info + + if (isRoot) + { + _indirectCallers.Add(oneInvocationExp, childResults.First()); + } + + yield return oneInvocationExp; + break; + } + } + } + } + } + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/LogicError/InfiniteRecursiveCallResources.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/LogicError/InfiniteRecursiveCallResources.Designer.cs new file mode 100644 index 0000000..0da4656 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/LogicError/InfiniteRecursiveCallResources.Designer.cs @@ -0,0 +1,100 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace UnityEngineAnalyzer.LogicError { + using System; + using System.Reflection; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class InfiniteRecursiveCallResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal InfiniteRecursiveCallResources() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityEngineAnalyzer.LogicError.InfiniteRecursiveCallResources", typeof(InfiniteRecursiveCallResources).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 使用此强类型资源类,为所有资源查找 + /// 重写当前线程的 CurrentUICulture 属性。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 Infinite recursive loop detected 的本地化字符串。 + /// + internal static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + /// + /// 查找类似 The method {0}.{1}() calls {1}() again. This is an infinite recursive call. 的本地化字符串。 + /// + internal static string MessageFormat { + get { + return ResourceManager.GetString("MessageFormat", resourceCulture); + } + } + + /// + /// 查找类似 The method {0}.{1}() calls {2} which eventually calls {1}() again. This is an infinite recursive call. 的本地化字符串。 + /// + internal static string MessageFormatRecursive { + get { + return ResourceManager.GetString("MessageFormatRecursive", resourceCulture); + } + } + + /// + /// 查找类似 Infinite recursive loop 的本地化字符串。 + /// + internal static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/LogicError/InfiniteRecursiveCallResources.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/LogicError/InfiniteRecursiveCallResources.resx new file mode 100644 index 0000000..299c102 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/LogicError/InfiniteRecursiveCallResources.resx @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Infinite recursive loop detected + An optional longer localizable description of the diagnostic. + + + The method {0}.{1}() calls {1}() again. This is an infinite recursive call. + + + The method {0}.{1}() calls {2} which eventually calls {1}() again. This is an infinite recursive call. + + + Infinite recursive loop + The title of the diagnostic. + + \ No newline at end of file diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/MonoBehaviourInfo.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/MonoBehaviourInfo.cs index 7f5b981..9d7acce 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/MonoBehaviourInfo.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/MonoBehaviourInfo.cs @@ -19,6 +19,17 @@ class MonoBehaviourInfo "FixedUpdate", "LateUpdate"); + private static readonly ImmutableHashSet MoreUpdateMethodNames = ImmutableHashSet.Create( + + "MoreFixedUpdate", + "MoreSlowFixedUpdate", + "MoreUpdate", + "MoreUpdate2", + "MoreUpdate3", + "MoreLateUpdate", + "MoreLateUpdate2" + ); + public MonoBehaviourInfo(SyntaxNodeAnalysisContext analysisContext) { _classDeclaration = analysisContext.Node as ClassDeclarationSyntax; @@ -34,10 +45,10 @@ public MonoBehaviourInfo(SyntaxNodeAnalysisContext analysisContext) public void ForEachUpdateMethod(Action callback) { + var methods = _classDeclaration.Members.OfType(); + if (this.IsMonoBehaviour()) { - var methods = _classDeclaration.Members.OfType(); - foreach (var method in methods) { if (UpdateMethodNames.Contains(method.Identifier.ValueText)) @@ -46,6 +57,17 @@ public void ForEachUpdateMethod(Action callback) } } } + + if (this.IsMoreLoopBehaviour()) + { + foreach (var method in methods) + { + if (MoreUpdateMethodNames.Contains(method.Identifier.ValueText)) + { + callback(method); + } + } + } } public bool IsMonoBehaviour() @@ -70,5 +92,32 @@ private static bool IsMonoBehavior(INamedTypeSymbol classDeclaration) return IsMonoBehavior(baseClass); //determine if the BaseClass extends mono behavior } + + + + public bool IsMoreLoopBehaviour() + { + return IsMonoBehavior(_classSymbol); + } + + private static bool IsMoreLoopBehaviour(INamedTypeSymbol classDeclaration) + { + if (classDeclaration.BaseType == null) + { + return false; + } + + var allInterfaces = classDeclaration.AllInterfaces; + foreach (var oneInterface in allInterfaces) + { + if (oneInterface.Name.Equals("IMoreLoopBehaviour")) + { + return true; + } + } + + return false; + + } } } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj b/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj index 93b5f25..2c7a6bf 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj @@ -74,6 +74,11 @@ True DoNotUseCoroutinesResources.resx + + True + True + DuplicatedDelegateDetectionResource.resx + True @@ -84,6 +89,7 @@ + True True @@ -113,18 +119,33 @@ True DoNotBoxWhenInvokeResource.resx + + + True + True + EnumShouldManualSetMemberValueResource.resx + True True DoNotUseEnumTypeParameterResource.resx + + True True UnsealedDerivedClassResources.resx + + + + True + True + LambdaAnalyzerResources.resx + @@ -133,6 +154,12 @@ True StructAnalyzerResources.resx + + True + True + InfiniteRecursiveCallResources.resx + + True @@ -193,6 +220,11 @@ ResXFileCodeGenerator UseCompareTagResources.Designer.cs + + ResXFileCodeGenerator + DuplicatedDelegateDetectionResource.Designer.cs + Designer + ResXFileCodeGenerator ShouldCacheDelegateResource.Designer.cs @@ -221,6 +253,10 @@ ResXFileCodeGenerator DoNotBoxWhenInvokeResource.Designer.cs + + ResXFileCodeGenerator + EnumShouldManualSetMemberValueResource.Designer.cs + ResXFileCodeGenerator DoNotUseEnumTypeParameterResource.Designer.cs @@ -229,9 +265,18 @@ ResXFileCodeGenerator UnsealedDerivedClassResources.Designer.cs + + ResXFileCodeGenerator + LambdaAnalyzerResources.Designer.cs + ResXFileCodeGenerator StructAnalyzerResources.Designer.cs + Designer + + + ResXFileCodeGenerator + InfiniteRecursiveCallResources.Designer.cs ResXFileCodeGenerator @@ -317,6 +362,9 @@ ..\..\packages\System.Reflection.Metadata.1.0.21\lib\portable-net45+win8\System.Reflection.Metadata.dll False + + ..\..\..\..\..\..\..\..\Program Files\Unity2017.1.3p2\Editor\Data\Managed\UnityEngine.dll + From e461164fee2fd709c2aeef8e1d00479007e3704e Mon Sep 17 00:00:00 2001 From: Donald Wu Date: Thu, 6 Dec 2018 10:08:14 +0800 Subject: [PATCH 07/10] update readme about AutoAddUnityEngineAnalyzer update readme about AutoAddUnityEngineAnalyzer --- readme.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/readme.md b/readme.md index 175809c..1e3fcf3 100644 --- a/readme.md +++ b/readme.md @@ -4,6 +4,13 @@ UnityEngineAnalyzer UnityEngineAnalyzer is a set of Roslyn analyzers that aim to detect common problems in Unity3D C# code. Unity3D makes it easy for us to make cross platform games, but there are hidden rules about performance and AOT, which might only come with experience, testing or reading the forums. It is hoped that such problems can be caught before compilation. +Auto Add UnityEngineAnalyzer to your Unity project +--------------------- +Each time your refresh your unity project, the UnityEngineAnalyzer may be removed from the solution. +To auto add it to your solution, you can: +1. copy Editor/AutoAddUnityEngineAnalyzer.cs to your unity project Editor folder; +1. modify `public const string UnityEngineAnalyzerPath = "Tools\\VisualStudio\\UnityEngineAnalyzer\\UnityEngineAnalyzer.dll";` to your actual UnityEngineAnalyzer.dll path + Comand Line Interface --------------------- From bf7a0684b6865b905f8275239dd7305ba0585e0d Mon Sep 17 00:00:00 2001 From: Donald Wu Date: Thu, 6 Dec 2018 10:17:17 +0800 Subject: [PATCH 08/10] Update readme.md --- readme.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/readme.md b/readme.md index 1e3fcf3..24e4ac9 100644 --- a/readme.md +++ b/readme.md @@ -11,6 +11,25 @@ To auto add it to your solution, you can: 1. copy Editor/AutoAddUnityEngineAnalyzer.cs to your unity project Editor folder; 1. modify `public const string UnityEngineAnalyzerPath = "Tools\\VisualStudio\\UnityEngineAnalyzer\\UnityEngineAnalyzer.dll";` to your actual UnityEngineAnalyzer.dll path +Implemented Analyzer +--------------------- +- Prefered to sealed the inherited class; 应对子类进行sealed +- struct should implement IEquatable, should override GetHashCode(); struct应实现IEquatable、应override GetHashCode() +- should cache delegate to avoid gc; 应cache delegate以防止产生gc +- avoid new object in Update() to avoid gc; 避免在Update()函数里new对象产生gc +- avoid boxing; 调用函数时应避免box产生gc +- should not use enum for generic; 泛型避免使用enum +- should use hash for Animator interfaces; Animator接口避免使用String,应使用Hash +- should use hash for Material interfaces; Material接口避免使用String,应使用Hash +- avoid calling Camera.main in Update-like message function; 避免Update()函数里调用Camera.main +- avoid calling GameObject.Find() in Update-like message function; 避免在Update()函数里GameObject.Find()之类的函数 +- should use CompareTag() but not string comparision; 应使用ComareTag(),而不是字符串比较 +- avoid using coroutine; 避免使用协程 +- avoid empty Update-like message function; 避免Mono空消息函数 +- avoid SendMessage(); 避免SendMessage +- detect some infinite recursive call; 错误无限递归检查 + + Comand Line Interface --------------------- From e0e5fc6d0dc81a62bffc764672f9217a9233862f Mon Sep 17 00:00:00 2001 From: Donald Wu Date: Thu, 6 Dec 2018 10:21:58 +0800 Subject: [PATCH 09/10] update readme --- Documents/usage.png | Bin 0 -> 18631 bytes readme.md | 3 +++ 2 files changed, 3 insertions(+) create mode 100644 Documents/usage.png diff --git a/Documents/usage.png b/Documents/usage.png new file mode 100644 index 0000000000000000000000000000000000000000..7cf1290772b7c531a016f71ce7f3e72675d05638 GIT binary patch literal 18631 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 literal 0 HcmV?d00001 diff --git a/readme.md b/readme.md index 24e4ac9..a4e412c 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,9 @@ UnityEngineAnalyzer =================== + +![](https://raw.githubusercontent.com/meng-hui/UnityEngineAnalyzer/master/Documents/usage.png) + UnityEngineAnalyzer is a set of Roslyn analyzers that aim to detect common problems in Unity3D C# code. Unity3D makes it easy for us to make cross platform games, but there are hidden rules about performance and AOT, which might only come with experience, testing or reading the forums. It is hoped that such problems can be caught before compilation. From e5fe919a9d0f8708cf594dbecb2c22446a759e55 Mon Sep 17 00:00:00 2001 From: Donald Wu Date: Thu, 6 Dec 2018 10:22:42 +0800 Subject: [PATCH 10/10] Update readme.md --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index a4e412c..5fae71d 100644 --- a/readme.md +++ b/readme.md @@ -2,7 +2,7 @@ UnityEngineAnalyzer =================== -![](https://raw.githubusercontent.com/meng-hui/UnityEngineAnalyzer/master/Documents/usage.png) +![](https://raw.githubusercontent.com/donaldwuid/UnityEngineAnalyzer/master/Documents/usage.png) UnityEngineAnalyzer is a set of Roslyn analyzers that aim to detect common problems in Unity3D C# code. Unity3D makes it easy for us to make cross platform games, but there are hidden rules about performance and AOT, which might only come with experience, testing or reading the forums. It is hoped that such problems can be caught before compilation.