From bf9f360f3472a8ae7d6d37e01ae76b10da75ae35 Mon Sep 17 00:00:00 2001 From: Vinny Date: Fri, 4 Nov 2016 19:26:42 -0700 Subject: [PATCH 01/46] Implementing UnsealedDerivedClass --- .../DiagnosticDescriptors.cs | 12 ++ .../UnityEngineAnalyzer/DiagnosticIDs.cs | 1 + .../IL2CPP/UnsealedDerivedClassAnalyzer.cs | 42 ++++++ .../UnsealedDerivedClassResources.Designer.cs | 91 ++++++++++++ .../IL2CPP/UnsealedDerivedClassResources.resx | 132 ++++++++++++++++++ ...NotUseForEachInUpdateResources.Designer.cs | 91 ++++++++++++ .../UnityEngineAnalyzer.csproj | 15 ++ 7 files changed, 384 insertions(+) create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/IL2CPP/UnsealedDerivedClassAnalyzer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/IL2CPP/UnsealedDerivedClassResources.Designer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/IL2CPP/UnsealedDerivedClassResources.resx create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/DoNotUseForEachInUpdateResources.Designer.cs diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs index 5e40115..7244679 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs @@ -5,6 +5,7 @@ using UnityEngineAnalyzer.EmptyMonoBehaviourMethods; using UnityEngineAnalyzer.FindMethodsInUpdate; using UnityEngineAnalyzer.ForEachInUpdate; +using UnityEngineAnalyzer.IL2CPP; using UnityEngineAnalyzer.OnGUI; using UnityEngineAnalyzer.StringMethods; @@ -13,6 +14,7 @@ namespace UnityEngineAnalyzer static class DiagnosticDescriptors { //NOTES: Naming of Descriptors are a bit inconsistant + //NOTES: The Resource Reading code seems repetative public static readonly DiagnosticDescriptor DoNotUseOnGUI = new DiagnosticDescriptor( id: DiagnosticIDs.DoNotUseOnGUI, @@ -114,5 +116,15 @@ static class DiagnosticDescriptors isEnabledByDefault: true, description: new LocalizableResourceString(nameof(DoNotUseForEachInUpdateResources.Description), DoNotUseForEachInUpdateResources.ResourceManager, typeof(DoNotUseForEachInUpdateResources)) ); + + public static readonly DiagnosticDescriptor UnsealedDerivedClass = new DiagnosticDescriptor( + id: DiagnosticIDs.UnsealedDerivedClass, + title: new LocalizableResourceString(nameof(UnsealedDerivedClassResources.Title), UnsealedDerivedClassResources.ResourceManager, typeof(UnsealedDerivedClassResources)), + messageFormat: new LocalizableResourceString(nameof(UnsealedDerivedClassResources.MessageFormat), UnsealedDerivedClassResources.ResourceManager, typeof(UnsealedDerivedClassResources)), + category: DiagnosticCategories.Performance, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: new LocalizableResourceString(nameof(UnsealedDerivedClassResources.Description), UnsealedDerivedClassResources.ResourceManager, typeof(UnsealedDerivedClassResources)) + ); } } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs index aac1077..79f92ed 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs @@ -10,6 +10,7 @@ public static class DiagnosticIDs public const string DoNotUseFindMethodsInUpdate = "UEA0005"; public const string DoNotUseCoroutines = "UEA0006"; public const string DoNotUseForEachInUpdate = "UEA0007"; + public const string UnsealedDerivedClass = "UEA0008"; //NOTES: These should probably be on their own analyzer - as they are not specific to Unity public const string DoNotUseRemoting = "AOT0001"; diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/IL2CPP/UnsealedDerivedClassAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/IL2CPP/UnsealedDerivedClassAnalyzer.cs new file mode 100644 index 0000000..0d5501f --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/IL2CPP/UnsealedDerivedClassAnalyzer.cs @@ -0,0 +1,42 @@ +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.IL2CPP +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class UnsealedDerivedClassAnalyzer : DiagnosticAnalyzer + { + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeClassSyntax, SyntaxKind.ClassDeclaration); + } + + private void AnalyzeClassSyntax(SyntaxNodeAnalysisContext context) + { + var classDeclaration = (ClassDeclarationSyntax)context.Node; + + if (classDeclaration.IsDerived() && !classDeclaration.IsSealed()) + { + var methods = classDeclaration.Members.OfType(); + + foreach (var method in methods) + { + + if (method.IsOverriden() && !method.IsSealed()) + { + var diagnostic = Diagnostic.Create(SupportedDiagnostics.First(), method.GetLocation(), + method.Identifier.ToString(), classDeclaration.Identifier.ToString()); + + context.ReportDiagnostic(diagnostic); + } + } + } + } + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.UnsealedDerivedClass); + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/IL2CPP/UnsealedDerivedClassResources.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/IL2CPP/UnsealedDerivedClassResources.Designer.cs new file mode 100644 index 0000000..752994c --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/IL2CPP/UnsealedDerivedClassResources.Designer.cs @@ -0,0 +1,91 @@ +//------------------------------------------------------------------------------ +// +// 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.IL2CPP { + using System; + using System.Reflection; + + + /// + /// 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. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class UnsealedDerivedClassResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal UnsealedDerivedClassResources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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.IL2CPP.UnsealedDerivedClassResources", typeof(UnsealedDerivedClassResources).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Unsealed Methods in Derived classes can impact performance. + /// + internal static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Method {0} in Derived Class {1} is not sealed. Sealing the method or class improves performance.. + /// + internal static string MessageFormat { + get { + return ResourceManager.GetString("MessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unsealed Derived Class. + /// + internal static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/IL2CPP/UnsealedDerivedClassResources.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/IL2CPP/UnsealedDerivedClassResources.resx new file mode 100644 index 0000000..cfee10d --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/IL2CPP/UnsealedDerivedClassResources.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 + + + Unsealed Methods in Derived classes can impact performance + An optional longer localizable description of the diagnostic. + + + Method {0} in Derived Class {1} is not sealed. Sealing the method or class improves performance. + The format-able message the diagnostic displays. + + + Unsealed Derived Class + The title of the diagnostic. + + \ No newline at end of file diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/DoNotUseForEachInUpdateResources.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/DoNotUseForEachInUpdateResources.Designer.cs new file mode 100644 index 0000000..b440ffd --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/DoNotUseForEachInUpdateResources.Designer.cs @@ -0,0 +1,91 @@ +//------------------------------------------------------------------------------ +// +// 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.StringMethods { + using System; + using System.Reflection; + + + /// + /// 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. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class DoNotUseStringMethodsResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal DoNotUseStringMethodsResources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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.StringMethods.DoNotUseStringMethodsResources", typeof(DoNotUseStringMethodsResources).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Use of SendMessage, SendMessageUpwards, BroadcastMessage, Invoke or InvokeRepeating leads to code that is hard to maintain. Consider using UnityEvent, C# event, delegates or a direct method call.. + /// + internal static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use of SendMessage, SendMessageUpwards, BroadcastMessage, Invoke or InvokeRepeating leads to code that is hard to maintain. Consider using UnityEvent, C# event, delegates or a direct method call.. + /// + internal static string MessageFormat { + get { + return ResourceManager.GetString("MessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Do not use SendMessage, SendMessageUpwards or BroadcastMessage.. + /// + internal static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj b/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj index fc59fdc..cf5a97d 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj @@ -83,6 +83,17 @@ DoNotUseForEachInUpdateResources.resx + + True + True + DoNotUseStringMethodsResources.resx + + + + True + True + UnsealedDerivedClassResources.resx + @@ -132,6 +143,10 @@ ResXFileCodeGenerator DoNotUseForEachInUpdateResources.Designer.cs + + ResXFileCodeGenerator + UnsealedDerivedClassResources.Designer.cs + ResXFileCodeGenerator DoNotUseForEachInUpdateResources.Designer.cs From 092a41d7e11598a7557b4c22707565362ffc6fa9 Mon Sep 17 00:00:00 2001 From: Vinny Date: Fri, 4 Nov 2016 21:09:27 -0700 Subject: [PATCH 02/46] Fixing bugs with Resources --- ...NotUseForEachInUpdateResources.Designer.cs | 91 ------------------- .../UnityEngineAnalyzer.csproj | 11 +-- 2 files changed, 3 insertions(+), 99 deletions(-) delete mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/DoNotUseForEachInUpdateResources.Designer.cs diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/DoNotUseForEachInUpdateResources.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/DoNotUseForEachInUpdateResources.Designer.cs deleted file mode 100644 index b440ffd..0000000 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/DoNotUseForEachInUpdateResources.Designer.cs +++ /dev/null @@ -1,91 +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.StringMethods { - using System; - using System.Reflection; - - - /// - /// 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. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class DoNotUseStringMethodsResources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal DoNotUseStringMethodsResources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [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.StringMethods.DoNotUseStringMethodsResources", typeof(DoNotUseStringMethodsResources).GetTypeInfo().Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to Use of SendMessage, SendMessageUpwards, BroadcastMessage, Invoke or InvokeRepeating leads to code that is hard to maintain. Consider using UnityEvent, C# event, delegates or a direct method call.. - /// - internal static string Description { - get { - return ResourceManager.GetString("Description", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use of SendMessage, SendMessageUpwards, BroadcastMessage, Invoke or InvokeRepeating leads to code that is hard to maintain. Consider using UnityEvent, C# event, delegates or a direct method call.. - /// - internal static string MessageFormat { - get { - return ResourceManager.GetString("MessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Do not use SendMessage, SendMessageUpwards or BroadcastMessage.. - /// - internal static string Title { - get { - return ResourceManager.GetString("Title", resourceCulture); - } - } - } -} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj b/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj index cf5a97d..9d01902 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj @@ -83,11 +83,6 @@ DoNotUseForEachInUpdateResources.resx - - True - True - DoNotUseStringMethodsResources.resx - True @@ -141,7 +136,7 @@ ResXFileCodeGenerator - DoNotUseForEachInUpdateResources.Designer.cs + DoNotUseCoroutinesResources.Designer.cs ResXFileCodeGenerator @@ -149,11 +144,11 @@ ResXFileCodeGenerator - DoNotUseForEachInUpdateResources.Designer.cs + DoNotUseOnGUIResources.Designer.cs ResXFileCodeGenerator - DoNotUseForEachInUpdateResources.Designer.cs + DoNotUseStringMethodsResources.Designer.cs From ad7ac35b5055a6d572c3fbdbc8c0603be2d116c6 Mon Sep 17 00:00:00 2001 From: Vinny Date: Fri, 4 Nov 2016 21:12:30 -0700 Subject: [PATCH 03/46] Creating new Analyzer --- .../InvokeFunctionMissingAnalyzer.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/InvokeFunctionMissingAnalyzer.cs diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/InvokeFunctionMissingAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/InvokeFunctionMissingAnalyzer.cs new file mode 100644 index 0000000..4c1950a --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/InvokeFunctionMissingAnalyzer.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace UnityEngineAnalyzer.StringMethods +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class InvokeFunctionMissingAnalyzer : DiagnosticAnalyzer + { + public override void Initialize(AnalysisContext context) + { + throw new System.NotImplementedException(); + } + + public override ImmutableArray SupportedDiagnostics + { + get + { + throw new NotImplementedException(); + } + } + } +} \ No newline at end of file From 5472dfb3bd8cbd1066c3007d68a1d3da115d66b6 Mon Sep 17 00:00:00 2001 From: Vinny Date: Wed, 9 Nov 2016 17:07:22 -0800 Subject: [PATCH 04/46] Adding descriptor --- .../DiagnosticDescriptors.cs | 10 ++ .../UnityEngineAnalyzer/DiagnosticIDs.cs | 2 + ...InvokeFunctionMissingResources.Designer.cs | 91 ++++++++++++ .../InvokeFunctionMissingResources.resx | 129 ++++++++++++++++++ 4 files changed, 232 insertions(+) create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/InvokeFunctionMissingResources.Designer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/InvokeFunctionMissingResources.resx diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs index 7244679..6d21546 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs @@ -126,5 +126,15 @@ static class DiagnosticDescriptors isEnabledByDefault: true, description: new LocalizableResourceString(nameof(UnsealedDerivedClassResources.Description), UnsealedDerivedClassResources.ResourceManager, typeof(UnsealedDerivedClassResources)) ); + + public static readonly DiagnosticDescriptor InvokeFunctionMissing = new DiagnosticDescriptor( + id: DiagnosticIDs.InvokeFunctionMissing, + title: new LocalizableResourceString(nameof(InvokeFunctionMissingResources.Title), InvokeFunctionMissingResources.ResourceManager, typeof(InvokeFunctionMissingResources)), + messageFormat: new LocalizableResourceString(nameof(InvokeFunctionMissingResources.MessageFormat), InvokeFunctionMissingResources.ResourceManager, typeof(InvokeFunctionMissingResources)), + category: DiagnosticCategories.Performance, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: new LocalizableResourceString(nameof(InvokeFunctionMissingResources.Description), InvokeFunctionMissingResources.ResourceManager, typeof(InvokeFunctionMissingResources)) + ); } } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs index 79f92ed..7c154b4 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs @@ -11,11 +11,13 @@ public static class DiagnosticIDs public const string DoNotUseCoroutines = "UEA0006"; public const string DoNotUseForEachInUpdate = "UEA0007"; public const string UnsealedDerivedClass = "UEA0008"; + public const string InvokeFunctionMissing = "UEA0009"; //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"; + } } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/InvokeFunctionMissingResources.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/InvokeFunctionMissingResources.Designer.cs new file mode 100644 index 0000000..f156857 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/InvokeFunctionMissingResources.Designer.cs @@ -0,0 +1,91 @@ +//------------------------------------------------------------------------------ +// +// 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.StringMethods { + using System; + using System.Reflection; + + + /// + /// 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. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class InvokeFunctionMissingResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal InvokeFunctionMissingResources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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.StringMethods.InvokeFunctionMissingResources", typeof(InvokeFunctionMissingResources).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to The function being invoked does not exist. + /// + internal static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The function {0} is invoking a method that does not exist. + /// + internal static string MessageFormat { + get { + return ResourceManager.GetString("MessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invoke Function is Missing. + /// + internal static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/InvokeFunctionMissingResources.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/InvokeFunctionMissingResources.resx new file mode 100644 index 0000000..5f7074f --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/InvokeFunctionMissingResources.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 + + + The function being invoked does not exist + + + The function {0} is invoking a method that does not exist + + + Invoke Function is Missing + + \ No newline at end of file From 43d75eecff1275ea96677f2d330e47657a4417c1 Mon Sep 17 00:00:00 2001 From: Vinny Date: Thu, 10 Nov 2016 13:00:08 -0800 Subject: [PATCH 05/46] Refactoring invocation method name --- .../UnityEngineAnalyzer/RolsynExtensions.cs | 27 ++++++++++++++++--- .../DoNotUseStringMethodsAnalyzer.cs | 15 +---------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/RolsynExtensions.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/RolsynExtensions.cs index d2c84ee..b98d385 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/RolsynExtensions.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/RolsynExtensions.cs @@ -1,9 +1,6 @@ using System; -using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Text; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -65,5 +62,29 @@ public static bool IsOverriden(this MethodDeclarationSyntax methodDeclaration) { return methodDeclaration.Modifiers.Any(m => m.Kind() == SyntaxKind.OverrideKeyword); } + + public static string MethodName(this InvocationExpressionSyntax invocation) + { + + string name; + + if (invocation.Expression is MemberAccessExpressionSyntax) + { + name = ((MemberAccessExpressionSyntax)invocation.Expression).Name.Identifier.ToString(); + } + else if (invocation.Expression is IdentifierNameSyntax) + { + name = ((IdentifierNameSyntax)invocation.Expression).ToString(); + } + else if (invocation.Expression is GenericNameSyntax) + { + name = ((GenericNameSyntax)invocation.Expression).Identifier.ToString(); + } + else + { + throw new ArgumentException("Unable to determine name of method"); + } + return name; + } } } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/DoNotUseStringMethodsAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/DoNotUseStringMethodsAnalyzer.cs index 3725ee2..ea79bfd 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/DoNotUseStringMethodsAnalyzer.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/DoNotUseStringMethodsAnalyzer.cs @@ -28,20 +28,7 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context) return; } - - string name = null; - if (invocation.Expression is MemberAccessExpressionSyntax) - { - name = ((MemberAccessExpressionSyntax)invocation.Expression).Name.Identifier.ToString(); - } - else if (invocation.Expression is IdentifierNameSyntax) - { - name = ((IdentifierNameSyntax)invocation.Expression).ToString(); - } - else if (invocation.Expression is GenericNameSyntax) - { - name = ((GenericNameSyntax)invocation.Expression).Identifier.ToString(); - } + var name = invocation.MethodName(); // check if any of the "string" methods are used From b063804b9b49645dfbc0bbc5f89ebbc7c22f4412 Mon Sep 17 00:00:00 2001 From: Vinny Date: Thu, 10 Nov 2016 13:56:30 -0800 Subject: [PATCH 06/46] Adding new analyzer for Invoke --- .../UnityEngineAnalyzer/RolsynExtensions.cs | 12 ++++ .../InvokeFunctionMissingAnalyzer.cs | 55 +++++++++++++++++-- ...InvokeFunctionMissingResources.Designer.cs | 2 +- .../InvokeFunctionMissingResources.resx | 2 +- .../UnityEngineAnalyzer.csproj | 10 ++++ 5 files changed, 75 insertions(+), 6 deletions(-) diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/RolsynExtensions.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/RolsynExtensions.cs index b98d385..dbdd37e 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/RolsynExtensions.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/RolsynExtensions.cs @@ -86,5 +86,17 @@ public static string MethodName(this InvocationExpressionSyntax invocation) } return name; } + + public static T GetArgumentValue(this ArgumentSyntax argument) + { + //NOTE: Possibly add support for constant parameters in the future + + if (argument.Expression is LiteralExpressionSyntax) + { + var argumentValue = ((LiteralExpressionSyntax)argument.Expression).Token.Value; + return (T)argumentValue; + } + return default(T); + } } } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/InvokeFunctionMissingAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/InvokeFunctionMissingAnalyzer.cs index 4c1950a..870d020 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/InvokeFunctionMissingAnalyzer.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/InvokeFunctionMissingAnalyzer.cs @@ -1,6 +1,10 @@ using System; using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; namespace UnityEngineAnalyzer.StringMethods @@ -8,17 +12,60 @@ namespace UnityEngineAnalyzer.StringMethods [DiagnosticAnalyzer(LanguageNames.CSharp)] public class InvokeFunctionMissingAnalyzer : DiagnosticAnalyzer { + private static readonly ImmutableHashSet InvokeMethods = ImmutableHashSet.Create("Invoke", "InvokeRepeating"); + private static readonly string InvokeMethodTypeName = "UnityEngine.MonoBehaviour"; + + public override void Initialize(AnalysisContext context) { - throw new System.NotImplementedException(); + context.RegisterSyntaxNodeAction(AnalyzeInvocation, SyntaxKind.InvocationExpression); } - public override ImmutableArray SupportedDiagnostics + private void AnalyzeInvocation(SyntaxNodeAnalysisContext context) { - get + var invocation = context.Node as InvocationExpressionSyntax; + if (invocation == null) { - throw new NotImplementedException(); + return; } + + var methodName = invocation.MethodName(); + + if (InvokeMethods.Contains(methodName)) + { + // check if the method is the one from UnityEngine + var symbolInfo = context.SemanticModel.GetSymbolInfo(invocation); + var methodSymbol = symbolInfo.Symbol as IMethodSymbol; + + var fullTypeName = methodSymbol?.ContainingType.ToString(); + + if (fullTypeName == InvokeMethodTypeName && invocation.ArgumentList.Arguments.Count > 0) + { + var firstArgumentExpression = invocation.ArgumentList.Arguments[0]; + + var invokedMethodName = firstArgumentExpression.GetArgumentValue(); + + var containingClassDeclaration = invocation.Ancestors().FirstOrDefault(a => a is ClassDeclarationSyntax) as ClassDeclarationSyntax; + + var allMethods = containingClassDeclaration?.Members.OfType(); + + var invokeEndPoint = allMethods.FirstOrDefault(m => m.Identifier.ValueText == invokedMethodName); + + if (invokeEndPoint == null) + { + var diagnostic = Diagnostic.Create(SupportedDiagnostics.First(), firstArgumentExpression.GetLocation(), + methodName, invokedMethodName); + + context.ReportDiagnostic(diagnostic); + } + } + + + + } + } + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.InvokeFunctionMissing); } } \ No newline at end of file diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/InvokeFunctionMissingResources.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/InvokeFunctionMissingResources.Designer.cs index f156857..25dcb41 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/InvokeFunctionMissingResources.Designer.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/InvokeFunctionMissingResources.Designer.cs @@ -71,7 +71,7 @@ internal static string Description { } /// - /// Looks up a localized string similar to The function {0} is invoking a method that does not exist. + /// Looks up a localized string similar to The function "{0}" is invoking the method "{1}" that does not exist. /// internal static string MessageFormat { get { diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/InvokeFunctionMissingResources.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/InvokeFunctionMissingResources.resx index 5f7074f..a33bf05 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/InvokeFunctionMissingResources.resx +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/InvokeFunctionMissingResources.resx @@ -121,7 +121,7 @@ The function being invoked does not exist - The function {0} is invoking a method that does not exist + The function "{0}" is invoking the method "{1}" that does not exist Invoke Function is Missing diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj b/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj index 9d01902..5ee6832 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj @@ -104,6 +104,12 @@ True DoNotUseStringMethodsResources.resx + + + True + True + InvokeFunctionMissingResources.resx + @@ -150,6 +156,10 @@ ResXFileCodeGenerator DoNotUseStringMethodsResources.Designer.cs + + ResXFileCodeGenerator + InvokeFunctionMissingResources.Designer.cs + From ce710d5e0ed0dce0f919127cca52454ea9a72ee7 Mon Sep 17 00:00:00 2001 From: Vinny Date: Thu, 10 Nov 2016 13:57:12 -0800 Subject: [PATCH 07/46] Invoke is now analyzed by a different analyzer --- .../StringMethods/DoNotUseStringMethodsAnalyzer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/DoNotUseStringMethodsAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/DoNotUseStringMethodsAnalyzer.cs index ea79bfd..d129df2 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/DoNotUseStringMethodsAnalyzer.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/DoNotUseStringMethodsAnalyzer.cs @@ -10,7 +10,7 @@ namespace UnityEngineAnalyzer.StringMethods [DiagnosticAnalyzer(LanguageNames.CSharp)] public sealed class DoNotUseStringMethodsAnalyzer : DiagnosticAnalyzer { - private static readonly ImmutableHashSet StringMethods = ImmutableHashSet.Create("SendMessage", "SendMessageUpwards", "BroadcastMessage", "Invoke", "InvokeRepeating"); + private static readonly ImmutableHashSet StringMethods = ImmutableHashSet.Create("SendMessage", "SendMessageUpwards", "BroadcastMessage"); private static readonly ImmutableHashSet Namespaces = ImmutableHashSet.Create("UnityEngine.Component", "UnityEngine.GameObject", "UnityEngine.MonoBehaviour"); public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.DoNotUseStringMethods); From 7ccd8684c352084a41b8d7acc82bb9ae5fcc2ce1 Mon Sep 17 00:00:00 2001 From: Vinny Date: Wed, 23 Nov 2016 15:21:17 -0500 Subject: [PATCH 08/46] Updates to improve Unity integration --- UnityEngineAnalyzer.CLI/AnalyzerReport.cs | 12 ++- UnityEngineAnalyzer.CLI/Program.cs | 30 +++++++- .../Reporting/ConsoleAnalyzerExporter.cs | 46 +---------- .../Reporting/DiagnosticInfo.cs | 19 +++-- .../Reporting/IAnalyzerExporter.cs | 3 +- .../Reporting/JsonAnalyzerExporter.cs | 13 +++- .../StandardOutputAnalyzerReporter.cs | 76 +++++++++++++++++++ UnityEngineAnalyzer.CLI/SolutionAnalyzer.cs | 17 ++++- .../UnityEngineAnalyzer.CLI.csproj | 1 + 9 files changed, 153 insertions(+), 64 deletions(-) create mode 100644 UnityEngineAnalyzer.CLI/Reporting/StandardOutputAnalyzerReporter.cs diff --git a/UnityEngineAnalyzer.CLI/AnalyzerReport.cs b/UnityEngineAnalyzer.CLI/AnalyzerReport.cs index 8dd2add..0d4cd60 100644 --- a/UnityEngineAnalyzer.CLI/AnalyzerReport.cs +++ b/UnityEngineAnalyzer.CLI/AnalyzerReport.cs @@ -35,10 +35,12 @@ public void AppendDiagnostics(IEnumerable diagnosticResults) Message = diagnostic.GetMessage(), FileName = diagnostic.Location.SourceTree.FilePath, LineNumber = lineSpan.StartLinePosition.Line, + CharacterPosition = lineSpan.StartLinePosition.Character, Severity = (DiagnosticInfoSeverity)diagnostic.Severity }; + foreach (var exporter in _exporters) { exporter.AppendDiagnostic(diagnosticInfo); @@ -50,7 +52,7 @@ public void FinalizeReport(TimeSpan duration) { foreach (var exporter in _exporters) { - exporter.Finish(duration); + exporter.FinalizeExporter(duration); } } @@ -61,5 +63,13 @@ public void InitializeReport(FileInfo projectFile) exporter.InitializeExporter(projectFile); } } + + public void NotifyException(Exception exception) + { + foreach (var exporter in _exporters) + { + exporter.NotifyException(exception); + } + } } } diff --git a/UnityEngineAnalyzer.CLI/Program.cs b/UnityEngineAnalyzer.CLI/Program.cs index 8c04e7a..6af6b78 100644 --- a/UnityEngineAnalyzer.CLI/Program.cs +++ b/UnityEngineAnalyzer.CLI/Program.cs @@ -3,16 +3,30 @@ using System.IO; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.Syntax; using UnityEngineAnalyzer.CLI.Reporting; namespace UnityEngineAnalyzer.CLI { public class Program { + private static Dictionary _exporters = new Dictionary(); + + static Program() + { + _exporters.Add(nameof(ConsoleAnalyzerExporter), typeof(ConsoleAnalyzerExporter)); + _exporters.Add(nameof(JsonAnalyzerExporter), typeof(JsonAnalyzerExporter)); + _exporters.Add(nameof(StandardOutputAnalyzerReporter), typeof(StandardOutputAnalyzerReporter)); + + } + + public static void Main(string[] args) { try { + //TODO: Use a proper parser for the commands + if (args.Length <= 0) { return; @@ -25,8 +39,18 @@ public static void Main(string[] args) //NOTE: This could be configurable via the CLI at some point var report = new AnalyzerReport(); - report.AddExporter(new ConsoleAnalyzerExporter()); - report.AddExporter(new JsonAnalyzerExporter()); + + if (args.Length > 1 && _exporters.ContainsKey(args[1])) + { + var exporterInstance = Activator.CreateInstance(_exporters[args[1]]); + report.AddExporter(exporterInstance as IAnalyzerExporter); + } + else + { + report.AddExporter(new ConsoleAnalyzerExporter()); + report.AddExporter(new JsonAnalyzerExporter()); + } + report.InitializeReport(fileInfo); @@ -46,8 +70,6 @@ public static void Main(string[] args) report.FinalizeReport(duration); - Console.WriteLine("Press any key to exit..."); - Console.ReadKey(); } catch (Exception generalException) { diff --git a/UnityEngineAnalyzer.CLI/Reporting/ConsoleAnalyzerExporter.cs b/UnityEngineAnalyzer.CLI/Reporting/ConsoleAnalyzerExporter.cs index bb4809a..add0467 100644 --- a/UnityEngineAnalyzer.CLI/Reporting/ConsoleAnalyzerExporter.cs +++ b/UnityEngineAnalyzer.CLI/Reporting/ConsoleAnalyzerExporter.cs @@ -3,57 +3,17 @@ namespace UnityEngineAnalyzer.CLI.Reporting { - public class ConsoleAnalyzerExporter : IAnalyzerExporter + public class ConsoleAnalyzerExporter : StandardOutputAnalyzerReporter { - private const string ConsoleSeparator = "\t"; - private const DiagnosticInfoSeverity MinimalSeverity = DiagnosticInfoSeverity.Warning; - public void AppendDiagnostic(DiagnosticInfo diagnosticInfo) - { - if (diagnosticInfo.Severity < MinimalSeverity) - { - return; - } - - Console.Write(diagnosticInfo.Id); - Console.Write(ConsoleSeparator); - - Console.ForegroundColor = ConsoleColorFromSeverity(diagnosticInfo.Severity); - Console.Write(diagnosticInfo.Severity.ToString()); - Console.Write(ConsoleSeparator); - - Console.ForegroundColor = ConsoleColor.Cyan; - Console.Write(diagnosticInfo.Message); - Console.ResetColor(); - Console.Write(ConsoleSeparator); - Console.WriteLine(@"{0}({1})",diagnosticInfo.FileName,diagnosticInfo.LineNumber); - } - - private ConsoleColor ConsoleColorFromSeverity(DiagnosticInfoSeverity severity) - { - switch (severity) - { - case DiagnosticInfoSeverity.Hidden: - return ConsoleColor.Gray; - case DiagnosticInfoSeverity.Info: - return ConsoleColor.Green; - case DiagnosticInfoSeverity.Warning: - return ConsoleColor.Yellow; - case DiagnosticInfoSeverity.Error: - return ConsoleColor.Red; - default: - return ConsoleColor.White; - } - } - - public void Finish(TimeSpan duration) + public override void FinalizeExporter(TimeSpan duration) { Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("Console Export Finished ({0})", duration); Console.ResetColor(); } - public void InitializeExporter(FileInfo projectFile) + public override void InitializeExporter(FileInfo projectFile) { Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("Unity Syntax Analyzer"); diff --git a/UnityEngineAnalyzer.CLI/Reporting/DiagnosticInfo.cs b/UnityEngineAnalyzer.CLI/Reporting/DiagnosticInfo.cs index e092be6..c2fd93e 100644 --- a/UnityEngineAnalyzer.CLI/Reporting/DiagnosticInfo.cs +++ b/UnityEngineAnalyzer.CLI/Reporting/DiagnosticInfo.cs @@ -1,19 +1,24 @@ namespace UnityEngineAnalyzer.CLI.Reporting { - public enum DiagnosticInfoSeverity - { - Hidden = 0, - Info = 1, - Warning = 2, - Error = 3 - } + public class DiagnosticInfo { + //TODO: Rename this to something like AnalysisResult + public string Id { get; set; } public string Message { get; set; } public string FileName { get; set; } public int LineNumber { get; set; } + public int CharacterPosition { get; set; } public DiagnosticInfoSeverity Severity { get; set; } + + public enum DiagnosticInfoSeverity + { + Hidden = 0, + Info = 1, + Warning = 2, + Error = 3 + } } } \ No newline at end of file diff --git a/UnityEngineAnalyzer.CLI/Reporting/IAnalyzerExporter.cs b/UnityEngineAnalyzer.CLI/Reporting/IAnalyzerExporter.cs index 62b65ad..3aee835 100644 --- a/UnityEngineAnalyzer.CLI/Reporting/IAnalyzerExporter.cs +++ b/UnityEngineAnalyzer.CLI/Reporting/IAnalyzerExporter.cs @@ -6,7 +6,8 @@ namespace UnityEngineAnalyzer.CLI.Reporting public interface IAnalyzerExporter { void AppendDiagnostic(DiagnosticInfo diagnosticInfo); - void Finish(TimeSpan duration); + void FinalizeExporter(TimeSpan duration); void InitializeExporter(FileInfo projectFile); + void NotifyException(Exception exception); } } \ No newline at end of file diff --git a/UnityEngineAnalyzer.CLI/Reporting/JsonAnalyzerExporter.cs b/UnityEngineAnalyzer.CLI/Reporting/JsonAnalyzerExporter.cs index a0e7b6c..8645bce 100644 --- a/UnityEngineAnalyzer.CLI/Reporting/JsonAnalyzerExporter.cs +++ b/UnityEngineAnalyzer.CLI/Reporting/JsonAnalyzerExporter.cs @@ -9,7 +9,7 @@ public class JsonAnalyzerExporter : IAnalyzerExporter { private const string JsonReportFileName = "report.json"; private const string HtmlReportFileName = "UnityReport.html"; - private const DiagnosticInfoSeverity MinimalSeverity = DiagnosticInfoSeverity.Warning; + private const DiagnosticInfo.DiagnosticInfoSeverity MinimalSeverity = DiagnosticInfo.DiagnosticInfoSeverity.Warning; private JsonTextWriter _jsonWriter; @@ -25,14 +25,14 @@ public void AppendDiagnostic(DiagnosticInfo diagnosticInfo) } } - public void Finish(TimeSpan duration) + public void FinalizeExporter(TimeSpan duration) { _jsonWriter.WriteEndArray(); _jsonWriter.WriteEndObject(); _jsonWriter.Close(); - - File.Copy(HtmlReportFileName, _destinationReportFile, true); + //Console.WriteLine(Process.GetCurrentProcess().StartInfo.WorkingDirectory); + //File.Copy(HtmlReportFileName, _destinationReportFile, true); //NOTE: This code might be temporary as it assumes that the CLI is being executed interactively //Process.Start(_destinationReportFile); @@ -68,5 +68,10 @@ public void InitializeExporter(FileInfo projectFile) _jsonSerializer.Formatting = Formatting.Indented; } + + public void NotifyException(Exception exception) + { + throw new NotImplementedException(); + } } } \ No newline at end of file diff --git a/UnityEngineAnalyzer.CLI/Reporting/StandardOutputAnalyzerReporter.cs b/UnityEngineAnalyzer.CLI/Reporting/StandardOutputAnalyzerReporter.cs new file mode 100644 index 0000000..3be7a79 --- /dev/null +++ b/UnityEngineAnalyzer.CLI/Reporting/StandardOutputAnalyzerReporter.cs @@ -0,0 +1,76 @@ +using System; +using System.IO; + +namespace UnityEngineAnalyzer.CLI.Reporting +{ + public class StandardOutputAnalyzerReporter : IAnalyzerExporter + { + protected const string ConsoleSeparator = "\t"; + protected const DiagnosticInfo.DiagnosticInfoSeverity MinimalSeverity = DiagnosticInfo.DiagnosticInfoSeverity.Warning; + + protected const string FailurePrefix = "# "; + + public void AppendDiagnostic(DiagnosticInfo diagnosticInfo) + { + if (diagnosticInfo.Severity < MinimalSeverity) + { + return; + } + + Console.Write(diagnosticInfo.Id); + Console.Write(ConsoleSeparator); + + Console.ForegroundColor = ConsoleColorFromSeverity(diagnosticInfo.Severity); + Console.Write(diagnosticInfo.Severity.ToString()); + Console.Write(ConsoleSeparator); + + Console.ForegroundColor = ConsoleColor.Cyan; + Console.Write(diagnosticInfo.Message); + Console.ResetColor(); + Console.WriteLine(@"{0}{1}{0}{2},{3}", ConsoleSeparator,diagnosticInfo.FileName, diagnosticInfo.LineNumber, diagnosticInfo.CharacterPosition); + + } + + private ConsoleColor ConsoleColorFromSeverity(DiagnosticInfo.DiagnosticInfoSeverity severity) + { + switch (severity) + { + case DiagnosticInfo.DiagnosticInfoSeverity.Hidden: + return ConsoleColor.Gray; + case DiagnosticInfo.DiagnosticInfoSeverity.Info: + return ConsoleColor.Green; + case DiagnosticInfo.DiagnosticInfoSeverity.Warning: + return ConsoleColor.Yellow; + case DiagnosticInfo.DiagnosticInfoSeverity.Error: + return ConsoleColor.Red; + default: + return ConsoleColor.White; + } + } + + public virtual void FinalizeExporter(TimeSpan duration) + { + } + + public virtual void InitializeExporter(FileInfo projectFile) + { + } + + public virtual void NotifyException(Exception exception) + { + Console.ForegroundColor = ConsoleColor.Red; + + var delimeters = new[] {"\r", "\n", Environment.NewLine }; + var exceptionLines = exception.ToString().Split(delimeters, StringSplitOptions.RemoveEmptyEntries); + + foreach (var exceptionLine in exceptionLines) + { + Console.WriteLine(FailurePrefix + exceptionLine); + } + + Console.WriteLine(FailurePrefix); + + Console.ResetColor(); + } + } +} diff --git a/UnityEngineAnalyzer.CLI/SolutionAnalyzer.cs b/UnityEngineAnalyzer.CLI/SolutionAnalyzer.cs index 724ee98..0e13ea8 100644 --- a/UnityEngineAnalyzer.CLI/SolutionAnalyzer.cs +++ b/UnityEngineAnalyzer.CLI/SolutionAnalyzer.cs @@ -44,13 +44,22 @@ private ImmutableArray GetAnalyzers() return analyzers; } - private async Task AnalyzeProject(Project project, ImmutableArray analyzers, AnalyzerReport report) + private async Task AnalyzeProject(Project project, ImmutableArray analyzers, + AnalyzerReport report) { - var compilation = await project.GetCompilationAsync(); + try + { + var compilation = await project.GetCompilationAsync(); + + var diagnosticResults = await compilation.WithAnalyzers(analyzers).GetAnalyzerDiagnosticsAsync(); - var diagnosticResults = await compilation.WithAnalyzers(analyzers).GetAnalyzerDiagnosticsAsync(); + report.AppendDiagnostics(diagnosticResults); + } + catch (Exception exception) + { + report.NotifyException(exception); + } - report.AppendDiagnostics(diagnosticResults); } } diff --git a/UnityEngineAnalyzer.CLI/UnityEngineAnalyzer.CLI.csproj b/UnityEngineAnalyzer.CLI/UnityEngineAnalyzer.CLI.csproj index 1e6c031..37697e1 100644 --- a/UnityEngineAnalyzer.CLI/UnityEngineAnalyzer.CLI.csproj +++ b/UnityEngineAnalyzer.CLI/UnityEngineAnalyzer.CLI.csproj @@ -110,6 +110,7 @@ + From d6c87b1a28c4a1b0c471c9e3b1994b774a0fbe62 Mon Sep 17 00:00:00 2001 From: Vinny Date: Thu, 29 Dec 2016 10:32:01 -0500 Subject: [PATCH 09/46] Update readme.md --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index b655f2a..a9abcba 100644 --- a/readme.md +++ b/readme.md @@ -7,7 +7,7 @@ UnityEngineAnalyzer is a set of Roslyn analyzers that aim to detect common probl Comand Line Interface --------------------- -In order to use the Command Line Interface (CLI), download the latest release of UnityEngineAnalyzer then unzip the archive. +In order to use the Command Line Interface (CLI), download the latest release of UnityEngineAnalyzer then unzip the archive (https://github.com/vad710/UnityEngineAnalyzer/releases). 1. Open a Command Prompt or Powershell Window 1. Run `UnityEngineAnalyzer.CLI.exe ` From af7b247bb0d734da109a93e342cceab86828e493 Mon Sep 17 00:00:00 2001 From: Vinny Date: Wed, 11 Jan 2017 18:29:34 -0500 Subject: [PATCH 10/46] Tentative fix for issue #5 --- UnityEngineAnalyzer.CLI/AnalyzerReport.cs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/UnityEngineAnalyzer.CLI/AnalyzerReport.cs b/UnityEngineAnalyzer.CLI/AnalyzerReport.cs index 0d4cd60..31a2f85 100644 --- a/UnityEngineAnalyzer.CLI/AnalyzerReport.cs +++ b/UnityEngineAnalyzer.CLI/AnalyzerReport.cs @@ -26,21 +26,29 @@ public void AppendDiagnostics(IEnumerable diagnosticResults) foreach (var diagnostic in diagnosticResults) { - var locationSpan = diagnostic.Location.SourceSpan; - var lineSpan = diagnostic.Location.SourceTree.GetLineSpan(locationSpan); + var location = diagnostic.Location; + var lineNumber = 0; + var characterPosition = 0; + + if (location != Location.None) + { + var locationSpan = diagnostic.Location.SourceSpan; + var lineSpan = diagnostic.Location.SourceTree.GetLineSpan(locationSpan); + lineNumber = lineSpan.StartLinePosition.Line; + characterPosition = lineSpan.StartLinePosition.Character; + } var diagnosticInfo = new DiagnosticInfo { Id = diagnostic.Id, Message = diagnostic.GetMessage(), FileName = diagnostic.Location.SourceTree.FilePath, - LineNumber = lineSpan.StartLinePosition.Line, - CharacterPosition = lineSpan.StartLinePosition.Character, - Severity = (DiagnosticInfoSeverity)diagnostic.Severity + LineNumber = lineNumber, + CharacterPosition = characterPosition, + Severity = (DiagnosticInfo.DiagnosticInfoSeverity)diagnostic.Severity }; - foreach (var exporter in _exporters) { exporter.AppendDiagnostic(diagnosticInfo); From 83c101bf880aaa9224c88387421658c0793f34d3 Mon Sep 17 00:00:00 2001 From: Vinny Date: Fri, 13 Jan 2017 13:31:24 -0500 Subject: [PATCH 11/46] Updating Copyright Info --- .../UnityEngineAnalyzer/Properties/AssemblyInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Properties/AssemblyInfo.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Properties/AssemblyInfo.cs index 6bfee81..1260997 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/Properties/AssemblyInfo.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Properties/AssemblyInfo.cs @@ -9,7 +9,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("UnityEngineAnalyzer")] -[assembly: AssemblyCopyright("Copyright © Meng Hui Koh 2016")] +[assembly: AssemblyCopyright("Copyright © Meng Hui Koh, Vinny DaSilva 2016-2017")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] From 139b065e2534b8f40237e80301737c44455c0ba3 Mon Sep 17 00:00:00 2001 From: Vinny Date: Fri, 13 Jan 2017 13:56:15 -0500 Subject: [PATCH 12/46] Minor Tweaks for Interactivity version --- UnityEngineAnalyzer.CLI/Program.cs | 19 ++++++++----------- .../Reporting/ConsoleAnalyzerExporter.cs | 3 +++ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/UnityEngineAnalyzer.CLI/Program.cs b/UnityEngineAnalyzer.CLI/Program.cs index 6af6b78..0fe0c69 100644 --- a/UnityEngineAnalyzer.CLI/Program.cs +++ b/UnityEngineAnalyzer.CLI/Program.cs @@ -10,14 +10,13 @@ namespace UnityEngineAnalyzer.CLI { public class Program { - private static Dictionary _exporters = new Dictionary(); + private static readonly Dictionary AvailableExporters = new Dictionary(); static Program() { - _exporters.Add(nameof(ConsoleAnalyzerExporter), typeof(ConsoleAnalyzerExporter)); - _exporters.Add(nameof(JsonAnalyzerExporter), typeof(JsonAnalyzerExporter)); - _exporters.Add(nameof(StandardOutputAnalyzerReporter), typeof(StandardOutputAnalyzerReporter)); - + AvailableExporters.Add(nameof(JsonAnalyzerExporter), typeof(JsonAnalyzerExporter)); + AvailableExporters.Add(nameof(StandardOutputAnalyzerReporter), typeof(StandardOutputAnalyzerReporter)); + AvailableExporters.Add(nameof(ConsoleAnalyzerExporter), typeof(ConsoleAnalyzerExporter)); } @@ -40,15 +39,16 @@ public static void Main(string[] args) //NOTE: This could be configurable via the CLI at some point var report = new AnalyzerReport(); - if (args.Length > 1 && _exporters.ContainsKey(args[1])) + if (args.Length > 1 && AvailableExporters.ContainsKey(args[1])) { - var exporterInstance = Activator.CreateInstance(_exporters[args[1]]); + var exporterInstance = Activator.CreateInstance(AvailableExporters[args[1]]); report.AddExporter(exporterInstance as IAnalyzerExporter); } else { - report.AddExporter(new ConsoleAnalyzerExporter()); + //It's generally a good idea to make sure that the Console Exporter is last since it is interactive report.AddExporter(new JsonAnalyzerExporter()); + report.AddExporter(new ConsoleAnalyzerExporter()); } @@ -77,9 +77,6 @@ public static void Main(string[] args) Console.WriteLine("There was an exception running the analysis"); Console.WriteLine(generalException.ToString()); } - - - } diff --git a/UnityEngineAnalyzer.CLI/Reporting/ConsoleAnalyzerExporter.cs b/UnityEngineAnalyzer.CLI/Reporting/ConsoleAnalyzerExporter.cs index add0467..5a7127f 100644 --- a/UnityEngineAnalyzer.CLI/Reporting/ConsoleAnalyzerExporter.cs +++ b/UnityEngineAnalyzer.CLI/Reporting/ConsoleAnalyzerExporter.cs @@ -11,6 +11,9 @@ public override void FinalizeExporter(TimeSpan duration) Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("Console Export Finished ({0})", duration); Console.ResetColor(); + + Console.WriteLine("Press any key to exit"); + Console.ReadKey(); } public override void InitializeExporter(FileInfo projectFile) From dbe50257f39d5eaadb9552f8f3221414f52f99c9 Mon Sep 17 00:00:00 2001 From: Vinny Date: Tue, 17 Jan 2017 17:58:02 -0500 Subject: [PATCH 13/46] Fixing NotImplemented exception in the Json Exporter --- .../Reporting/JsonAnalyzerExporter.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/UnityEngineAnalyzer.CLI/Reporting/JsonAnalyzerExporter.cs b/UnityEngineAnalyzer.CLI/Reporting/JsonAnalyzerExporter.cs index 8645bce..4f3c8c3 100644 --- a/UnityEngineAnalyzer.CLI/Reporting/JsonAnalyzerExporter.cs +++ b/UnityEngineAnalyzer.CLI/Reporting/JsonAnalyzerExporter.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using Newtonsoft.Json; @@ -14,6 +15,7 @@ public class JsonAnalyzerExporter : IAnalyzerExporter private JsonTextWriter _jsonWriter; private readonly JsonSerializer _jsonSerializer = new JsonSerializer(); + private readonly List _exceptions = new List(); private string _destinationReportFile; @@ -28,11 +30,21 @@ public void AppendDiagnostic(DiagnosticInfo diagnosticInfo) public void FinalizeExporter(TimeSpan duration) { _jsonWriter.WriteEndArray(); + + _jsonWriter.WritePropertyName("Exceptions"); + _jsonWriter.WriteStartArray(); + + foreach (var exception in _exceptions) + { + _jsonSerializer.Serialize(_jsonWriter, exception); + } + _jsonWriter.WriteEndArray(); + _jsonWriter.WriteEndObject(); _jsonWriter.Close(); //Console.WriteLine(Process.GetCurrentProcess().StartInfo.WorkingDirectory); - //File.Copy(HtmlReportFileName, _destinationReportFile, true); + File.Copy(HtmlReportFileName, _destinationReportFile, true); //NOTE: This code might be temporary as it assumes that the CLI is being executed interactively //Process.Start(_destinationReportFile); @@ -71,7 +83,7 @@ public void InitializeExporter(FileInfo projectFile) public void NotifyException(Exception exception) { - throw new NotImplementedException(); + _exceptions.Add(exception); } } } \ No newline at end of file From 326c9d5629190ac7f89ce94e1bbf042d01f60caf Mon Sep 17 00:00:00 2001 From: Vinny Date: Tue, 17 Jan 2017 17:58:38 -0500 Subject: [PATCH 14/46] Removing dependency on Location when it is Location.None --- UnityEngineAnalyzer.CLI/AnalyzerReport.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/UnityEngineAnalyzer.CLI/AnalyzerReport.cs b/UnityEngineAnalyzer.CLI/AnalyzerReport.cs index 31a2f85..4444837 100644 --- a/UnityEngineAnalyzer.CLI/AnalyzerReport.cs +++ b/UnityEngineAnalyzer.CLI/AnalyzerReport.cs @@ -29,20 +29,22 @@ public void AppendDiagnostics(IEnumerable diagnosticResults) var location = diagnostic.Location; var lineNumber = 0; var characterPosition = 0; + var fileName = string.Empty; if (location != Location.None) { - var locationSpan = diagnostic.Location.SourceSpan; - var lineSpan = diagnostic.Location.SourceTree.GetLineSpan(locationSpan); + var locationSpan = location.SourceSpan; + var lineSpan = location.SourceTree.GetLineSpan(locationSpan); lineNumber = lineSpan.StartLinePosition.Line; characterPosition = lineSpan.StartLinePosition.Character; + fileName = location.SourceTree?.FilePath; } var diagnosticInfo = new DiagnosticInfo { Id = diagnostic.Id, Message = diagnostic.GetMessage(), - FileName = diagnostic.Location.SourceTree.FilePath, + FileName = fileName, LineNumber = lineNumber, CharacterPosition = characterPosition, Severity = (DiagnosticInfo.DiagnosticInfoSeverity)diagnostic.Severity From 5a0afd1bf81bb6c4e8d20d8ef769376090996c98 Mon Sep 17 00:00:00 2001 From: Vinny Date: Mon, 23 Jan 2017 17:55:09 -0500 Subject: [PATCH 15/46] Adding more info to exception --- UnityEngineAnalyzer/UnityEngineAnalyzer/RolsynExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/RolsynExtensions.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/RolsynExtensions.cs index dbdd37e..03de4b8 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/RolsynExtensions.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/RolsynExtensions.cs @@ -82,7 +82,7 @@ public static string MethodName(this InvocationExpressionSyntax invocation) } else { - throw new ArgumentException("Unable to determine name of method"); + throw new ArgumentException("Unable to determine name of method. Invocation is of type: " + invocation.Expression.GetType()); } return name; } From eefa1150c8b5028b82cdc9f102f2b8035e516736 Mon Sep 17 00:00:00 2001 From: Kasperki Date: Thu, 6 Apr 2017 23:24:34 +0300 Subject: [PATCH 16/46] Animator Analyzer Checks that project is using hash and not strings. --- .../DoNotUseStateNameAnalyzerTests.cs | 86 ++++++++++++ .../UnityEngineAnalyzer.Test.csproj | 1 + .../Animator/DoNotUseStateNameAnalyzer.cs | 66 +++++++++ .../DoNotUseStateNameResource.Designer.cs | 91 ++++++++++++ .../Animator/DoNotUseStateNameResource.resx | 132 ++++++++++++++++++ .../DiagnosticDescriptors.cs | 13 +- .../UnityEngineAnalyzer/DiagnosticIDs.cs | 1 + .../UnityEngineAnalyzer.csproj | 10 ++ 8 files changed, 399 insertions(+), 1 deletion(-) create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Animator/DoNotUseStateNameAnalyzerTests.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Animator/DoNotUseStateNameAnalyzer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Animator/DoNotUseStateNameResource.Designer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Animator/DoNotUseStateNameResource.resx diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Animator/DoNotUseStateNameAnalyzerTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Animator/DoNotUseStateNameAnalyzerTests.cs new file mode 100644 index 0000000..b245e8f --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Animator/DoNotUseStateNameAnalyzerTests.cs @@ -0,0 +1,86 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.Animator; + +namespace UnityEngineAnalyzer.Test.Animator +{ + + [TestFixture] + sealed class DoNotSetAnimatorParameterWithNameAnalyzerTests : AnalyzerTestFixture + { + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new DoNotUseStateNameAnalyzer(); + + [Test] + public void AnimatorSetFloatStringName() + { + const string code = @" +using UnityEngine; + +class C : MonoBehaviour +{ + Animator animator; + + void Start() + { + [|animator.SetFloat(""Run"", 1.2f)|]; + } +}"; + + Document document; + TextSpan span; + TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); + + HasDiagnostic(document, span, DiagnosticIDs.DoNotUseStateNameInAnimator); + } + + [Test] + public void AnimatorSetIntStringName() + { + const string code = @" +using UnityEngine; + +class C : MonoBehaviour +{ + Animator animator; + + void Start() + { + [|animator.SetInteger(""Walk"", 1)|]; + } +}"; + + Document document; + TextSpan span; + TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); + + HasDiagnostic(document, span, DiagnosticIDs.DoNotUseStateNameInAnimator); + } + + [Test] + public void AnimatorSetBoolStringName() + { + const string code = @" +using UnityEngine; + +class C : MonoBehaviour +{ + Animator animator; + + void Start() + { + [|animator.SetBool(""Fly"", true)|]; + } +}"; + + Document document; + TextSpan span; + TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); + + HasDiagnostic(document, span, DiagnosticIDs.DoNotUseStateNameInAnimator); + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj index d5501b0..cc86884 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj @@ -126,6 +126,7 @@ + diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Animator/DoNotUseStateNameAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Animator/DoNotUseStateNameAnalyzer.cs new file mode 100644 index 0000000..7165657 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Animator/DoNotUseStateNameAnalyzer.cs @@ -0,0 +1,66 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using System.Collections.Immutable; + +namespace UnityEngineAnalyzer.Animator +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class DoNotUseStateNameAnalyzer : DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.DoNotUseStateName); + + private static readonly ImmutableHashSet animatorStateNameMethods = ImmutableHashSet.Create( + "GetBool", + "GetFloat", + "GetInteger", + "GetVector", + "GetQuaternion", + "SetBool", + "SetFloat", + "SetInteger", + "SetVector", + "SetQuaternion", + "SetTrigger", + "PlayInFixedTime", + "Play", + "IsParameterControlledByCurve", + "CrossFade", + "CrossFadeInFixedTime"); + + 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 (!animatorStateNameMethods.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("Animator")) + { + if (methodSymbol.Parameters[0].Type.MetadataName == "String") + { + var diagnostic = Diagnostic.Create(DiagnosticDescriptors.DoNotUseStateName, invocation.GetLocation(), containingClass.Name, methodSymbol.Name); + context.ReportDiagnostic(diagnostic); + } + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Animator/DoNotUseStateNameResource.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Animator/DoNotUseStateNameResource.Designer.cs new file mode 100644 index 0000000..3de929d --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Animator/DoNotUseStateNameResource.Designer.cs @@ -0,0 +1,91 @@ +//------------------------------------------------------------------------------ +// +// 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.Animator { + using System; + using System.Reflection; + + + /// + /// 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. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class DoNotUseStateNameResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal DoNotUseStateNameResource() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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.Animator.DoNotUseStateNameResource", typeof(DoNotUseStateNameResource).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Animator.StringToHash can convert your state name to hash, it's faster than string comparison. + /// + internal static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use stateNameHash instead of stateName. + /// + internal static string MessageFormat { + get { + return ResourceManager.GetString("MessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use stateNameHash instead of stateName. + /// + internal static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Animator/DoNotUseStateNameResource.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/Animator/DoNotUseStateNameResource.resx new file mode 100644 index 0000000..db5b682 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Animator/DoNotUseStateNameResource.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 + + + Animator.StringToHash can convert your state name to hash, it's faster than string comparison + An optional longer localizable description of the diagnostic. + + + Use stateNameHash instead of stateName + The format-able message the diagnostic displays. + + + Use stateNameHash instead of stateName + The title of the diagnostic. + + \ No newline at end of file diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs index 6d21546..0ac196e 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs @@ -1,4 +1,5 @@ using Microsoft.CodeAnalysis; +using UnityEngineAnalyzer.Animator; using UnityEngineAnalyzer.AOT; using UnityEngineAnalyzer.CompareTag; using UnityEngineAnalyzer.Coroutines; @@ -135,6 +136,16 @@ static class DiagnosticDescriptors defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true, description: new LocalizableResourceString(nameof(InvokeFunctionMissingResources.Description), InvokeFunctionMissingResources.ResourceManager, typeof(InvokeFunctionMissingResources)) - ); + ); + + public static readonly DiagnosticDescriptor DoNotUseStateName = new DiagnosticDescriptor( + id: DiagnosticIDs.DoNotUseStateNameInAnimator, + title: new LocalizableResourceString(nameof(DoNotUseStateNameResource.Title), DoNotUseStateNameResource.ResourceManager, typeof(DoNotUseStateNameResource)), + messageFormat: new LocalizableResourceString(nameof(DoNotUseStateNameResource.MessageFormat), DoNotUseStateNameResource.ResourceManager, typeof(DoNotUseStateNameResource)), + category: DiagnosticCategories.Performance, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: new LocalizableResourceString(nameof(DoNotUseStateNameResource.Description), DoNotUseStateNameResource.ResourceManager, typeof(DoNotUseStateNameResource)) + ); } } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs index 7c154b4..5d99f94 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs @@ -12,6 +12,7 @@ public static class DiagnosticIDs public const string DoNotUseForEachInUpdate = "UEA0007"; public const string UnsealedDerivedClass = "UEA0008"; public const string InvokeFunctionMissing = "UEA0009"; + public const string DoNotUseStateNameInAnimator = "UEA0010"; //NOTES: These should probably be on their own analyzer - as they are not specific to Unity public const string DoNotUseRemoting = "AOT0001"; diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj b/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj index 5ee6832..bd0fcda 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj @@ -32,6 +32,12 @@ 4 + + True + True + DoNotUseStateNameResource.resx + + True @@ -112,6 +118,10 @@ + + ResXFileCodeGenerator + DoNotUseStateNameResource.Designer.cs + ResXFileCodeGenerator DoNotUseReflectionEmitResources.Designer.cs From 7b7958ff5c048fc3e76c3f1fa8a0a910210ae4e3 Mon Sep 17 00:00:00 2001 From: Kasperki Date: Sat, 30 Sep 2017 14:44:44 +0300 Subject: [PATCH 17/46] Material string property analyzer methods --- .../DoNotUseStringPropertyNamesTests.cs | 86 ++++++++++++ .../UnityEngineAnalyzer.Test.csproj | 1 + .../DiagnosticDescriptors.cs | 11 ++ .../UnityEngineAnalyzer/DiagnosticIDs.cs | 1 + .../DoNotUseStringPropertyNamesAnalyzer.cs | 75 ++++++++++ ...UseStringPropertyNamesResource.Designer.cs | 91 ++++++++++++ .../DoNotUseStringPropertyNamesResource.resx | 132 ++++++++++++++++++ .../UnityEngineAnalyzer.csproj | 10 ++ 8 files changed, 407 insertions(+) create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Material/DoNotUseStringPropertyNamesTests.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Material/DoNotUseStringPropertyNamesAnalyzer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Material/DoNotUseStringPropertyNamesResource.Designer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Material/DoNotUseStringPropertyNamesResource.resx diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Material/DoNotUseStringPropertyNamesTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Material/DoNotUseStringPropertyNamesTests.cs new file mode 100644 index 0000000..7867e5c --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Material/DoNotUseStringPropertyNamesTests.cs @@ -0,0 +1,86 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.Material; + +namespace UnityEngineAnalyzer.Test.Material +{ + + [TestFixture] + sealed class DoNotUseStringPropertyNamesAnalyzerTests : AnalyzerTestFixture + { + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new DoNotUseStringPropertyNamesAnalyzer(); + + [Test] + public void MaterialGetFloatWithStringProperty() + { + const string code = @" +using UnityEngine; + +class C : MonoBehaviour +{ + Material material; + + void Start() + { + [|material.GetFloat(""_Shininess"")|]; + } +}"; + + Document document; + TextSpan span; + TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); + + HasDiagnostic(document, span, DiagnosticIDs.DoNotUseStringPropertyNamesInMaterial); + } + + [Test] + public void MaterialSetFloatWithStringProperty() + { + const string code = @" +using UnityEngine; + +class C : MonoBehaviour +{ + Material material; + + void Start() + { + [|material.SetFloat(""_Shininess"", 1.2f)|]; + } +}"; + + Document document; + TextSpan span; + TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); + + HasDiagnostic(document, span, DiagnosticIDs.DoNotUseStringPropertyNamesInMaterial); + } + + [Test] + public void MaterialSetMatrixWithStringProperty() + { + const string code = @" +using UnityEngine; + +class C : MonoBehaviour +{ + Material material; + + void Start() + { + [|material.SetVector(""_WaveAndDistance"", Vector3.one)|]; + } +}"; + + Document document; + TextSpan span; + TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); + + HasDiagnostic(document, span, DiagnosticIDs.DoNotUseStringPropertyNamesInMaterial); + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj index cc86884..92ddb09 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj @@ -135,6 +135,7 @@ + diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs index 0ac196e..5f71662 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs @@ -7,6 +7,7 @@ using UnityEngineAnalyzer.FindMethodsInUpdate; using UnityEngineAnalyzer.ForEachInUpdate; using UnityEngineAnalyzer.IL2CPP; +using UnityEngineAnalyzer.Material; using UnityEngineAnalyzer.OnGUI; using UnityEngineAnalyzer.StringMethods; @@ -147,5 +148,15 @@ static class DiagnosticDescriptors isEnabledByDefault: true, description: new LocalizableResourceString(nameof(DoNotUseStateNameResource.Description), DoNotUseStateNameResource.ResourceManager, typeof(DoNotUseStateNameResource)) ); + + public static readonly DiagnosticDescriptor DoNotUseStringPropertyNames = new DiagnosticDescriptor( + id: DiagnosticIDs.DoNotUseStringPropertyNamesInMaterial, + title: new LocalizableResourceString(nameof(DoNotUseStringPropertyNamesResource.Title), DoNotUseStringPropertyNamesResource.ResourceManager, typeof(DoNotUseStringPropertyNamesResource)), + messageFormat: new LocalizableResourceString(nameof(DoNotUseStringPropertyNamesResource.MessageFormat), DoNotUseStringPropertyNamesResource.ResourceManager, typeof(DoNotUseStringPropertyNamesResource)), + category: DiagnosticCategories.Performance, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: new LocalizableResourceString(nameof(DoNotUseStringPropertyNamesResource.Description), DoNotUseStringPropertyNamesResource.ResourceManager, typeof(DoNotUseStringPropertyNamesResource)) + ); } } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs index 5d99f94..6e339ca 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs @@ -13,6 +13,7 @@ public static class DiagnosticIDs public const string UnsealedDerivedClass = "UEA0008"; public const string InvokeFunctionMissing = "UEA0009"; public const string DoNotUseStateNameInAnimator = "UEA0010"; + public const string DoNotUseStringPropertyNamesInMaterial = "UEA0011"; //NOTES: These should probably be on their own analyzer - as they are not specific to Unity public const string DoNotUseRemoting = "AOT0001"; diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Material/DoNotUseStringPropertyNamesAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Material/DoNotUseStringPropertyNamesAnalyzer.cs new file mode 100644 index 0000000..b18cad7 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Material/DoNotUseStringPropertyNamesAnalyzer.cs @@ -0,0 +1,75 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using System.Collections.Immutable; + +namespace UnityEngineAnalyzer.Material +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class DoNotUseStringPropertyNamesAnalyzer : DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.DoNotUseStringPropertyNames); + + private static readonly ImmutableHashSet materialStringPropertyMethods = ImmutableHashSet.Create( + "GetColor", + "GetColorArray", + "GetFloat", + "GetFloatArray", + "GetInt", + "GetMatrix", + "GetMatrixArray", + "GetTexture", + "GetTextureOffset", + "GetTextureScale", + "GetVector", + "GetVectorArray", + "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 (!materialStringPropertyMethods.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.DoNotUseStringPropertyNames, invocation.GetLocation(), containingClass.Name, methodSymbol.Name); + context.ReportDiagnostic(diagnostic); + } + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Material/DoNotUseStringPropertyNamesResource.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Material/DoNotUseStringPropertyNamesResource.Designer.cs new file mode 100644 index 0000000..7a95cd1 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Material/DoNotUseStringPropertyNamesResource.Designer.cs @@ -0,0 +1,91 @@ +//------------------------------------------------------------------------------ +// +// 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.Material { + using System; + using System.Reflection; + + + /// + /// 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. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class DoNotUseStringPropertyNamesResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal DoNotUseStringPropertyNamesResource() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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.Material.DoNotUseStringPropertyNamesResource", typeof(DoNotUseStringPropertyNamesResource).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Integer comparison is faster than string comparisons, Use Shader.PropertyToID("name");. + /// + internal static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use integer proerty ID instead. + /// + internal static string MessageFormat { + get { + return ResourceManager.GetString("MessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use integer proerty ID instead. + /// + internal static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Material/DoNotUseStringPropertyNamesResource.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/Material/DoNotUseStringPropertyNamesResource.resx new file mode 100644 index 0000000..c702a54 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Material/DoNotUseStringPropertyNamesResource.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 + + + Integer comparison is faster than string comparisons, Use Shader.PropertyToID("name"); + An optional longer localizable description of the diagnostic. + + + Use integer proerty ID instead + The format-able message the diagnostic displays. + + + Use integer proerty ID instead + The title of the diagnostic. + + \ No newline at end of file diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj b/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj index bd0fcda..6fb8a0b 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj @@ -95,6 +95,12 @@ True UnsealedDerivedClassResources.resx + + + True + True + DoNotUseStringPropertyNamesResource.resx + @@ -158,6 +164,10 @@ ResXFileCodeGenerator UnsealedDerivedClassResources.Designer.cs + + ResXFileCodeGenerator + DoNotUseStringPropertyNamesResource.Designer.cs + ResXFileCodeGenerator DoNotUseOnGUIResources.Designer.cs From 2761532daa560fc1b4481a007e31d61bbfb5eb3b Mon Sep 17 00:00:00 2001 From: Kasperki Date: Sun, 1 Oct 2017 09:29:15 +0300 Subject: [PATCH 18/46] Updated InvokeFunctionMissingnAnalyzer to be consistent with other Analyzers --- .../DoNotUseStringMethodsAnalyzerTests.cs | 78 --------------- .../InvokeFunctionMissingAnalyzerTests.cs | 94 +++++++++++++++++++ .../UnityEngineAnalyzer.Test.csproj | 1 + .../InvokeFunctionMissingAnalyzer.cs | 10 +- 4 files changed, 97 insertions(+), 86 deletions(-) create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer.Test/StringMethods/InvokeFunctionMissingAnalyzerTests.cs diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/StringMethods/DoNotUseStringMethodsAnalyzerTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/StringMethods/DoNotUseStringMethodsAnalyzerTests.cs index 3a86930..98a9816 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/StringMethods/DoNotUseStringMethodsAnalyzerTests.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/StringMethods/DoNotUseStringMethodsAnalyzerTests.cs @@ -186,83 +186,5 @@ class C : MonoBehaviour HasDiagnostic(document, span, DiagnosticIDs.DoNotUseStringMethods); } - - [Test] - public void InvokeUsedInMonoBehaviourClass() - { - const string code = @" -using UnityEngine; - -class C : MonoBehaviour -{ - void Start() { [|Invoke(string.Empty, 0f)|]; } -}"; - - Document document; - TextSpan span; - TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); - - HasDiagnostic(document, span, DiagnosticIDs.DoNotUseStringMethods); - } - - [Test] - public void InvokeUsedByMonoBehaviourClass() - { - const string code = @" -using UnityEngine; - -class CC : MonoBehaviour { } - -class C : MonoBehaviour -{ - private CC cc; - void Start() { [|cc.Invoke(string.Empty, 0f)|]; } -}"; - - Document document; - TextSpan span; - TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); - - HasDiagnostic(document, span, DiagnosticIDs.DoNotUseStringMethods); - } - - [Test] - public void InvokeRepeatingUsedInMonoBehaviourClass() - { - const string code = @" -using UnityEngine; - -class C : MonoBehaviour -{ - void Start() { [|InvokeRepeating(string.Empty, 0f, 0f)|]; } -}"; - - Document document; - TextSpan span; - TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); - - HasDiagnostic(document, span, DiagnosticIDs.DoNotUseStringMethods); - } - - [Test] - public void InvokeRepeatingUsedByMonoBehaviourClass() - { - const string code = @" -using UnityEngine; - -class CC : MonoBehaviour { } - -class C : MonoBehaviour -{ - private CC cc; - void Start() { [|cc.InvokeRepeating(string.Empty, 0f, 0f)|]; } -}"; - - Document document; - TextSpan span; - TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); - - HasDiagnostic(document, span, DiagnosticIDs.DoNotUseStringMethods); - } } } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/StringMethods/InvokeFunctionMissingAnalyzerTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/StringMethods/InvokeFunctionMissingAnalyzerTests.cs new file mode 100644 index 0000000..e1c4939 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/StringMethods/InvokeFunctionMissingAnalyzerTests.cs @@ -0,0 +1,94 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.StringMethods; + +namespace UnityEngineAnalyzer.Test.StringMethods +{ + [TestFixture] + sealed class InvokeFunctionMissingAnalyzerTests : AnalyzerTestFixture + { + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new InvokeFunctionMissingAnalyzer(); + + [Test] + public void InvokeUsedInMonoBehaviourClass() + { + const string code = @" +using UnityEngine; + +class C : MonoBehaviour +{ + void Start() { [|Invoke(string.Empty, 0f)|]; } +}"; + + Document document; + TextSpan span; + TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); + + HasDiagnostic(document, span, DiagnosticIDs.InvokeFunctionMissing); + } + + [Test] + public void InvokeUsedByMonoBehaviourClass() + { + const string code = @" +using UnityEngine; + +class CC : MonoBehaviour { } + +class C : MonoBehaviour +{ + private CC cc; + void Start() { [|cc.Invoke(string.Empty, 0f)|]; } +}"; + + Document document; + TextSpan span; + TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); + + HasDiagnostic(document, span, DiagnosticIDs.InvokeFunctionMissing); + } + + [Test] + public void InvokeRepeatingUsedInMonoBehaviourClass() + { + const string code = @" +using UnityEngine; + +class C : MonoBehaviour +{ + void Start() { [|InvokeRepeating(string.Empty, 0f, 0f)|]; } +}"; + + Document document; + TextSpan span; + TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); + + HasDiagnostic(document, span, DiagnosticIDs.InvokeFunctionMissing); + } + + [Test] + public void InvokeRepeatingUsedByMonoBehaviourClass() + { + const string code = @" +using UnityEngine; + +class CC : MonoBehaviour { } + +class C : MonoBehaviour +{ + private CC cc; + void Start() { [|cc.InvokeRepeating(string.Empty, 0f, 0f)|]; } +}"; + + Document document; + TextSpan span; + TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); + + HasDiagnostic(document, span, DiagnosticIDs.InvokeFunctionMissing); + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj index cc86884..8796b7f 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj @@ -139,6 +139,7 @@ + diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/InvokeFunctionMissingAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/InvokeFunctionMissingAnalyzer.cs index 870d020..a771b20 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/InvokeFunctionMissingAnalyzer.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/InvokeFunctionMissingAnalyzer.cs @@ -15,6 +15,7 @@ public class InvokeFunctionMissingAnalyzer : DiagnosticAnalyzer private static readonly ImmutableHashSet InvokeMethods = ImmutableHashSet.Create("Invoke", "InvokeRepeating"); private static readonly string InvokeMethodTypeName = "UnityEngine.MonoBehaviour"; + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.InvokeFunctionMissing); public override void Initialize(AnalysisContext context) { @@ -53,19 +54,12 @@ private void AnalyzeInvocation(SyntaxNodeAnalysisContext context) if (invokeEndPoint == null) { - var diagnostic = Diagnostic.Create(SupportedDiagnostics.First(), firstArgumentExpression.GetLocation(), - methodName, invokedMethodName); - + var diagnostic = Diagnostic.Create(DiagnosticDescriptors.InvokeFunctionMissing, invocation.GetLocation()); context.ReportDiagnostic(diagnostic); } } - - - } } - - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.InvokeFunctionMissing); } } \ No newline at end of file From 22f85b7c8a6389eaec7447b47b72b45fd489076b Mon Sep 17 00:00:00 2001 From: Kasperki Date: Sun, 1 Oct 2017 17:40:33 +0300 Subject: [PATCH 19/46] Camera Analyzer: checks for Camera.main usage in Updates. --- .../Camera/CameraMainAnalyzerTests.cs | 107 ++++++++++++++ .../UnityEngineAnalyzer.Test.csproj | 1 + .../Camera/CameraMainAnalyzer.cs | 89 ++++++++++++ .../Camera/CameraMainResource.Designer.cs | 91 ++++++++++++ .../Camera/CameraMainResource.resx | 132 ++++++++++++++++++ .../DiagnosticDescriptors.cs | 11 ++ .../UnityEngineAnalyzer/DiagnosticIDs.cs | 1 + .../UnityEngineAnalyzer.csproj | 10 ++ 8 files changed, 442 insertions(+) create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Camera/CameraMainAnalyzerTests.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/CameraMainAnalyzer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/CameraMainResource.Designer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/CameraMainResource.resx diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Camera/CameraMainAnalyzerTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Camera/CameraMainAnalyzerTests.cs new file mode 100644 index 0000000..7fba584 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Camera/CameraMainAnalyzerTests.cs @@ -0,0 +1,107 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.Camera; + +namespace UnityEngineAnalyzer.Test.Camera +{ + [TestFixture] + sealed class CameraMainAnalyzerTests : AnalyzerTestFixture + { + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new CameraMainAnalyzer(); + + [Test] + public void CameraMainShouldRaiseWarningOnMemberExpression() + { + const string code = @" +using UnityEngine; + +class C : MonoBehaviour +{ + void Update() + { + var orthographicSize = [|Camera.main.orthographicSize|]; + } +}"; + + Document document; + TextSpan span; + TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); + + HasDiagnostic(document, span, DiagnosticIDs.CameraMainIsSlow); + } + + [Test] + public void CameraMainShouldRaiseWarningOnMethod() + { + const string code = @" +using UnityEngine; + +class C : MonoBehaviour +{ + void Update() + { + var size = [|Camera.main.ScreenPointToRay|](new Vector3(200, 200, 0)); + } +}"; + + Document document; + TextSpan span; + TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); + + HasDiagnostic(document, span, DiagnosticIDs.CameraMainIsSlow); + } + + [Test] + public void CameraMainShouldThrowWarningFromCalledMethods() + { + const string code = @" +using UnityEngine; + +class C : MonoBehaviour +{ + void Update() + { + Call(); + + //Camera.main.transform.position = Vector3.one; + } + + public void Call() + { + var size = [|Camera.main.ScreenPointToRay|](new Vector3(200, 200, 0)); + } +}"; + + Document document; + TextSpan span; + TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); + + HasDiagnostic(document, span, DiagnosticIDs.CameraMainIsSlow); + } + + [Test] + public void CameraMainShouldThrowWarningOnlyInHotPath() + { + const string code = @" +using UnityEngine; + +class C : MonoBehaviour +{ + void Start() + { + var size = [|Camera.main.ScreenPointToRay|](new Vector3(200, 200, 0)); + } +}"; + + Document document; + TextSpan span; + TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); + + NoDiagnostic(document, DiagnosticIDs.CameraMainIsSlow); + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj index cc86884..a0b5259 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj @@ -130,6 +130,7 @@ + diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/CameraMainAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/CameraMainAnalyzer.cs new file mode 100644 index 0000000..6ca15c2 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/CameraMainAnalyzer.cs @@ -0,0 +1,89 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; + +namespace UnityEngineAnalyzer.Camera +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class CameraMainAnalyzer : DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.CameraMainIsSlow); + private List searched = new List(); + + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeClassSyntax, SyntaxKind.ClassDeclaration); + } + + public void AnalyzeClassSyntax(SyntaxNodeAnalysisContext context) + { + var monoBehaviourInfo = new MonoBehaviourInfo(context); + + monoBehaviourInfo.ForEachUpdateMethod((updateMethod) => + { + SearchCameraMain(context, updateMethod); + RecursiveMethodCrawler(context, updateMethod); + }); + } + + private void SearchCameraMain(SyntaxNodeAnalysisContext context, MethodDeclarationSyntax method) + { + var memberAccessExpression = method.DescendantNodes().OfType(); + + foreach (var memberAccess in memberAccessExpression) + { + if (searched.Contains(memberAccess)) + { + return; + } + + searched.Add(memberAccess); + + SymbolInfo symbolInfo; + if (!context.TryGetSymbolInfo(memberAccess.Expression, out symbolInfo)) + { + continue; + } + + var containingClass = symbolInfo.Symbol.ContainingType; + + if (containingClass != null && containingClass.ContainingNamespace.Name.Equals("UnityEngine") && containingClass.Name.Equals("Camera")) + { + if (symbolInfo.Symbol.Name.Equals("main")) + { + var diagnostic = Diagnostic.Create(DiagnosticDescriptors.CameraMainIsSlow, memberAccess.GetLocation(), memberAccess.Name, symbolInfo.Symbol.Name); + context.ReportDiagnostic(diagnostic); + } + } + } + } + + private void RecursiveMethodCrawler(SyntaxNodeAnalysisContext context, MethodDeclarationSyntax method) + { + var invocationAccessExpression = method.DescendantNodes().OfType(); + SearchCameraMain(context, method); + + foreach (var invalid in invocationAccessExpression) + { + SymbolInfo symbolInfo; + if (!context.TryGetSymbolInfo(invalid, out symbolInfo)) + { + continue; + } + + var methodSymbol = symbolInfo.Symbol as IMethodSymbol; + var methodDeclarations = methodSymbol.DeclaringSyntaxReferences; + + foreach (var methodDeclaration in methodDeclarations) + { + var theMethodSyntax = methodDeclaration.GetSyntax() as MethodDeclarationSyntax; + RecursiveMethodCrawler(context, theMethodSyntax); + } + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/CameraMainResource.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/CameraMainResource.Designer.cs new file mode 100644 index 0000000..a28aa7d --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/CameraMainResource.Designer.cs @@ -0,0 +1,91 @@ +//------------------------------------------------------------------------------ +// +// 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; + + + /// + /// 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. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class CameraMainResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal CameraMainResource() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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.CameraMainResource", typeof(CameraMainResource).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Camera.main uses FindByTag<"MainCamera"> cache this call instead.. + /// + internal static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Camera.main is slow. + /// + internal static string MessageFormat { + get { + return ResourceManager.GetString("MessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Camera.main is slow. + /// + internal static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/CameraMainResource.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/CameraMainResource.resx new file mode 100644 index 0000000..82e89ea --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/CameraMainResource.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 + + + Camera.main uses FindByTag<"MainCamera"> cache this call instead. + An optional longer localizable description of the diagnostic. + + + Camera.main is slow + The format-able message the diagnostic displays. + + + Camera.main is slow + 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..193c38f 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs @@ -1,6 +1,7 @@ using Microsoft.CodeAnalysis; using UnityEngineAnalyzer.Animator; using UnityEngineAnalyzer.AOT; +using UnityEngineAnalyzer.Camera; using UnityEngineAnalyzer.CompareTag; using UnityEngineAnalyzer.Coroutines; using UnityEngineAnalyzer.EmptyMonoBehaviourMethods; @@ -147,5 +148,15 @@ static class DiagnosticDescriptors isEnabledByDefault: true, description: new LocalizableResourceString(nameof(DoNotUseStateNameResource.Description), DoNotUseStateNameResource.ResourceManager, typeof(DoNotUseStateNameResource)) ); + + public static readonly DiagnosticDescriptor CameraMainIsSlow = new DiagnosticDescriptor( + id: DiagnosticIDs.CameraMainIsSlow, + title: new LocalizableResourceString(nameof(CameraMainResource.Title), CameraMainResource.ResourceManager, typeof(CameraMainResource)), + messageFormat: new LocalizableResourceString(nameof(CameraMainResource.MessageFormat), CameraMainResource.ResourceManager, typeof(CameraMainResource)), + category: DiagnosticCategories.Performance, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: new LocalizableResourceString(nameof(CameraMainResource.Description), CameraMainResource.ResourceManager, typeof(CameraMainResource)) + ); } } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs index 5d99f94..f0acced 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs @@ -13,6 +13,7 @@ public static class DiagnosticIDs public const string UnsealedDerivedClass = "UEA0008"; public const string InvokeFunctionMissing = "UEA0009"; public const string DoNotUseStateNameInAnimator = "UEA0010"; + public const string CameraMainIsSlow = "UEA0012"; //NOTES: These should probably be on their own analyzer - as they are not specific to Unity public const string DoNotUseRemoting = "AOT0001"; diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj b/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj index bd0fcda..af05a97 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj @@ -56,6 +56,12 @@ True TypeGetTypeResources.resx + + + True + True + CameraMainResource.resx + True @@ -134,6 +140,10 @@ ResXFileCodeGenerator TypeGetTypeResources.Designer.cs + + ResXFileCodeGenerator + CameraMainResource.Designer.cs + ResXFileCodeGenerator UseCompareTagResources.Designer.cs From 14074afd8c1d64321b42a2e2ad49c5028480488b Mon Sep 17 00:00:00 2001 From: Kasperki Date: Mon, 2 Oct 2017 21:07:54 +0300 Subject: [PATCH 20/46] Physics Analyzer, checks for methods that have non allocating versions. --- .../Physics/UseNonAllocMethodsTests.cs | 85 +++++++++++ .../UnityEngineAnalyzer.Test.csproj | 1 + .../DiagnosticDescriptors.cs | 11 ++ .../UnityEngineAnalyzer/DiagnosticIDs.cs | 1 + .../Physics/UseNonAllocMethodsAnalyzer.cs | 79 +++++++++++ .../UseNonAllocMethodsResources.Designer.cs | 91 ++++++++++++ .../Physics/UseNonAllocMethodsResources.resx | 132 ++++++++++++++++++ .../UnityEngineAnalyzer.csproj | 10 ++ 8 files changed, 410 insertions(+) create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Physics/UseNonAllocMethodsTests.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Physics/UseNonAllocMethodsAnalyzer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Physics/UseNonAllocMethodsResources.Designer.cs create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/Physics/UseNonAllocMethodsResources.resx diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Physics/UseNonAllocMethodsTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Physics/UseNonAllocMethodsTests.cs new file mode 100644 index 0000000..313d18e --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Physics/UseNonAllocMethodsTests.cs @@ -0,0 +1,85 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.Physics; + +namespace UnityEngineAnalyzer.Test.Animator +{ + + [TestFixture] + sealed class UseNonAllocMethodsTests : AnalyzerTestFixture + { + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new UseNonAllocMethodsAnalyzer(); + + [Test] + public void PhysicsRaycastAll() + { + const string code = @" +using UnityEngine; + +class C : MonoBehaviour +{ + void Update() + { + RaycastHit[] hits; + hits = [|Physics.RaycastAll(transform.position, transform.forward, 100.0F)|]; + } +}"; + + Document document; + TextSpan span; + TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); + + HasDiagnostic(document, span, DiagnosticIDs.PhysicsUseNonAllocMethods); + } + + [Test] + public void Physics2DCircleCastAll() + { + const string code = @" +using UnityEngine; + +class C : MonoBehaviour +{ + void Start() + { + [|Physics2D.CircleCastAll(Vector2.zero, 5, Vector2.one)|]; + } +}"; + + Document document; + TextSpan span; + TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); + + HasDiagnostic(document, span, DiagnosticIDs.PhysicsUseNonAllocMethods); + } + + [Test] + public void PhysicsUseNonAllocMethods_Should_Notice_Multiple_Calls() + { + const string code = @" +using UnityEngine; + +class C : MonoBehaviour +{ + Animator animator; + + void Start() + { + [|Physics2D.CircleCastAll(Vector2.zero, 5, Vector2.one)|]; + + [|Physics2D.LinecastAll(Vector2.zero, Vector2.one)|]; + } +}"; + + Document document; + TextSpan span; + TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); + + HasDiagnostic(document, span, DiagnosticIDs.PhysicsUseNonAllocMethods); + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj index cc86884..1867ef4 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj @@ -137,6 +137,7 @@ + diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs index 0ac196e..a293412 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs @@ -8,6 +8,7 @@ using UnityEngineAnalyzer.ForEachInUpdate; using UnityEngineAnalyzer.IL2CPP; using UnityEngineAnalyzer.OnGUI; +using UnityEngineAnalyzer.Physics; using UnityEngineAnalyzer.StringMethods; namespace UnityEngineAnalyzer @@ -147,5 +148,15 @@ static class DiagnosticDescriptors isEnabledByDefault: true, description: new LocalizableResourceString(nameof(DoNotUseStateNameResource.Description), DoNotUseStateNameResource.ResourceManager, typeof(DoNotUseStateNameResource)) ); + + public static readonly DiagnosticDescriptor UseNonAllocMethods = new DiagnosticDescriptor( + id: DiagnosticIDs.PhysicsUseNonAllocMethods, + title: new LocalizableResourceString(nameof(UseNonAllocMethodsResources.Title), UseNonAllocMethodsResources.ResourceManager, typeof(UseNonAllocMethodsResources)), + messageFormat: new LocalizableResourceString(nameof(UseNonAllocMethodsResources.MessageFormat), UseNonAllocMethodsResources.ResourceManager, typeof(UseNonAllocMethodsResources)), + category: DiagnosticCategories.GC, + defaultSeverity: DiagnosticSeverity.Info, + isEnabledByDefault: true, + description: new LocalizableResourceString(nameof(UseNonAllocMethodsResources.Description), UseNonAllocMethodsResources.ResourceManager, typeof(UseNonAllocMethodsResources)) + ); } } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs index 5d99f94..af90b22 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs @@ -13,6 +13,7 @@ public static class DiagnosticIDs public const string UnsealedDerivedClass = "UEA0008"; public const string InvokeFunctionMissing = "UEA0009"; public const string DoNotUseStateNameInAnimator = "UEA0010"; + public const string PhysicsUseNonAllocMethods = "UEA0013"; //NOTES: These should probably be on their own analyzer - as they are not specific to Unity public const string DoNotUseRemoting = "AOT0001"; diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Physics/UseNonAllocMethodsAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Physics/UseNonAllocMethodsAnalyzer.cs new file mode 100644 index 0000000..ffa437f --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Physics/UseNonAllocMethodsAnalyzer.cs @@ -0,0 +1,79 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using System.Collections.Immutable; + +namespace UnityEngineAnalyzer.Physics +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class UseNonAllocMethodsAnalyzer : DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.UseNonAllocMethods); + + private static readonly ImmutableHashSet PhysicsAllocatingCasts = ImmutableHashSet.Create( + "BoxCastAll", + "CapsuleCastAll", + "OverlapBox", + "OverlapCapsule", + "OverlapSphere", + "RaycastAll", + "SphereCastAll"); + + private static readonly ImmutableHashSet Physics2DAllocatingCasts = ImmutableHashSet.Create( + "BoxCastAll", + "CapsuleCastAll", + "CircleCastAll", + "GetRayIntersectionAll", + "LinecastAll", + "OverlapAreaAll", + "OverlapBoxAll", + "OverlapCapsuleAll", + "OverlapCircleAll", + "OverlapPointAll", + "RaycastAll"); + + 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(); + + string curClass = string.Empty; + // check if any of the methods are used + if (PhysicsAllocatingCasts.Contains(name)) + { + curClass = "Physics"; + } + else if (Physics2DAllocatingCasts.Contains(name)) + { + curClass = "Physics2D"; + } + else + { + return; + } + + var symbolInfo = context.SemanticModel.GetSymbolInfo(invocation); + var methodSymbol = symbolInfo.Symbol as IMethodSymbol; + + var containingType = methodSymbol.ContainingType; + + // check if the method is the one from UnityEngine.Animator + if (containingType.ContainingNamespace.Name.Equals("UnityEngine") && containingType.Name.Equals(curClass)) + { + var diagnostic = Diagnostic.Create(DiagnosticDescriptors.UseNonAllocMethods, invocation.GetLocation(), containingType.Name, methodSymbol.Name); + context.ReportDiagnostic(diagnostic); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Physics/UseNonAllocMethodsResources.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Physics/UseNonAllocMethodsResources.Designer.cs new file mode 100644 index 0000000..0f504f6 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Physics/UseNonAllocMethodsResources.Designer.cs @@ -0,0 +1,91 @@ +//------------------------------------------------------------------------------ +// +// 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.Physics { + using System; + using System.Reflection; + + + /// + /// 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. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class UseNonAllocMethodsResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal UseNonAllocMethodsResources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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.Physics.UseNonAllocMethodsResources", typeof(UseNonAllocMethodsResources).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to These calls have NonAlloc version since Unity 5.3. + /// + internal static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to '{0}' has non allocating version.. + /// + internal static string MessageFormat { + get { + return ResourceManager.GetString("MessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This call has non allocating version.. + /// + internal static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Physics/UseNonAllocMethodsResources.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/Physics/UseNonAllocMethodsResources.resx new file mode 100644 index 0000000..9480c13 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Physics/UseNonAllocMethodsResources.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 + + + These calls have NonAlloc version since Unity 5.3 + An optional longer localizable description of the diagnostic. + + + '{0}' has non allocating version. + The format-able message the diagnostic displays. + + + This call has non allocating version. + The title of the diagnostic. + + \ No newline at end of file diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj b/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj index bd0fcda..d6cd567 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj @@ -102,6 +102,12 @@ True DoNotUseOnGUIResources.resx + + + True + True + UseNonAllocMethodsResources.resx + @@ -162,6 +168,10 @@ ResXFileCodeGenerator DoNotUseOnGUIResources.Designer.cs + + ResXFileCodeGenerator + UseNonAllocMethodsResources.Designer.cs + ResXFileCodeGenerator DoNotUseStringMethodsResources.Designer.cs From 6f5f4625490b8f22f57d6eca4c5b7914726e3c4c Mon Sep 17 00:00:00 2001 From: Kasperki Date: Mon, 2 Oct 2017 21:12:04 +0300 Subject: [PATCH 21/46] better name for variable. --- .../Physics/UseNonAllocMethodsAnalyzer.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Physics/UseNonAllocMethodsAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Physics/UseNonAllocMethodsAnalyzer.cs index ffa437f..46160ab 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/Physics/UseNonAllocMethodsAnalyzer.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Physics/UseNonAllocMethodsAnalyzer.cs @@ -48,15 +48,15 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context) var name = invocation.MethodName(); - string curClass = string.Empty; + string containingClassName = string.Empty; // check if any of the methods are used if (PhysicsAllocatingCasts.Contains(name)) { - curClass = "Physics"; + containingClassName = "Physics"; } else if (Physics2DAllocatingCasts.Contains(name)) { - curClass = "Physics2D"; + containingClassName = "Physics2D"; } else { @@ -69,7 +69,7 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context) var containingType = methodSymbol.ContainingType; // check if the method is the one from UnityEngine.Animator - if (containingType.ContainingNamespace.Name.Equals("UnityEngine") && containingType.Name.Equals(curClass)) + if (containingType.ContainingNamespace.Name.Equals("UnityEngine") && containingType.Name.Equals(containingClassName)) { var diagnostic = Diagnostic.Create(DiagnosticDescriptors.UseNonAllocMethods, invocation.GetLocation(), containingType.Name, methodSymbol.Name); context.ReportDiagnostic(diagnostic); From 9d5b26f6ce03b04fcd53240e7eab39fed66d6eed Mon Sep 17 00:00:00 2001 From: Kasperki Date: Sat, 7 Oct 2017 11:44:18 +0300 Subject: [PATCH 22/46] Configuration can be read now from Json file. --- UnityEngineAnalyzer.CLI/Program.cs | 9 +++- UnityEngineAnalyzer.CLI/SolutionAnalyzer.cs | 48 ++++++++++++++++++--- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/UnityEngineAnalyzer.CLI/Program.cs b/UnityEngineAnalyzer.CLI/Program.cs index 0fe0c69..90f8b3d 100644 --- a/UnityEngineAnalyzer.CLI/Program.cs +++ b/UnityEngineAnalyzer.CLI/Program.cs @@ -58,8 +58,15 @@ public static void Main(string[] args) var tasks = new List(); if (fileInfo.Exists) { + FileInfo configFileInfo = null; + if (args.Length > 1) + { + var configFileName = args[1]; + configFileInfo = new FileInfo(configFileName); + } + var solutionAnalyzer = new SolutionAnalyzer(); - var analyzeTask = solutionAnalyzer.LoadAnadAnalyzeProject(fileInfo, report); + var analyzeTask = solutionAnalyzer.LoadAndAnalyzeProjectAsync(fileInfo, configFileInfo, report); tasks.Add(analyzeTask); } diff --git a/UnityEngineAnalyzer.CLI/SolutionAnalyzer.cs b/UnityEngineAnalyzer.CLI/SolutionAnalyzer.cs index 0e13ea8..2b05582 100644 --- a/UnityEngineAnalyzer.CLI/SolutionAnalyzer.cs +++ b/UnityEngineAnalyzer.CLI/SolutionAnalyzer.cs @@ -7,23 +7,57 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.MSBuild; using UnityEngineAnalyzer.ForEachInUpdate; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.Collections.Generic; namespace UnityEngineAnalyzer.CLI { public class SolutionAnalyzer { - public async Task LoadAnadAnalyzeProject(FileInfo projectFile, AnalyzerReport report) //TODO: Add async suffix + public async Task LoadAndAnalyzeProjectAsync(FileInfo projectFile, FileInfo configFileInfo, AnalyzerReport report) { var workspace = MSBuildWorkspace.Create(); var project = await workspace.OpenProjectAsync(projectFile.FullName, CancellationToken.None); - var analyzers = this.GetAnalyzers(); + var analyzerDictionary = LoadConfigFile(configFileInfo); + var analyzers = this.GetAnalyzers(analyzerDictionary); await AnalyzeProject(project, analyzers, report); } - private ImmutableArray GetAnalyzers() + private Dictionary LoadConfigFile(FileInfo configFile) + { + if (configFile == null || configFile.Exists == false) + { + return null; + } + + Dictionary analyzerDictionary = new Dictionary(); + + var config = File.ReadAllText(configFile.FullName); //TODO: read async? + var jsonObject = JsonConvert.DeserializeObject(config); + + foreach (var item in jsonObject) + { + analyzerDictionary.Add(item.Key, (bool)item.Value); + } + + return analyzerDictionary; + } + + private bool IsAnalyzerAllowedInConfiguration(Dictionary analyzerDictionary, string analyzerName) + { + if (analyzerDictionary == null || analyzerDictionary.ContainsKey(analyzerName) == false) + { + return true; + } + + return analyzerDictionary[analyzerName]; + } + + private ImmutableArray GetAnalyzers(Dictionary analyzerDictionary) { var listBuilder = ImmutableArray.CreateBuilder(); @@ -34,12 +68,14 @@ private ImmutableArray GetAnalyzers() { if (type.BaseType == typeof(DiagnosticAnalyzer)) { - var instance = Activator.CreateInstance(type) as DiagnosticAnalyzer; - listBuilder.Add(instance); + if (IsAnalyzerAllowedInConfiguration(analyzerDictionary, type.Name)) + { + var instance = Activator.CreateInstance(type) as DiagnosticAnalyzer; + listBuilder.Add(instance); + } } } - var analyzers = listBuilder.ToImmutable(); return analyzers; } From ea82dfd64ccef6ea3effe1437a88a9b4177f47e3 Mon Sep 17 00:00:00 2001 From: Kasperki Date: Sat, 7 Oct 2017 15:37:12 +0300 Subject: [PATCH 23/46] ConfigurationFileGenerator & fixed optional arguments to work better. --- .../ConfigurationFileGenerator.cs | 38 +++++++++++++++++++ UnityEngineAnalyzer.CLI/Program.cs | 17 +++++---- .../UnityEngineAnalyzer.CLI.csproj | 1 + 3 files changed, 49 insertions(+), 7 deletions(-) create mode 100644 UnityEngineAnalyzer.CLI/ConfigurationFileGenerator.cs diff --git a/UnityEngineAnalyzer.CLI/ConfigurationFileGenerator.cs b/UnityEngineAnalyzer.CLI/ConfigurationFileGenerator.cs new file mode 100644 index 0000000..3515501 --- /dev/null +++ b/UnityEngineAnalyzer.CLI/ConfigurationFileGenerator.cs @@ -0,0 +1,38 @@ +using Microsoft.CodeAnalysis.Diagnostics; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.IO; +using UnityEngineAnalyzer.ForEachInUpdate; + +namespace UnityEngineAnalyzer.CLI +{ + class ConfigurationFileGenerator + { + private const string configurationFileName = "configuration.json"; + + public void GenerateConfigurationFile() + { + var assembly = typeof(DoNotUseForEachInUpdate).Assembly; + var allTypes = assembly.DefinedTypes; + + var rootJson = new JObject(); + + foreach (var type in allTypes) + { + if (type.BaseType == typeof(DiagnosticAnalyzer)) + { + rootJson.Add(new JProperty(type.Name, true)); + } + } + + using (StreamWriter sw = File.CreateText("./" + configurationFileName)) + { + using (JsonTextWriter writer = new JsonTextWriter(sw)) + { + writer.Formatting = Formatting.Indented; + rootJson.WriteTo(writer); + } + } + } + } +} diff --git a/UnityEngineAnalyzer.CLI/Program.cs b/UnityEngineAnalyzer.CLI/Program.cs index 90f8b3d..957cec2 100644 --- a/UnityEngineAnalyzer.CLI/Program.cs +++ b/UnityEngineAnalyzer.CLI/Program.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; using System.IO; -using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CSharp.Syntax; using UnityEngineAnalyzer.CLI.Reporting; namespace UnityEngineAnalyzer.CLI @@ -24,6 +22,9 @@ public static void Main(string[] args) { try { + var configurationFileGenerator = new ConfigurationFileGenerator(); + configurationFileGenerator.GenerateConfigurationFile(); + //TODO: Use a proper parser for the commands if (args.Length <= 0) @@ -39,10 +40,13 @@ public static void Main(string[] args) //NOTE: This could be configurable via the CLI at some point var report = new AnalyzerReport(); - if (args.Length > 1 && AvailableExporters.ContainsKey(args[1])) + int optinalArgumentIndex = 1; + if (args.Length > optinalArgumentIndex && AvailableExporters.ContainsKey(args[optinalArgumentIndex])) { - var exporterInstance = Activator.CreateInstance(AvailableExporters[args[1]]); + var exporterInstance = Activator.CreateInstance(AvailableExporters[args[optinalArgumentIndex]]); report.AddExporter(exporterInstance as IAnalyzerExporter); + + optinalArgumentIndex++; } else { @@ -52,16 +56,15 @@ public static void Main(string[] args) } - report.InitializeReport(fileInfo); var tasks = new List(); if (fileInfo.Exists) { FileInfo configFileInfo = null; - if (args.Length > 1) + if (args.Length > optinalArgumentIndex) { - var configFileName = args[1]; + var configFileName = args[optinalArgumentIndex]; configFileInfo = new FileInfo(configFileName); } diff --git a/UnityEngineAnalyzer.CLI/UnityEngineAnalyzer.CLI.csproj b/UnityEngineAnalyzer.CLI/UnityEngineAnalyzer.CLI.csproj index 37697e1..30bf4a9 100644 --- a/UnityEngineAnalyzer.CLI/UnityEngineAnalyzer.CLI.csproj +++ b/UnityEngineAnalyzer.CLI/UnityEngineAnalyzer.CLI.csproj @@ -104,6 +104,7 @@ + From 6dbe8150f2f70f1332d1ccdb5cc5640b47fbc120 Mon Sep 17 00:00:00 2001 From: Kasperki Date: Sat, 7 Oct 2017 22:41:24 +0300 Subject: [PATCH 24/46] example analyzerConfiguration.json file. --- .../ConfigurationFileGenerator.cs | 3 ++- UnityEngineAnalyzer.CLI/Program.cs | 3 --- .../analyzerConfiguration.json | 16 ++++++++++++++++ 3 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 UnityEngineAnalyzer.CLI/analyzerConfiguration.json diff --git a/UnityEngineAnalyzer.CLI/ConfigurationFileGenerator.cs b/UnityEngineAnalyzer.CLI/ConfigurationFileGenerator.cs index 3515501..b8e0ee5 100644 --- a/UnityEngineAnalyzer.CLI/ConfigurationFileGenerator.cs +++ b/UnityEngineAnalyzer.CLI/ConfigurationFileGenerator.cs @@ -6,9 +6,10 @@ namespace UnityEngineAnalyzer.CLI { + //NOTE: This kind of configuration file should be created after build class ConfigurationFileGenerator { - private const string configurationFileName = "configuration.json"; + private const string configurationFileName = "analyzerConfiguration.json"; public void GenerateConfigurationFile() { diff --git a/UnityEngineAnalyzer.CLI/Program.cs b/UnityEngineAnalyzer.CLI/Program.cs index 957cec2..402967d 100644 --- a/UnityEngineAnalyzer.CLI/Program.cs +++ b/UnityEngineAnalyzer.CLI/Program.cs @@ -22,9 +22,6 @@ public static void Main(string[] args) { try { - var configurationFileGenerator = new ConfigurationFileGenerator(); - configurationFileGenerator.GenerateConfigurationFile(); - //TODO: Use a proper parser for the commands if (args.Length <= 0) diff --git a/UnityEngineAnalyzer.CLI/analyzerConfiguration.json b/UnityEngineAnalyzer.CLI/analyzerConfiguration.json new file mode 100644 index 0000000..4f9e6ae --- /dev/null +++ b/UnityEngineAnalyzer.CLI/analyzerConfiguration.json @@ -0,0 +1,16 @@ +{ + "DoNotUseStringMethodsAnalyzer": true, + "InvokeFunctionMissingAnalyzer": true, + "UseNonAllocMethodsAnalyzer": true, + "DoNotUseOnGUIAnalyzer": true, + "UnsealedDerivedClassAnalyzer": true, + "DoNotUseForEachInUpdate": true, + "DoNotUseFindMethodsInUpdateAnalyzer": true, + "EmptyMonoBehaviourMethodsAnalyzer": true, + "DoNotUseCoroutinesAnalyzer": true, + "UseCompareTagAnalyzer": true, + "DoNotUseReflectionEmitAnalyzer": true, + "DoNotUseRemotingAnalyzer": true, + "TypeGetTypeAnalyzer": true, + "DoNotUseStateNameAnalyzer": true +} \ No newline at end of file From 1b41c10e1375fecd9e047248c8abaf47b67c0396 Mon Sep 17 00:00:00 2001 From: Kasperki Date: Sat, 7 Oct 2017 22:56:50 +0300 Subject: [PATCH 25/46] Updated readme.md --- readme.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index a9abcba..3f16c1b 100644 --- a/readme.md +++ b/readme.md @@ -10,10 +10,11 @@ Comand Line Interface In order to use the Command Line Interface (CLI), download the latest release of UnityEngineAnalyzer then unzip the archive (https://github.com/vad710/UnityEngineAnalyzer/releases). 1. Open a Command Prompt or Powershell Window -1. Run `UnityEngineAnalyzer.CLI.exe ` +1. Run `UnityEngineAnalyzer.CLI.exe ` 1. Observe the analysis results 1. (Optional) In the same location as the project file are `report.json` and `UnityReport.html` files containig the results of the analysis - +1. (Optional) configuration file path. + * Enable / Disable analyzers you want to run. Example: From 136375727885684a1691862c066e966fb408e730 Mon Sep 17 00:00:00 2001 From: Donald Wu Date: Sun, 8 Oct 2017 13:20:25 +0800 Subject: [PATCH 26/46] 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 27/46] 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 28/46] 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 1f36f6033bbe804142ab3e24b186adef1530447b Mon Sep 17 00:00:00 2001 From: Kasper Kiiskinen Date: Wed, 11 Oct 2017 13:38:33 +0300 Subject: [PATCH 29/46] Fix Physics methods that have same name. --- .../Physics/UseNonAllocMethodsTests.cs | 22 +++++++++++++++++++ .../DiagnosticDescriptors.cs | 2 +- .../Physics/UseNonAllocMethodsAnalyzer.cs | 18 +++++++++------ 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Physics/UseNonAllocMethodsTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Physics/UseNonAllocMethodsTests.cs index 313d18e..c295f05 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Physics/UseNonAllocMethodsTests.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Physics/UseNonAllocMethodsTests.cs @@ -36,6 +36,28 @@ void Update() HasDiagnostic(document, span, DiagnosticIDs.PhysicsUseNonAllocMethods); } + [Test] + public void Physics2DRaycastAll() + { + const string code = @" +using UnityEngine; + +class C : MonoBehaviour +{ + void Update() + { + RaycastHit2D[] hits; + hits = [|Physics2D.RaycastAll(Vector2.zero, Vector2.one)|]; + } +}"; + + Document document; + TextSpan span; + TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); + + HasDiagnostic(document, span, DiagnosticIDs.PhysicsUseNonAllocMethods); + } + [Test] public void Physics2DCircleCastAll() { diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs index a293412..86512ab 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs @@ -154,7 +154,7 @@ static class DiagnosticDescriptors title: new LocalizableResourceString(nameof(UseNonAllocMethodsResources.Title), UseNonAllocMethodsResources.ResourceManager, typeof(UseNonAllocMethodsResources)), messageFormat: new LocalizableResourceString(nameof(UseNonAllocMethodsResources.MessageFormat), UseNonAllocMethodsResources.ResourceManager, typeof(UseNonAllocMethodsResources)), category: DiagnosticCategories.GC, - defaultSeverity: DiagnosticSeverity.Info, + defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true, description: new LocalizableResourceString(nameof(UseNonAllocMethodsResources.Description), UseNonAllocMethodsResources.ResourceManager, typeof(UseNonAllocMethodsResources)) ); diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Physics/UseNonAllocMethodsAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Physics/UseNonAllocMethodsAnalyzer.cs index 46160ab..0f7f473 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/Physics/UseNonAllocMethodsAnalyzer.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Physics/UseNonAllocMethodsAnalyzer.cs @@ -2,6 +2,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; +using System.Collections.Generic; using System.Collections.Immutable; namespace UnityEngineAnalyzer.Physics @@ -48,17 +49,20 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context) var name = invocation.MethodName(); - string containingClassName = string.Empty; + List containingClassName = new List(); + // check if any of the methods are used if (PhysicsAllocatingCasts.Contains(name)) { - containingClassName = "Physics"; + containingClassName.Add("Physics"); } - else if (Physics2DAllocatingCasts.Contains(name)) + + if (Physics2DAllocatingCasts.Contains(name)) { - containingClassName = "Physics2D"; + containingClassName.Add("Physics2D"); } - else + + if (containingClassName.Count == 0) { return; } @@ -68,8 +72,8 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context) var containingType = methodSymbol.ContainingType; - // check if the method is the one from UnityEngine.Animator - if (containingType.ContainingNamespace.Name.Equals("UnityEngine") && containingType.Name.Equals(containingClassName)) + // check if the method is the one from UnityEngine.Physics + if (containingType.ContainingNamespace.Name.Equals("UnityEngine") && containingClassName.Contains(containingType.Name)) { var diagnostic = Diagnostic.Create(DiagnosticDescriptors.UseNonAllocMethods, invocation.GetLocation(), containingType.Name, methodSymbol.Name); context.ReportDiagnostic(diagnostic); From ffb59fc3c6b73123fc39712513a8fd8bfe06b03b Mon Sep 17 00:00:00 2001 From: Kasper Kiiskinen Date: Wed, 11 Oct 2017 14:57:47 +0300 Subject: [PATCH 30/46] Fix stackoverflow error, thrown in recursion. --- .../Camera/CameraMainAnalyzer.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/CameraMainAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/CameraMainAnalyzer.cs index 6ca15c2..9a1042c 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/CameraMainAnalyzer.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Camera/CameraMainAnalyzer.cs @@ -62,15 +62,24 @@ private void SearchCameraMain(SyntaxNodeAnalysisContext context, MethodDeclarati } } + private object lastMethodRecursion = null; + private void RecursiveMethodCrawler(SyntaxNodeAnalysisContext context, MethodDeclarationSyntax method) { var invocationAccessExpression = method.DescendantNodes().OfType(); SearchCameraMain(context, method); - foreach (var invalid in invocationAccessExpression) + foreach (var invocation in invocationAccessExpression) { + if (lastMethodRecursion == invocationAccessExpression) + { + break; + } + + lastMethodRecursion = invocationAccessExpression; + SymbolInfo symbolInfo; - if (!context.TryGetSymbolInfo(invalid, out symbolInfo)) + if (!context.TryGetSymbolInfo(invocation, out symbolInfo)) { continue; } From 885dd8b8ac7225f0ba36d598cee482f5c9a3edb4 Mon Sep 17 00:00:00 2001 From: Kasper Kiiskinen Date: Thu, 12 Oct 2017 10:16:55 +0300 Subject: [PATCH 31/46] Updated DiagnosticDescriptors. --- .../ConfigurationFileGenerator.cs | 6 +- .../DiagnosticDescriptors.cs | 216 +++++------------- 2 files changed, 56 insertions(+), 166 deletions(-) diff --git a/UnityEngineAnalyzer.CLI/ConfigurationFileGenerator.cs b/UnityEngineAnalyzer.CLI/ConfigurationFileGenerator.cs index b8e0ee5..8f0906d 100644 --- a/UnityEngineAnalyzer.CLI/ConfigurationFileGenerator.cs +++ b/UnityEngineAnalyzer.CLI/ConfigurationFileGenerator.cs @@ -18,11 +18,11 @@ public void GenerateConfigurationFile() var rootJson = new JObject(); - foreach (var type in allTypes) + foreach (var typeInfo in allTypes) { - if (type.BaseType == typeof(DiagnosticAnalyzer)) + if (typeInfo.BaseType == typeof(DiagnosticAnalyzer)) { - rootJson.Add(new JProperty(type.Name, true)); + rootJson.Add(new JProperty(typeInfo.Name, true)); //TODO SupportedDiagnostics.IsEnabledByDefault; } } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs index b17296f..d4fa7b5 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs @@ -1,4 +1,5 @@ using Microsoft.CodeAnalysis; +using System.Resources; using UnityEngineAnalyzer.Animator; using UnityEngineAnalyzer.AOT; using UnityEngineAnalyzer.Camera; @@ -17,168 +18,57 @@ namespace UnityEngineAnalyzer { static class DiagnosticDescriptors { - //NOTES: Naming of Descriptors are a bit inconsistant - //NOTES: The Resource Reading code seems repetative - - public static readonly DiagnosticDescriptor DoNotUseOnGUI = new DiagnosticDescriptor( - id: DiagnosticIDs.DoNotUseOnGUI, - title: new LocalizableResourceString(nameof(DoNotUseOnGUIResources.Title), DoNotUseOnGUIResources.ResourceManager, typeof(DoNotUseOnGUIResources)), - messageFormat: new LocalizableResourceString(nameof(DoNotUseOnGUIResources.MessageFormat), DoNotUseOnGUIResources.ResourceManager, typeof(DoNotUseOnGUIResources)), - category: DiagnosticCategories.GC, - defaultSeverity: DiagnosticSeverity.Info, - isEnabledByDefault: true, - description: new LocalizableResourceString(nameof(DoNotUseOnGUIResources.Description), DoNotUseOnGUIResources.ResourceManager, typeof(DoNotUseOnGUIResources))); - - public static readonly DiagnosticDescriptor DoNotUseStringMethods = new DiagnosticDescriptor( - id: DiagnosticIDs.DoNotUseStringMethods, - title: new LocalizableResourceString(nameof(DoNotUseStringMethodsResources.Title), DoNotUseStringMethodsResources.ResourceManager, typeof(DoNotUseStringMethodsResources)), - messageFormat: new LocalizableResourceString(nameof(DoNotUseStringMethodsResources.MessageFormat), DoNotUseStringMethodsResources.ResourceManager, typeof(DoNotUseStringMethodsResources)), - category: DiagnosticCategories.StringMethods, - defaultSeverity: DiagnosticSeverity.Info, - isEnabledByDefault: true, - description: new LocalizableResourceString(nameof(DoNotUseStringMethodsResources.Description), DoNotUseStringMethodsResources.ResourceManager, typeof(DoNotUseStringMethodsResources))); - - public static readonly DiagnosticDescriptor DoNotUseCoroutines = new DiagnosticDescriptor( - id: DiagnosticIDs.DoNotUseCoroutines, - title: new LocalizableResourceString(nameof(DoNotUseCoroutinesResources.Title), DoNotUseCoroutinesResources.ResourceManager, typeof(DoNotUseCoroutinesResources)), - messageFormat: new LocalizableResourceString(nameof(DoNotUseCoroutinesResources.MessageFormat), DoNotUseCoroutinesResources.ResourceManager, typeof(DoNotUseCoroutinesResources)), - category: DiagnosticCategories.GC, - defaultSeverity: DiagnosticSeverity.Info, - isEnabledByDefault: true, - description: new LocalizableResourceString(nameof(DoNotUseCoroutinesResources.Description), DoNotUseCoroutinesResources.ResourceManager, typeof(DoNotUseCoroutinesResources))); - - - public static readonly DiagnosticDescriptor EmptyMonoBehaviourMethod = new DiagnosticDescriptor( - id: DiagnosticIDs.EmptyMonoBehaviourMethod, - title: new LocalizableResourceString(nameof(EmptyMonoBehaviourMethodsResources.Title), EmptyMonoBehaviourMethodsResources.ResourceManager, typeof(EmptyMonoBehaviourMethodsResources)), - messageFormat: new LocalizableResourceString(nameof(EmptyMonoBehaviourMethodsResources.MessageFormat), EmptyMonoBehaviourMethodsResources.ResourceManager, typeof(EmptyMonoBehaviourMethodsResources)), - category: DiagnosticCategories.Miscellaneous, - defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true, - description: new LocalizableResourceString(nameof(EmptyMonoBehaviourMethodsResources.Description), EmptyMonoBehaviourMethodsResources.ResourceManager, typeof(EmptyMonoBehaviourMethodsResources))); - - public static readonly DiagnosticDescriptor UseCompareTag = new DiagnosticDescriptor( - id: DiagnosticIDs.UseCompareTag, - title: new LocalizableResourceString(nameof(UseCompareTagResources.Title), UseCompareTagResources.ResourceManager, typeof(UseCompareTagResources)), - messageFormat: new LocalizableResourceString(nameof(UseCompareTagResources.MessageFormat), UseCompareTagResources.ResourceManager, typeof(UseCompareTagResources)), - category: DiagnosticCategories.GC, - defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true, - description: new LocalizableResourceString(nameof(UseCompareTagResources.Description), UseCompareTagResources.ResourceManager, typeof(UseCompareTagResources))); - - public static readonly DiagnosticDescriptor DoNotUseFindMethodsInUpdate = new DiagnosticDescriptor( - id: DiagnosticIDs.DoNotUseFindMethodsInUpdate, - title: new LocalizableResourceString(nameof(DoNotUseFindMethodsInUpdateResources.Title), DoNotUseFindMethodsInUpdateResources.ResourceManager, typeof(DoNotUseFindMethodsInUpdateResources)), - messageFormat: new LocalizableResourceString(nameof(DoNotUseFindMethodsInUpdateResources.MessageFormat), DoNotUseFindMethodsInUpdateResources.ResourceManager, typeof(DoNotUseFindMethodsInUpdateResources)), - category: DiagnosticCategories.Performance, - defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true, - description: new LocalizableResourceString(nameof(DoNotUseFindMethodsInUpdateResources.Description), DoNotUseFindMethodsInUpdateResources.ResourceManager, typeof(DoNotUseFindMethodsInUpdateResources))); - - public static readonly DiagnosticDescriptor DoNotUseFindMethodsInUpdateRecursive = new DiagnosticDescriptor( - id: DiagnosticIDs.DoNotUseFindMethodsInUpdate, - title: new LocalizableResourceString(nameof(DoNotUseFindMethodsInUpdateResources.Title), DoNotUseFindMethodsInUpdateResources.ResourceManager, typeof(DoNotUseFindMethodsInUpdateResources)), - messageFormat: new LocalizableResourceString(nameof(DoNotUseFindMethodsInUpdateResources.MessageFormatRecursive), DoNotUseFindMethodsInUpdateResources.ResourceManager, typeof(DoNotUseFindMethodsInUpdateResources)), - category: DiagnosticCategories.Performance, - defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true, - description: new LocalizableResourceString(nameof(DoNotUseFindMethodsInUpdateResources.Description), DoNotUseFindMethodsInUpdateResources.ResourceManager, typeof(DoNotUseFindMethodsInUpdateResources))); - - public static readonly DiagnosticDescriptor DoNotUseRemoting = new DiagnosticDescriptor( - id: DiagnosticIDs.DoNotUseRemoting, - title: new LocalizableResourceString(nameof(DoNotUseRemotingResources.Title), DoNotUseRemotingResources.ResourceManager, typeof(DoNotUseRemotingResources)), - messageFormat: new LocalizableResourceString(nameof(DoNotUseRemotingResources.MessageFormat), DoNotUseRemotingResources.ResourceManager, typeof(DoNotUseRemotingResources)), - category: DiagnosticCategories.AOT, - defaultSeverity: DiagnosticSeverity.Info, - isEnabledByDefault: true, - description: new LocalizableResourceString(nameof(DoNotUseRemotingResources.Description), DoNotUseRemotingResources.ResourceManager, typeof(DoNotUseRemotingResources))); - - public static readonly DiagnosticDescriptor DoNotUseReflectionEmit = new DiagnosticDescriptor( - id: DiagnosticIDs.DoNotUseReflectionEmit, - title: new LocalizableResourceString(nameof(DoNotUseReflectionEmitResources.Title), DoNotUseReflectionEmitResources.ResourceManager, typeof(DoNotUseReflectionEmitResources)), - messageFormat: new LocalizableResourceString(nameof(DoNotUseReflectionEmitResources.MessageFormat), DoNotUseReflectionEmitResources.ResourceManager, typeof(DoNotUseReflectionEmitResources)), - category: DiagnosticCategories.AOT, - defaultSeverity: DiagnosticSeverity.Info, - isEnabledByDefault: true, - description: new LocalizableResourceString(nameof(DoNotUseReflectionEmitResources.Description), DoNotUseReflectionEmitResources.ResourceManager, typeof(DoNotUseReflectionEmitResources))); - - public static readonly DiagnosticDescriptor TypeGetType = new DiagnosticDescriptor( - id: DiagnosticIDs.TypeGetType, - title: new LocalizableResourceString(nameof(TypeGetTypeResources.Title), TypeGetTypeResources.ResourceManager, typeof(TypeGetTypeResources)), - messageFormat: new LocalizableResourceString(nameof(TypeGetTypeResources.MessageFormat), TypeGetTypeResources.ResourceManager, typeof(TypeGetTypeResources)), - category: DiagnosticCategories.AOT, - defaultSeverity: DiagnosticSeverity.Info, - isEnabledByDefault: true, - description: new LocalizableResourceString(nameof(TypeGetTypeResources.Description), TypeGetTypeResources.ResourceManager, typeof(TypeGetTypeResources))); - - public static readonly DiagnosticDescriptor DoNotUseForEachInUpdate = new DiagnosticDescriptor( - id: DiagnosticIDs.DoNotUseForEachInUpdate, - title: new LocalizableResourceString(nameof(DoNotUseForEachInUpdateResources.Title), DoNotUseForEachInUpdateResources.ResourceManager, typeof(DoNotUseForEachInUpdateResources)), - messageFormat: new LocalizableResourceString(nameof(DoNotUseForEachInUpdateResources.MessageFormat), DoNotUseForEachInUpdateResources.ResourceManager, typeof(DoNotUseForEachInUpdateResources)), - category: DiagnosticCategories.Performance, - defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true, - description: new LocalizableResourceString(nameof(DoNotUseForEachInUpdateResources.Description), DoNotUseForEachInUpdateResources.ResourceManager, typeof(DoNotUseForEachInUpdateResources)) - ); - - public static readonly DiagnosticDescriptor UnsealedDerivedClass = new DiagnosticDescriptor( - id: DiagnosticIDs.UnsealedDerivedClass, - title: new LocalizableResourceString(nameof(UnsealedDerivedClassResources.Title), UnsealedDerivedClassResources.ResourceManager, typeof(UnsealedDerivedClassResources)), - messageFormat: new LocalizableResourceString(nameof(UnsealedDerivedClassResources.MessageFormat), UnsealedDerivedClassResources.ResourceManager, typeof(UnsealedDerivedClassResources)), - category: DiagnosticCategories.Performance, - defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true, - description: new LocalizableResourceString(nameof(UnsealedDerivedClassResources.Description), UnsealedDerivedClassResources.ResourceManager, typeof(UnsealedDerivedClassResources)) - ); - - public static readonly DiagnosticDescriptor InvokeFunctionMissing = new DiagnosticDescriptor( - id: DiagnosticIDs.InvokeFunctionMissing, - title: new LocalizableResourceString(nameof(InvokeFunctionMissingResources.Title), InvokeFunctionMissingResources.ResourceManager, typeof(InvokeFunctionMissingResources)), - messageFormat: new LocalizableResourceString(nameof(InvokeFunctionMissingResources.MessageFormat), InvokeFunctionMissingResources.ResourceManager, typeof(InvokeFunctionMissingResources)), - category: DiagnosticCategories.Performance, - defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true, - description: new LocalizableResourceString(nameof(InvokeFunctionMissingResources.Description), InvokeFunctionMissingResources.ResourceManager, typeof(InvokeFunctionMissingResources)) - ); - - public static readonly DiagnosticDescriptor DoNotUseStateName = new DiagnosticDescriptor( - id: DiagnosticIDs.DoNotUseStateNameInAnimator, - title: new LocalizableResourceString(nameof(DoNotUseStateNameResource.Title), DoNotUseStateNameResource.ResourceManager, typeof(DoNotUseStateNameResource)), - messageFormat: new LocalizableResourceString(nameof(DoNotUseStateNameResource.MessageFormat), DoNotUseStateNameResource.ResourceManager, typeof(DoNotUseStateNameResource)), - category: DiagnosticCategories.Performance, - defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true, - description: new LocalizableResourceString(nameof(DoNotUseStateNameResource.Description), DoNotUseStateNameResource.ResourceManager, typeof(DoNotUseStateNameResource)) - ); - - public static readonly DiagnosticDescriptor CameraMainIsSlow = new DiagnosticDescriptor( - id: DiagnosticIDs.CameraMainIsSlow, - title: new LocalizableResourceString(nameof(CameraMainResource.Title), CameraMainResource.ResourceManager, typeof(CameraMainResource)), - messageFormat: new LocalizableResourceString(nameof(CameraMainResource.MessageFormat), CameraMainResource.ResourceManager, typeof(CameraMainResource)), - category: DiagnosticCategories.Performance, - defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true, - description: new LocalizableResourceString(nameof(CameraMainResource.Description), CameraMainResource.ResourceManager, typeof(CameraMainResource)) - ); - - public static readonly DiagnosticDescriptor DoNotUseStringPropertyNames = new DiagnosticDescriptor( - id: DiagnosticIDs.DoNotUseStringPropertyNamesInMaterial, - title: new LocalizableResourceString(nameof(DoNotUseStringPropertyNamesResource.Title), DoNotUseStringPropertyNamesResource.ResourceManager, typeof(DoNotUseStringPropertyNamesResource)), - messageFormat: new LocalizableResourceString(nameof(DoNotUseStringPropertyNamesResource.MessageFormat), DoNotUseStringPropertyNamesResource.ResourceManager, typeof(DoNotUseStringPropertyNamesResource)), - category: DiagnosticCategories.Performance, - defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true, - description: new LocalizableResourceString(nameof(DoNotUseStringPropertyNamesResource.Description), DoNotUseStringPropertyNamesResource.ResourceManager, typeof(DoNotUseStringPropertyNamesResource)) - ); - - public static readonly DiagnosticDescriptor UseNonAllocMethods = new DiagnosticDescriptor( - id: DiagnosticIDs.PhysicsUseNonAllocMethods, - title: new LocalizableResourceString(nameof(UseNonAllocMethodsResources.Title), UseNonAllocMethodsResources.ResourceManager, typeof(UseNonAllocMethodsResources)), - messageFormat: new LocalizableResourceString(nameof(UseNonAllocMethodsResources.MessageFormat), UseNonAllocMethodsResources.ResourceManager, typeof(UseNonAllocMethodsResources)), - category: DiagnosticCategories.GC, - defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true, - description: new LocalizableResourceString(nameof(UseNonAllocMethodsResources.Description), UseNonAllocMethodsResources.ResourceManager, typeof(UseNonAllocMethodsResources)) - ); + public static readonly DiagnosticDescriptor DoNotUseOnGUI; + public static readonly DiagnosticDescriptor DoNotUseStringMethods; + public static readonly DiagnosticDescriptor DoNotUseCoroutines; + public static readonly DiagnosticDescriptor EmptyMonoBehaviourMethod; + public static readonly DiagnosticDescriptor UseCompareTag; + public static readonly DiagnosticDescriptor DoNotUseFindMethodsInUpdate; + public static readonly DiagnosticDescriptor DoNotUseFindMethodsInUpdateRecursive; + public static readonly DiagnosticDescriptor DoNotUseRemoting; + public static readonly DiagnosticDescriptor DoNotUseReflectionEmit; + public static readonly DiagnosticDescriptor TypeGetType; + public static readonly DiagnosticDescriptor DoNotUseForEachInUpdate; + public static readonly DiagnosticDescriptor UnsealedDerivedClass; + public static readonly DiagnosticDescriptor InvokeFunctionMissing; + public static readonly DiagnosticDescriptor DoNotUseStateName; + public static readonly DiagnosticDescriptor DoNotUseStringPropertyNames; + public static readonly DiagnosticDescriptor UseNonAllocMethods; + public static readonly DiagnosticDescriptor CameraMainIsSlow; + + static DiagnosticDescriptors() + { + DoNotUseOnGUI = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseOnGUI, DiagnosticCategories.GC, DiagnosticSeverity.Info); + DoNotUseStringMethods = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseStringMethods, DiagnosticCategories.GC, DiagnosticSeverity.Info); + DoNotUseCoroutines = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseCoroutines, DiagnosticCategories.GC, DiagnosticSeverity.Info); + EmptyMonoBehaviourMethod = CreateDiagnosticDescriptor(DiagnosticIDs.EmptyMonoBehaviourMethod, DiagnosticCategories.Miscellaneous, DiagnosticSeverity.Warning); + UseCompareTag = CreateDiagnosticDescriptor(DiagnosticIDs.UseCompareTag, DiagnosticCategories.GC, DiagnosticSeverity.Warning); + DoNotUseFindMethodsInUpdate = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseFindMethodsInUpdate, DiagnosticCategories.Performance, DiagnosticSeverity.Warning); + DoNotUseFindMethodsInUpdateRecursive = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseFindMethodsInUpdate, DiagnosticCategories.Performance, DiagnosticSeverity.Warning); + DoNotUseRemoting = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseRemoting, DiagnosticCategories.AOT, DiagnosticSeverity.Info); + DoNotUseReflectionEmit = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseReflectionEmit, DiagnosticCategories.AOT, DiagnosticSeverity.Info); + TypeGetType = CreateDiagnosticDescriptor(DiagnosticIDs.TypeGetType, DiagnosticCategories.AOT, DiagnosticSeverity.Info); + DoNotUseForEachInUpdate = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseForEachInUpdate, DiagnosticCategories.Performance, DiagnosticSeverity.Warning); + 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); + UseNonAllocMethods = CreateDiagnosticDescriptor(DiagnosticIDs.PhysicsUseNonAllocMethods, DiagnosticCategories.GC, DiagnosticSeverity.Warning); + CameraMainIsSlow = CreateDiagnosticDescriptor(DiagnosticIDs.CameraMainIsSlow, DiagnosticCategories.GC, DiagnosticSeverity.Warning); + } + + private static DiagnosticDescriptor CreateDiagnosticDescriptor(string id, string category, DiagnosticSeverity severity, 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, + description: new LocalizableResourceString("Description", resourceManager, typeof(T))); + } } } From 4b8f846aed6f04ad25c3eb60c7df9d288bac03b3 Mon Sep 17 00:00:00 2001 From: Kasper Kiiskinen Date: Mon, 16 Oct 2017 10:27:08 +0300 Subject: [PATCH 32/46] Added command line parser. --- UnityEngineAnalyzer.CLI/AnalyzerReport.cs | 5 +++ UnityEngineAnalyzer.CLI/Options.cs | 17 +++++++++ UnityEngineAnalyzer.CLI/Program.cs | 35 ++++++++++--------- .../Reporting/DiagnosticInfo.cs | 2 -- .../UnityEngineAnalyzer.CLI.csproj | 4 +++ UnityEngineAnalyzer.CLI/packages.config | 1 + readme.md | 8 +++-- 7 files changed, 51 insertions(+), 21 deletions(-) create mode 100644 UnityEngineAnalyzer.CLI/Options.cs diff --git a/UnityEngineAnalyzer.CLI/AnalyzerReport.cs b/UnityEngineAnalyzer.CLI/AnalyzerReport.cs index 4444837..815472c 100644 --- a/UnityEngineAnalyzer.CLI/AnalyzerReport.cs +++ b/UnityEngineAnalyzer.CLI/AnalyzerReport.cs @@ -17,6 +17,11 @@ public void AddExporter(IAnalyzerExporter exporter) _exporters.Add(exporter); } + public int GetExporterCount() + { + return _exporters.Count; + } + public void AppendDiagnostics(IEnumerable diagnosticResults) { if (_exporters.Count == 0) diff --git a/UnityEngineAnalyzer.CLI/Options.cs b/UnityEngineAnalyzer.CLI/Options.cs new file mode 100644 index 0000000..f885f47 --- /dev/null +++ b/UnityEngineAnalyzer.CLI/Options.cs @@ -0,0 +1,17 @@ +using CommandLine; +using System.Collections.Generic; + +namespace UnityEngineAnalyzer.CLI +{ + internal class Options + { + [ValueOption(0)] + public string ProjectFile { get; set; } + + [Option('e', "exporter", HelpText = "Exporters to be used.")] + public IEnumerable Exporters { get; set; } + + [Option('c', "configuration", HelpText = "Custom json configuration to be used.")] + public string ConfigurationFile { get; set; } + } +} \ No newline at end of file diff --git a/UnityEngineAnalyzer.CLI/Program.cs b/UnityEngineAnalyzer.CLI/Program.cs index 402967d..070268d 100644 --- a/UnityEngineAnalyzer.CLI/Program.cs +++ b/UnityEngineAnalyzer.CLI/Program.cs @@ -17,52 +17,55 @@ static Program() AvailableExporters.Add(nameof(ConsoleAnalyzerExporter), typeof(ConsoleAnalyzerExporter)); } - public static void Main(string[] args) { try { - //TODO: Use a proper parser for the commands + var options = new Options(); + var isValid = CommandLine.Parser.Default.ParseArgumentsStrict(args, options); - if (args.Length <= 0) + if (isValid == false || options.ProjectFile == null) { return; } var startTime = DateTime.Now; - var fileName = args[0]; + var fileName = options.ProjectFile; var fileInfo = new FileInfo(fileName); //NOTE: This could be configurable via the CLI at some point var report = new AnalyzerReport(); - int optinalArgumentIndex = 1; - if (args.Length > optinalArgumentIndex && AvailableExporters.ContainsKey(args[optinalArgumentIndex])) + if (options.Exporters != null) { - var exporterInstance = Activator.CreateInstance(AvailableExporters[args[optinalArgumentIndex]]); - report.AddExporter(exporterInstance as IAnalyzerExporter); - - optinalArgumentIndex++; + foreach (var exporter in options.Exporters) + { + if (AvailableExporters.ContainsKey(exporter)) + { + var exporterInstance = Activator.CreateInstance(AvailableExporters[exporter]); + report.AddExporter(exporterInstance as IAnalyzerExporter); + } + } } - else - { + + if (report.GetExporterCount() == 0) + { //It's generally a good idea to make sure that the Console Exporter is last since it is interactive report.AddExporter(new JsonAnalyzerExporter()); report.AddExporter(new ConsoleAnalyzerExporter()); } - report.InitializeReport(fileInfo); var tasks = new List(); if (fileInfo.Exists) { FileInfo configFileInfo = null; - if (args.Length > optinalArgumentIndex) + + if (options.ConfigurationFile != null) { - var configFileName = args[optinalArgumentIndex]; - configFileInfo = new FileInfo(configFileName); + configFileInfo = new FileInfo(options.ConfigurationFile); } var solutionAnalyzer = new SolutionAnalyzer(); diff --git a/UnityEngineAnalyzer.CLI/Reporting/DiagnosticInfo.cs b/UnityEngineAnalyzer.CLI/Reporting/DiagnosticInfo.cs index c2fd93e..88cedd0 100644 --- a/UnityEngineAnalyzer.CLI/Reporting/DiagnosticInfo.cs +++ b/UnityEngineAnalyzer.CLI/Reporting/DiagnosticInfo.cs @@ -1,7 +1,5 @@ namespace UnityEngineAnalyzer.CLI.Reporting { - - public class DiagnosticInfo { //TODO: Rename this to something like AnalysisResult diff --git a/UnityEngineAnalyzer.CLI/UnityEngineAnalyzer.CLI.csproj b/UnityEngineAnalyzer.CLI/UnityEngineAnalyzer.CLI.csproj index 30bf4a9..c9fe854 100644 --- a/UnityEngineAnalyzer.CLI/UnityEngineAnalyzer.CLI.csproj +++ b/UnityEngineAnalyzer.CLI/UnityEngineAnalyzer.CLI.csproj @@ -33,6 +33,9 @@ 4 + + ..\packages\CommandLineParser.1.9.71\lib\net45\CommandLine.dll + ..\packages\Microsoft.CodeAnalysis.Common.1.3.2\lib\net45\Microsoft.CodeAnalysis.dll True @@ -105,6 +108,7 @@ + diff --git a/UnityEngineAnalyzer.CLI/packages.config b/UnityEngineAnalyzer.CLI/packages.config index 5c06964..ef2d310 100644 --- a/UnityEngineAnalyzer.CLI/packages.config +++ b/UnityEngineAnalyzer.CLI/packages.config @@ -1,5 +1,6 @@  + diff --git a/readme.md b/readme.md index 3f16c1b..95c0177 100644 --- a/readme.md +++ b/readme.md @@ -10,11 +10,13 @@ Comand Line Interface In order to use the Command Line Interface (CLI), download the latest release of UnityEngineAnalyzer then unzip the archive (https://github.com/vad710/UnityEngineAnalyzer/releases). 1. Open a Command Prompt or Powershell Window -1. Run `UnityEngineAnalyzer.CLI.exe ` +1. Run `UnityEngineAnalyzer.CLI.exe ` 1. Observe the analysis results -1. (Optional) In the same location as the project file are `report.json` and `UnityReport.html` files containig the results of the analysis +1. (Optional) In the same location as the project file are `report.json` and `UnityReport.html` files containig the results of the analysis + * Use command `-e customexporter exporter2 ...` to load custom exporters 1. (Optional) configuration file path. - * Enable / Disable analyzers you want to run. + * Use command `-c configureFilePath.json` to load custom configurations + * Configuration json, allows to enable / disable analyzers Example: From 032779636ced7b5376bc0069ba5f1bf1a8d020c4 Mon Sep 17 00:00:00 2001 From: Kasper Kiiskinen Date: Mon, 16 Oct 2017 10:54:32 +0300 Subject: [PATCH 33/46] MinimalSeverity can be now given as CLI argument. --- UnityEngineAnalyzer.CLI/Options.cs | 4 +++ UnityEngineAnalyzer.CLI/Program.cs | 4 +-- .../Reporting/AnalyzerExporter.cs | 21 ++++++++++++++ .../Reporting/ConsoleAnalyzerExporter.cs | 3 ++ .../Reporting/JsonAnalyzerExporter.cs | 16 +++++------ .../StandardOutputAnalyzerReporter.cs | 28 ++++++++++--------- .../UnityEngineAnalyzer.CLI.csproj | 1 + readme.md | 3 ++ 8 files changed, 57 insertions(+), 23 deletions(-) create mode 100644 UnityEngineAnalyzer.CLI/Reporting/AnalyzerExporter.cs diff --git a/UnityEngineAnalyzer.CLI/Options.cs b/UnityEngineAnalyzer.CLI/Options.cs index f885f47..e45d863 100644 --- a/UnityEngineAnalyzer.CLI/Options.cs +++ b/UnityEngineAnalyzer.CLI/Options.cs @@ -1,5 +1,6 @@ using CommandLine; using System.Collections.Generic; +using UnityEngineAnalyzer.CLI.Reporting; namespace UnityEngineAnalyzer.CLI { @@ -13,5 +14,8 @@ internal class Options [Option('c', "configuration", HelpText = "Custom json configuration to be used.")] public string ConfigurationFile { get; set; } + + [Option('s', "severity", DefaultValue = DiagnosticInfo.DiagnosticInfoSeverity.Warning, HelpText = "Minimal severity to be reported.")] + public DiagnosticInfo.DiagnosticInfoSeverity MinimalSeverity { get; set; } } } \ No newline at end of file diff --git a/UnityEngineAnalyzer.CLI/Program.cs b/UnityEngineAnalyzer.CLI/Program.cs index 070268d..800fcf8 100644 --- a/UnityEngineAnalyzer.CLI/Program.cs +++ b/UnityEngineAnalyzer.CLI/Program.cs @@ -52,8 +52,8 @@ public static void Main(string[] args) if (report.GetExporterCount() == 0) { //It's generally a good idea to make sure that the Console Exporter is last since it is interactive - report.AddExporter(new JsonAnalyzerExporter()); - report.AddExporter(new ConsoleAnalyzerExporter()); + report.AddExporter(new JsonAnalyzerExporter(options.MinimalSeverity)); + report.AddExporter(new ConsoleAnalyzerExporter(options.MinimalSeverity)); } report.InitializeReport(fileInfo); diff --git a/UnityEngineAnalyzer.CLI/Reporting/AnalyzerExporter.cs b/UnityEngineAnalyzer.CLI/Reporting/AnalyzerExporter.cs new file mode 100644 index 0000000..9be9ea2 --- /dev/null +++ b/UnityEngineAnalyzer.CLI/Reporting/AnalyzerExporter.cs @@ -0,0 +1,21 @@ +using System; +using System.IO; + +namespace UnityEngineAnalyzer.CLI.Reporting +{ + public abstract class AnalyzerExporter : IAnalyzerExporter + { + //NOTE: Can we use using.static.DiagnosticsInfo; C#6 feature? + protected DiagnosticInfo.DiagnosticInfoSeverity MinimalSeverity; + + public AnalyzerExporter(DiagnosticInfo.DiagnosticInfoSeverity MinimalSeverity) + { + this.MinimalSeverity = MinimalSeverity; + } + + public abstract void AppendDiagnostic(DiagnosticInfo diagnosticInfo); + public abstract void FinalizeExporter(TimeSpan duration); + public abstract void InitializeExporter(FileInfo projectFile); + public abstract void NotifyException(Exception exception); + } +} diff --git a/UnityEngineAnalyzer.CLI/Reporting/ConsoleAnalyzerExporter.cs b/UnityEngineAnalyzer.CLI/Reporting/ConsoleAnalyzerExporter.cs index 5a7127f..7705934 100644 --- a/UnityEngineAnalyzer.CLI/Reporting/ConsoleAnalyzerExporter.cs +++ b/UnityEngineAnalyzer.CLI/Reporting/ConsoleAnalyzerExporter.cs @@ -5,6 +5,9 @@ namespace UnityEngineAnalyzer.CLI.Reporting { public class ConsoleAnalyzerExporter : StandardOutputAnalyzerReporter { + public ConsoleAnalyzerExporter(DiagnosticInfo.DiagnosticInfoSeverity MinimalSeverity) : base(MinimalSeverity) + { + } public override void FinalizeExporter(TimeSpan duration) { diff --git a/UnityEngineAnalyzer.CLI/Reporting/JsonAnalyzerExporter.cs b/UnityEngineAnalyzer.CLI/Reporting/JsonAnalyzerExporter.cs index 4f3c8c3..0e2e4b9 100644 --- a/UnityEngineAnalyzer.CLI/Reporting/JsonAnalyzerExporter.cs +++ b/UnityEngineAnalyzer.CLI/Reporting/JsonAnalyzerExporter.cs @@ -1,25 +1,25 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using Newtonsoft.Json; namespace UnityEngineAnalyzer.CLI.Reporting { - public class JsonAnalyzerExporter : IAnalyzerExporter + public class JsonAnalyzerExporter : AnalyzerExporter { private const string JsonReportFileName = "report.json"; private const string HtmlReportFileName = "UnityReport.html"; - private const DiagnosticInfo.DiagnosticInfoSeverity MinimalSeverity = DiagnosticInfo.DiagnosticInfoSeverity.Warning; - private JsonTextWriter _jsonWriter; private readonly JsonSerializer _jsonSerializer = new JsonSerializer(); private readonly List _exceptions = new List(); private string _destinationReportFile; + public JsonAnalyzerExporter(DiagnosticInfo.DiagnosticInfoSeverity MinimalSeverity) : base(MinimalSeverity) + { + } - public void AppendDiagnostic(DiagnosticInfo diagnosticInfo) + public override void AppendDiagnostic(DiagnosticInfo diagnosticInfo) { if (diagnosticInfo.Severity >= MinimalSeverity) { @@ -27,7 +27,7 @@ public void AppendDiagnostic(DiagnosticInfo diagnosticInfo) } } - public void FinalizeExporter(TimeSpan duration) + public override void FinalizeExporter(TimeSpan duration) { _jsonWriter.WriteEndArray(); @@ -50,7 +50,7 @@ public void FinalizeExporter(TimeSpan duration) //Process.Start(_destinationReportFile); } - public void InitializeExporter(FileInfo projectFile) + public override void InitializeExporter(FileInfo projectFile) { if (!projectFile.Exists) { @@ -81,7 +81,7 @@ public void InitializeExporter(FileInfo projectFile) _jsonSerializer.Formatting = Formatting.Indented; } - public void NotifyException(Exception exception) + public override void NotifyException(Exception exception) { _exceptions.Add(exception); } diff --git a/UnityEngineAnalyzer.CLI/Reporting/StandardOutputAnalyzerReporter.cs b/UnityEngineAnalyzer.CLI/Reporting/StandardOutputAnalyzerReporter.cs index 3be7a79..be177d1 100644 --- a/UnityEngineAnalyzer.CLI/Reporting/StandardOutputAnalyzerReporter.cs +++ b/UnityEngineAnalyzer.CLI/Reporting/StandardOutputAnalyzerReporter.cs @@ -3,14 +3,16 @@ namespace UnityEngineAnalyzer.CLI.Reporting { - public class StandardOutputAnalyzerReporter : IAnalyzerExporter + public class StandardOutputAnalyzerReporter : AnalyzerExporter { protected const string ConsoleSeparator = "\t"; - protected const DiagnosticInfo.DiagnosticInfoSeverity MinimalSeverity = DiagnosticInfo.DiagnosticInfoSeverity.Warning; - protected const string FailurePrefix = "# "; - public void AppendDiagnostic(DiagnosticInfo diagnosticInfo) + public StandardOutputAnalyzerReporter(DiagnosticInfo.DiagnosticInfoSeverity MinimalSeverity) : base(MinimalSeverity) + { + } + + public override void AppendDiagnostic(DiagnosticInfo diagnosticInfo) { if (diagnosticInfo.Severity < MinimalSeverity) { @@ -48,15 +50,7 @@ private ConsoleColor ConsoleColorFromSeverity(DiagnosticInfo.DiagnosticInfoSever } } - public virtual void FinalizeExporter(TimeSpan duration) - { - } - - public virtual void InitializeExporter(FileInfo projectFile) - { - } - - public virtual void NotifyException(Exception exception) + public override void NotifyException(Exception exception) { Console.ForegroundColor = ConsoleColor.Red; @@ -72,5 +66,13 @@ public virtual void NotifyException(Exception exception) Console.ResetColor(); } + + public override void FinalizeExporter(TimeSpan duration) + { + } + + public override void InitializeExporter(FileInfo projectFile) + { + } } } diff --git a/UnityEngineAnalyzer.CLI/UnityEngineAnalyzer.CLI.csproj b/UnityEngineAnalyzer.CLI/UnityEngineAnalyzer.CLI.csproj index c9fe854..48e3142 100644 --- a/UnityEngineAnalyzer.CLI/UnityEngineAnalyzer.CLI.csproj +++ b/UnityEngineAnalyzer.CLI/UnityEngineAnalyzer.CLI.csproj @@ -109,6 +109,7 @@ + diff --git a/readme.md b/readme.md index 95c0177..48609ee 100644 --- a/readme.md +++ b/readme.md @@ -17,6 +17,9 @@ In order to use the Command Line Interface (CLI), download the latest release of 1. (Optional) configuration file path. * Use command `-c configureFilePath.json` to load custom configurations * Configuration json, allows to enable / disable analyzers +1. (Optional) minimal severity for reports + * Use command `-s Info/Warning/Error` to defined used minimal severity for reporting + * Default is Warning Example: From c8cf67b163b2b183022ed3cd5e53fcdbb55876a7 Mon Sep 17 00:00:00 2001 From: Donald Wu Date: Tue, 17 Oct 2017 10:44:04 +0800 Subject: [PATCH 34/46] 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 2bb14dc92b295f102f806a35443b57651075a6ae Mon Sep 17 00:00:00 2001 From: Kasperki Date: Tue, 17 Oct 2017 16:40:43 +0300 Subject: [PATCH 35/46] fix typo --- .../Material/DoNotUseStringPropertyNamesAnalyzer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Material/DoNotUseStringPropertyNamesAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Material/DoNotUseStringPropertyNamesAnalyzer.cs index b18cad7..9e87a36 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/Material/DoNotUseStringPropertyNamesAnalyzer.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Material/DoNotUseStringPropertyNamesAnalyzer.cs @@ -61,7 +61,7 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context) var containingClass = methodSymbol.ContainingType; - // check if the method is the one from UnityEngine.Animator + // check if the method is the one from UnityEngine.Material if (containingClass.ContainingNamespace.Name.Equals("UnityEngine") && containingClass.Name.Equals("Material")) { if (methodSymbol.Parameters[0].Type.MetadataName == "String") From 14fe6b3ca11b1d4972b14e963a67a04f9e290343 Mon Sep 17 00:00:00 2001 From: Kasperki Date: Tue, 17 Oct 2017 20:31:13 +0300 Subject: [PATCH 36/46] start of version check --- .../Reporting/AnalyzerExporter.cs | 28 +++++++++++++--- .../StandardOutputAnalyzerReporter.cs | 8 ++--- .../DiagnosticDescriptors.cs | 22 +++++++++++-- .../UnityEngineAnalyzer.csproj | 1 + .../UnityEngineAnalyzer/UnityVersions.cs | 32 +++++++++++++++++++ 5 files changed, 81 insertions(+), 10 deletions(-) create mode 100644 UnityEngineAnalyzer/UnityEngineAnalyzer/UnityVersions.cs diff --git a/UnityEngineAnalyzer.CLI/Reporting/AnalyzerExporter.cs b/UnityEngineAnalyzer.CLI/Reporting/AnalyzerExporter.cs index 9be9ea2..e715ab9 100644 --- a/UnityEngineAnalyzer.CLI/Reporting/AnalyzerExporter.cs +++ b/UnityEngineAnalyzer.CLI/Reporting/AnalyzerExporter.cs @@ -1,16 +1,36 @@ -using System; +using Microsoft.CodeAnalysis; +using System; using System.IO; +using static UnityEngineAnalyzer.CLI.Reporting.DiagnosticInfo; namespace UnityEngineAnalyzer.CLI.Reporting { public abstract class AnalyzerExporter : IAnalyzerExporter { - //NOTE: Can we use using.static.DiagnosticsInfo; C#6 feature? - protected DiagnosticInfo.DiagnosticInfoSeverity MinimalSeverity; + protected DiagnosticInfoSeverity MinimalSeverity; + private readonly UnityVersion unityVersion; - public AnalyzerExporter(DiagnosticInfo.DiagnosticInfoSeverity MinimalSeverity) + public AnalyzerExporter(DiagnosticInfoSeverity MinimalSeverity, UnityVersion unityVersion) { this.MinimalSeverity = MinimalSeverity; + this.unityVersion = unityVersion; + } + + public bool AbleToAnalyzer(DiagnosticInfoSeverity currentSeverity, DiagnosticDescriptor diagnosticInfo, UnityVersion unityVersion = UnityVersion.ALL) + { + if (MinimalSeverity < currentSeverity) + { + return false; + } + + var off = DiagnosticDescriptors.GetVersion(diagnosticInfo); + + if (unityVersion < off.Item1 || unityVersion > off.Item2) + { + return false; + } + + return true; } public abstract void AppendDiagnostic(DiagnosticInfo diagnosticInfo); diff --git a/UnityEngineAnalyzer.CLI/Reporting/StandardOutputAnalyzerReporter.cs b/UnityEngineAnalyzer.CLI/Reporting/StandardOutputAnalyzerReporter.cs index be177d1..738ca68 100644 --- a/UnityEngineAnalyzer.CLI/Reporting/StandardOutputAnalyzerReporter.cs +++ b/UnityEngineAnalyzer.CLI/Reporting/StandardOutputAnalyzerReporter.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using static UnityEngineAnalyzer.CLI.Reporting.DiagnosticInfo; namespace UnityEngineAnalyzer.CLI.Reporting { @@ -8,13 +9,13 @@ public class StandardOutputAnalyzerReporter : AnalyzerExporter protected const string ConsoleSeparator = "\t"; protected const string FailurePrefix = "# "; - public StandardOutputAnalyzerReporter(DiagnosticInfo.DiagnosticInfoSeverity MinimalSeverity) : base(MinimalSeverity) + public StandardOutputAnalyzerReporter(DiagnosticInfoSeverity MinimalSeverity, UnityVersion unityVersion) : base(MinimalSeverity, unityVersion) { } public override void AppendDiagnostic(DiagnosticInfo diagnosticInfo) { - if (diagnosticInfo.Severity < MinimalSeverity) + if (AbleToAnalyzer(MinimalSeverity, diagnosticInfo) == false) { return; } @@ -30,10 +31,9 @@ public override void AppendDiagnostic(DiagnosticInfo diagnosticInfo) Console.Write(diagnosticInfo.Message); Console.ResetColor(); Console.WriteLine(@"{0}{1}{0}{2},{3}", ConsoleSeparator,diagnosticInfo.FileName, diagnosticInfo.LineNumber, diagnosticInfo.CharacterPosition); - } - private ConsoleColor ConsoleColorFromSeverity(DiagnosticInfo.DiagnosticInfoSeverity severity) + private ConsoleColor ConsoleColorFromSeverity(DiagnosticInfoSeverity severity) { switch (severity) { diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs index d4fa7b5..a5a34db 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs @@ -1,4 +1,6 @@ using Microsoft.CodeAnalysis; +using System; +using System.Linq; using System.Resources; using UnityEngineAnalyzer.Animator; using UnityEngineAnalyzer.AOT; @@ -16,7 +18,7 @@ namespace UnityEngineAnalyzer { - static class DiagnosticDescriptors + public static class DiagnosticDescriptors { public static readonly DiagnosticDescriptor DoNotUseOnGUI; public static readonly DiagnosticDescriptor DoNotUseStringMethods; @@ -57,7 +59,7 @@ static DiagnosticDescriptors() CameraMainIsSlow = CreateDiagnosticDescriptor(DiagnosticIDs.CameraMainIsSlow, DiagnosticCategories.GC, DiagnosticSeverity.Warning); } - private static DiagnosticDescriptor CreateDiagnosticDescriptor(string id, string category, DiagnosticSeverity severity, bool isEnabledByDefault = true) + private static DiagnosticDescriptor CreateDiagnosticDescriptor(string id, string category, DiagnosticSeverity severity, UnityVersion latest = UnityVersion.ALL, bool isEnabledByDefault = true) { var resourceManager = new ResourceManager(typeof(T)); @@ -68,7 +70,23 @@ private static DiagnosticDescriptor CreateDiagnosticDescriptor(string id, str category: category, defaultSeverity: severity, isEnabledByDefault: isEnabledByDefault, + customTags: CreateUnityVersionInfo(latest, 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) }; + } + + //TODO USE: (UnityVersion start, UnityVersion end) Tuple when updated to Net Standard 2.0 + public static Tuple GetVersion(DiagnosticDescriptor dc) + { + var list = dc.CustomTags.ToList(); + var start = (UnityVersion)Enum.Parse(typeof(UnityVersion), list[0]); + var end = (UnityVersion)Enum.Parse(typeof(UnityVersion), list[1]); + + return Tuple.Create(start, end); + } } } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj b/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj index 79b219b..4295573 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj @@ -134,6 +134,7 @@ True InvokeFunctionMissingResources.resx + diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityVersions.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityVersions.cs new file mode 100644 index 0000000..9c9e9d1 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityVersions.cs @@ -0,0 +1,32 @@ +using System; + +namespace UnityEngineAnalyzer +{ + public enum UnityVersion + { + ALL, + UNITY_1_0, + UNITY_2_0, + UNITY_3_0, + UNITY_3_5, + UNITY_4_0, + UNITY_4_1, + UNITY_4_2, + UNITY_4_3, + UNITY_4_4, + UNITY_4_5, + UNITY_4_6, + UNITY_4_7, + UNITY_5_0, + UNITY_5_1, + UNITY_5_2, + UNITY_5_3, + UNITY_5_4, + UNITY_5_5, + UNITY_5_6, + UNITY_2017_0, + UNITY_2017_1, + UNITY_2017_2, + UNITY_2017_3, + } +} \ No newline at end of file From 86b4e1f59bac93259ecc7ad0d3b34cc71da5c591 Mon Sep 17 00:00:00 2001 From: Kasper Kiiskinen Date: Wed, 18 Oct 2017 11:58:20 +0300 Subject: [PATCH 37/46] Unity Version check working. --- UnityEngineAnalyzer.CLI/AnalyzerReport.cs | 9 ++-- UnityEngineAnalyzer.CLI/Options.cs | 11 ++-- UnityEngineAnalyzer.CLI/Program.cs | 51 +++++++++++++++++-- .../Reporting/AnalyzerExporter.cs | 23 +++------ .../Reporting/ConsoleAnalyzerExporter.cs | 8 +-- .../Reporting/DiagnosticInfo.cs | 1 + .../Reporting/IAnalyzerExporter.cs | 2 +- .../Reporting/JsonAnalyzerExporter.cs | 7 +-- .../StandardOutputAnalyzerReporter.cs | 15 +++--- .../DiagnosticDescriptors.cs | 17 ++++--- .../UnityEngineAnalyzer/DiagnosticIDs.cs | 2 - .../UnityEngineAnalyzer/UnityVersions.cs | 19 +++++-- 12 files changed, 110 insertions(+), 55 deletions(-) diff --git a/UnityEngineAnalyzer.CLI/AnalyzerReport.cs b/UnityEngineAnalyzer.CLI/AnalyzerReport.cs index 815472c..cbebc27 100644 --- a/UnityEngineAnalyzer.CLI/AnalyzerReport.cs +++ b/UnityEngineAnalyzer.CLI/AnalyzerReport.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.IO; using Microsoft.CodeAnalysis; using UnityEngineAnalyzer.CLI.Reporting; @@ -52,10 +51,10 @@ public void AppendDiagnostics(IEnumerable diagnosticResults) FileName = fileName, LineNumber = lineNumber, CharacterPosition = characterPosition, - Severity = (DiagnosticInfo.DiagnosticInfoSeverity)diagnostic.Severity + Severity = (DiagnosticInfo.DiagnosticInfoSeverity)diagnostic.Severity, + VersionSpan = DiagnosticDescriptors.GetVersion(diagnostic.Descriptor) }; - foreach (var exporter in _exporters) { exporter.AppendDiagnostic(diagnosticInfo); @@ -71,11 +70,11 @@ public void FinalizeReport(TimeSpan duration) } } - public void InitializeReport(FileInfo projectFile) + public void InitializeReport(Options options) { foreach (var exporter in _exporters) { - exporter.InitializeExporter(projectFile); + exporter.InitializeExporter(options); } } diff --git a/UnityEngineAnalyzer.CLI/Options.cs b/UnityEngineAnalyzer.CLI/Options.cs index e45d863..5892559 100644 --- a/UnityEngineAnalyzer.CLI/Options.cs +++ b/UnityEngineAnalyzer.CLI/Options.cs @@ -1,10 +1,10 @@ using CommandLine; using System.Collections.Generic; -using UnityEngineAnalyzer.CLI.Reporting; +using static UnityEngineAnalyzer.CLI.Reporting.DiagnosticInfo; namespace UnityEngineAnalyzer.CLI { - internal class Options + public class Options { [ValueOption(0)] public string ProjectFile { get; set; } @@ -15,7 +15,10 @@ internal class Options [Option('c', "configuration", HelpText = "Custom json configuration to be used.")] public string ConfigurationFile { get; set; } - [Option('s', "severity", DefaultValue = DiagnosticInfo.DiagnosticInfoSeverity.Warning, HelpText = "Minimal severity to be reported.")] - public DiagnosticInfo.DiagnosticInfoSeverity MinimalSeverity { get; set; } + [Option('s', "severity", DefaultValue = DiagnosticInfoSeverity.Warning, HelpText = "Minimal severity to be reported.")] + public DiagnosticInfoSeverity MinimalSeverity { get; set; } + + [Option('v', "version", DefaultValue = UnityVersion.NONE, HelpText = "Check against spesific Unity version.")] + public UnityVersion Version { get; set; } } } \ No newline at end of file diff --git a/UnityEngineAnalyzer.CLI/Program.cs b/UnityEngineAnalyzer.CLI/Program.cs index 800fcf8..2e836b3 100644 --- a/UnityEngineAnalyzer.CLI/Program.cs +++ b/UnityEngineAnalyzer.CLI/Program.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; +using System.Text.RegularExpressions; using System.Threading.Tasks; using UnityEngineAnalyzer.CLI.Reporting; @@ -9,6 +11,7 @@ namespace UnityEngineAnalyzer.CLI public class Program { private static readonly Dictionary AvailableExporters = new Dictionary(); + private const UnityVersion DEFAULT_UNITY_VERSION = UnityVersion.LATEST; static Program() { @@ -29,6 +32,8 @@ public static void Main(string[] args) return; } + options.Version = DefineUnityVersion(options); + var startTime = DateTime.Now; var fileName = options.ProjectFile; @@ -52,11 +57,11 @@ public static void Main(string[] args) if (report.GetExporterCount() == 0) { //It's generally a good idea to make sure that the Console Exporter is last since it is interactive - report.AddExporter(new JsonAnalyzerExporter(options.MinimalSeverity)); - report.AddExporter(new ConsoleAnalyzerExporter(options.MinimalSeverity)); + report.AddExporter(new JsonAnalyzerExporter(options)); + report.AddExporter(new ConsoleAnalyzerExporter(options)); } - report.InitializeReport(fileInfo); + report.InitializeReport(options); var tasks = new List(); if (fileInfo.Exists) @@ -89,6 +94,46 @@ public static void Main(string[] args) } } + //TODO SET TO OWN CLASS + private static UnityVersion DefineUnityVersion(Options options) + { + if (options.Version != UnityVersion.NONE) + { + return options.Version; + } + + //THIS ONLY WORKS ON UNITY >= 5, before that ProjectVersion.txt did not exists + var projectPath = new FileInfo(options.ProjectFile).Directory; + var projectVersionFile = new FileInfo(projectPath + "/ProjectSettings/ProjectVersion.txt"); + if (projectVersionFile.Exists) + { + var projectVersionString = File.ReadAllText(projectVersionFile.FullName); + return TryParseUnityVersion(projectVersionString); + } + + return DEFAULT_UNITY_VERSION; + } + + //TODO UNIT TESTS + private static UnityVersion TryParseUnityVersion(string version) + { + string editorText = "m_EditorVersion: "; + var match = Regex.Match(version, editorText + "[0-9.a-z]*"); + + string src = match.Value.Substring(editorText.Length); + src = src.Replace('.', '_'); + src = src.Substring(0, src.IndexOf('_') + 2); + var unityVersions = Enum.GetValues(typeof(UnityVersion)).Cast(); + foreach (var unityVersion in unityVersions) + { + if (Enum.GetName(typeof(UnityVersion), unityVersion).Contains(src)) + { + return unityVersion; + } + } + + return DEFAULT_UNITY_VERSION; + } } } diff --git a/UnityEngineAnalyzer.CLI/Reporting/AnalyzerExporter.cs b/UnityEngineAnalyzer.CLI/Reporting/AnalyzerExporter.cs index e715ab9..9056dfc 100644 --- a/UnityEngineAnalyzer.CLI/Reporting/AnalyzerExporter.cs +++ b/UnityEngineAnalyzer.CLI/Reporting/AnalyzerExporter.cs @@ -1,31 +1,24 @@ -using Microsoft.CodeAnalysis; -using System; -using System.IO; -using static UnityEngineAnalyzer.CLI.Reporting.DiagnosticInfo; +using System; namespace UnityEngineAnalyzer.CLI.Reporting { public abstract class AnalyzerExporter : IAnalyzerExporter { - protected DiagnosticInfoSeverity MinimalSeverity; - private readonly UnityVersion unityVersion; + private readonly Options options; - public AnalyzerExporter(DiagnosticInfoSeverity MinimalSeverity, UnityVersion unityVersion) + public AnalyzerExporter(Options options) { - this.MinimalSeverity = MinimalSeverity; - this.unityVersion = unityVersion; + this.options = options; } - public bool AbleToAnalyzer(DiagnosticInfoSeverity currentSeverity, DiagnosticDescriptor diagnosticInfo, UnityVersion unityVersion = UnityVersion.ALL) + public bool IsAnalyzerRelevant(DiagnosticInfo diagnosticInfo) { - if (MinimalSeverity < currentSeverity) + if (options.MinimalSeverity > diagnosticInfo.Severity) { return false; } - var off = DiagnosticDescriptors.GetVersion(diagnosticInfo); - - if (unityVersion < off.Item1 || unityVersion > off.Item2) + if (options.Version < diagnosticInfo.VersionSpan.First || options.Version > diagnosticInfo.VersionSpan.Last) { return false; } @@ -35,7 +28,7 @@ public bool AbleToAnalyzer(DiagnosticInfoSeverity currentSeverity, DiagnosticDes public abstract void AppendDiagnostic(DiagnosticInfo diagnosticInfo); public abstract void FinalizeExporter(TimeSpan duration); - public abstract void InitializeExporter(FileInfo projectFile); + public abstract void InitializeExporter(Options options); public abstract void NotifyException(Exception exception); } } diff --git a/UnityEngineAnalyzer.CLI/Reporting/ConsoleAnalyzerExporter.cs b/UnityEngineAnalyzer.CLI/Reporting/ConsoleAnalyzerExporter.cs index 7705934..7af1c76 100644 --- a/UnityEngineAnalyzer.CLI/Reporting/ConsoleAnalyzerExporter.cs +++ b/UnityEngineAnalyzer.CLI/Reporting/ConsoleAnalyzerExporter.cs @@ -1,11 +1,10 @@ using System; -using System.IO; namespace UnityEngineAnalyzer.CLI.Reporting { public class ConsoleAnalyzerExporter : StandardOutputAnalyzerReporter { - public ConsoleAnalyzerExporter(DiagnosticInfo.DiagnosticInfoSeverity MinimalSeverity) : base(MinimalSeverity) + public ConsoleAnalyzerExporter(Options options) : base(options) { } @@ -19,12 +18,13 @@ public override void FinalizeExporter(TimeSpan duration) Console.ReadKey(); } - public override void InitializeExporter(FileInfo projectFile) + public override void InitializeExporter(Options options) { Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("Unity Syntax Analyzer"); Console.WriteLine(); - Console.WriteLine("Analyzing: {0}", projectFile.FullName); + Console.WriteLine("Analyzing: {0}", options.ProjectFile); + Console.WriteLine("With Unity version: " + Enum.GetName((typeof(UnityVersion)), options.Version)); Console.WriteLine(); Console.ResetColor(); } diff --git a/UnityEngineAnalyzer.CLI/Reporting/DiagnosticInfo.cs b/UnityEngineAnalyzer.CLI/Reporting/DiagnosticInfo.cs index 88cedd0..c13d113 100644 --- a/UnityEngineAnalyzer.CLI/Reporting/DiagnosticInfo.cs +++ b/UnityEngineAnalyzer.CLI/Reporting/DiagnosticInfo.cs @@ -10,6 +10,7 @@ public class DiagnosticInfo public int LineNumber { get; set; } public int CharacterPosition { get; set; } public DiagnosticInfoSeverity Severity { get; set; } + public UnityVersionSpan VersionSpan { get; set; } public enum DiagnosticInfoSeverity { diff --git a/UnityEngineAnalyzer.CLI/Reporting/IAnalyzerExporter.cs b/UnityEngineAnalyzer.CLI/Reporting/IAnalyzerExporter.cs index 3aee835..cdb9bc4 100644 --- a/UnityEngineAnalyzer.CLI/Reporting/IAnalyzerExporter.cs +++ b/UnityEngineAnalyzer.CLI/Reporting/IAnalyzerExporter.cs @@ -7,7 +7,7 @@ public interface IAnalyzerExporter { void AppendDiagnostic(DiagnosticInfo diagnosticInfo); void FinalizeExporter(TimeSpan duration); - void InitializeExporter(FileInfo projectFile); + void InitializeExporter(Options options); void NotifyException(Exception exception); } } \ No newline at end of file diff --git a/UnityEngineAnalyzer.CLI/Reporting/JsonAnalyzerExporter.cs b/UnityEngineAnalyzer.CLI/Reporting/JsonAnalyzerExporter.cs index 0e2e4b9..520ff15 100644 --- a/UnityEngineAnalyzer.CLI/Reporting/JsonAnalyzerExporter.cs +++ b/UnityEngineAnalyzer.CLI/Reporting/JsonAnalyzerExporter.cs @@ -15,13 +15,13 @@ public class JsonAnalyzerExporter : AnalyzerExporter private readonly List _exceptions = new List(); private string _destinationReportFile; - public JsonAnalyzerExporter(DiagnosticInfo.DiagnosticInfoSeverity MinimalSeverity) : base(MinimalSeverity) + public JsonAnalyzerExporter(Options options) : base(options) { } public override void AppendDiagnostic(DiagnosticInfo diagnosticInfo) { - if (diagnosticInfo.Severity >= MinimalSeverity) + if (IsAnalyzerRelevant(diagnosticInfo)) { _jsonSerializer.Serialize(_jsonWriter, diagnosticInfo); } @@ -50,8 +50,9 @@ public override void FinalizeExporter(TimeSpan duration) //Process.Start(_destinationReportFile); } - public override void InitializeExporter(FileInfo projectFile) + public override void InitializeExporter(Options options) { + var projectFile = new FileInfo(options.ProjectFile); if (!projectFile.Exists) { throw new ArgumentException("Project file does not exist"); diff --git a/UnityEngineAnalyzer.CLI/Reporting/StandardOutputAnalyzerReporter.cs b/UnityEngineAnalyzer.CLI/Reporting/StandardOutputAnalyzerReporter.cs index 738ca68..ee1dfb5 100644 --- a/UnityEngineAnalyzer.CLI/Reporting/StandardOutputAnalyzerReporter.cs +++ b/UnityEngineAnalyzer.CLI/Reporting/StandardOutputAnalyzerReporter.cs @@ -1,5 +1,4 @@ using System; -using System.IO; using static UnityEngineAnalyzer.CLI.Reporting.DiagnosticInfo; namespace UnityEngineAnalyzer.CLI.Reporting @@ -9,13 +8,13 @@ public class StandardOutputAnalyzerReporter : AnalyzerExporter protected const string ConsoleSeparator = "\t"; protected const string FailurePrefix = "# "; - public StandardOutputAnalyzerReporter(DiagnosticInfoSeverity MinimalSeverity, UnityVersion unityVersion) : base(MinimalSeverity, unityVersion) + public StandardOutputAnalyzerReporter(Options options) : base(options) { } public override void AppendDiagnostic(DiagnosticInfo diagnosticInfo) { - if (AbleToAnalyzer(MinimalSeverity, diagnosticInfo) == false) + if (IsAnalyzerRelevant(diagnosticInfo) == false) { return; } @@ -37,13 +36,13 @@ private ConsoleColor ConsoleColorFromSeverity(DiagnosticInfoSeverity severity) { switch (severity) { - case DiagnosticInfo.DiagnosticInfoSeverity.Hidden: + case DiagnosticInfoSeverity.Hidden: return ConsoleColor.Gray; - case DiagnosticInfo.DiagnosticInfoSeverity.Info: + case DiagnosticInfoSeverity.Info: return ConsoleColor.Green; - case DiagnosticInfo.DiagnosticInfoSeverity.Warning: + case DiagnosticInfoSeverity.Warning: return ConsoleColor.Yellow; - case DiagnosticInfo.DiagnosticInfoSeverity.Error: + case DiagnosticInfoSeverity.Error: return ConsoleColor.Red; default: return ConsoleColor.White; @@ -71,7 +70,7 @@ public override void FinalizeExporter(TimeSpan duration) { } - public override void InitializeExporter(FileInfo projectFile) + public override void InitializeExporter(Options options) { } } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs index a5a34db..34b4358 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs @@ -55,11 +55,11 @@ static DiagnosticDescriptors() InvokeFunctionMissing = CreateDiagnosticDescriptor(DiagnosticIDs.InvokeFunctionMissing, DiagnosticCategories.Performance, DiagnosticSeverity.Warning); DoNotUseStateName = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseStateNameInAnimator, DiagnosticCategories.Performance, DiagnosticSeverity.Warning); DoNotUseStringPropertyNames = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseStringPropertyNamesInMaterial, DiagnosticCategories.Performance, DiagnosticSeverity.Warning); - UseNonAllocMethods = CreateDiagnosticDescriptor(DiagnosticIDs.PhysicsUseNonAllocMethods, DiagnosticCategories.GC, DiagnosticSeverity.Warning); + UseNonAllocMethods = CreateDiagnosticDescriptor(DiagnosticIDs.PhysicsUseNonAllocMethods, DiagnosticCategories.GC, DiagnosticSeverity.Warning, UnityVersion.UNITY_5_3); CameraMainIsSlow = CreateDiagnosticDescriptor(DiagnosticIDs.CameraMainIsSlow, DiagnosticCategories.GC, DiagnosticSeverity.Warning); } - private static DiagnosticDescriptor CreateDiagnosticDescriptor(string id, string category, DiagnosticSeverity severity, UnityVersion latest = UnityVersion.ALL, bool isEnabledByDefault = true) + private static DiagnosticDescriptor CreateDiagnosticDescriptor(string id, string category, DiagnosticSeverity severity, UnityVersion first = UnityVersion.UNITY_1_0, UnityVersion latest = UnityVersion.LATEST, bool isEnabledByDefault = true) { var resourceManager = new ResourceManager(typeof(T)); @@ -70,7 +70,7 @@ private static DiagnosticDescriptor CreateDiagnosticDescriptor(string id, str category: category, defaultSeverity: severity, isEnabledByDefault: isEnabledByDefault, - customTags: CreateUnityVersionInfo(latest, latest), + customTags: CreateUnityVersionInfo(first, latest), description: new LocalizableResourceString("Description", resourceManager, typeof(T))); } @@ -79,14 +79,19 @@ private static string[] CreateUnityVersionInfo(UnityVersion start, UnityVersion return new string[] { Enum.GetName(typeof(UnityVersion), start), Enum.GetName(typeof(UnityVersion), end) }; } - //TODO USE: (UnityVersion start, UnityVersion end) Tuple when updated to Net Standard 2.0 - public static Tuple GetVersion(DiagnosticDescriptor dc) + public static UnityVersionSpan GetVersion(DiagnosticDescriptor dc) { var list = dc.CustomTags.ToList(); + + if (list.Count < 2) + { + return new UnityVersionSpan(UnityVersion.NONE, UnityVersion.LATEST); + } + var start = (UnityVersion)Enum.Parse(typeof(UnityVersion), list[0]); var end = (UnityVersion)Enum.Parse(typeof(UnityVersion), list[1]); - return Tuple.Create(start, end); + return new UnityVersionSpan(start, end); } } } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs index 3b07f22..8bb4f66 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs @@ -21,7 +21,5 @@ public static class DiagnosticIDs public const string DoNotUseRemoting = "AOT0001"; public const string DoNotUseReflectionEmit = "AOT0002"; public const string TypeGetType = "AOT0003"; - - } } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityVersions.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityVersions.cs index 9c9e9d1..b547fa4 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityVersions.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityVersions.cs @@ -1,10 +1,20 @@ -using System; - -namespace UnityEngineAnalyzer +namespace UnityEngineAnalyzer { + public class UnityVersionSpan + { + public UnityVersion First { get; set; } + public UnityVersion Last { get; set; } + + public UnityVersionSpan (UnityVersion first, UnityVersion last) + { + First = first; + Last = last; + } + } + public enum UnityVersion { - ALL, + NONE, UNITY_1_0, UNITY_2_0, UNITY_3_0, @@ -28,5 +38,6 @@ public enum UnityVersion UNITY_2017_1, UNITY_2017_2, UNITY_2017_3, + LATEST, } } \ No newline at end of file From d406e7713da8b353bae5e76ace51acb3936e0db7 Mon Sep 17 00:00:00 2001 From: Kasper Kiiskinen Date: Thu, 19 Oct 2017 16:44:58 +0300 Subject: [PATCH 38/46] Refactoring UnityVersionResolver.cs --- UnityEngineAnalyzer.CLI/Program.cs | 45 +------------ .../UnityEngineAnalyzer.CLI.csproj | 1 + .../UnityVersionResolver.cs | 65 +++++++++++++++++++ 3 files changed, 68 insertions(+), 43 deletions(-) create mode 100644 UnityEngineAnalyzer.CLI/UnityVersionResolver.cs diff --git a/UnityEngineAnalyzer.CLI/Program.cs b/UnityEngineAnalyzer.CLI/Program.cs index 2e836b3..9cee5a2 100644 --- a/UnityEngineAnalyzer.CLI/Program.cs +++ b/UnityEngineAnalyzer.CLI/Program.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text.RegularExpressions; using System.Threading.Tasks; using UnityEngineAnalyzer.CLI.Reporting; @@ -11,7 +9,6 @@ namespace UnityEngineAnalyzer.CLI public class Program { private static readonly Dictionary AvailableExporters = new Dictionary(); - private const UnityVersion DEFAULT_UNITY_VERSION = UnityVersion.LATEST; static Program() { @@ -32,7 +29,8 @@ public static void Main(string[] args) return; } - options.Version = DefineUnityVersion(options); + var unityVersionResolver = new UnityVersionResolver(); + options.Version = unityVersionResolver.ResolveVersion(options); var startTime = DateTime.Now; @@ -95,45 +93,6 @@ public static void Main(string[] args) } //TODO SET TO OWN CLASS - private static UnityVersion DefineUnityVersion(Options options) - { - if (options.Version != UnityVersion.NONE) - { - return options.Version; - } - //THIS ONLY WORKS ON UNITY >= 5, before that ProjectVersion.txt did not exists - var projectPath = new FileInfo(options.ProjectFile).Directory; - var projectVersionFile = new FileInfo(projectPath + "/ProjectSettings/ProjectVersion.txt"); - if (projectVersionFile.Exists) - { - var projectVersionString = File.ReadAllText(projectVersionFile.FullName); - return TryParseUnityVersion(projectVersionString); - } - - return DEFAULT_UNITY_VERSION; - } - - //TODO UNIT TESTS - private static UnityVersion TryParseUnityVersion(string version) - { - string editorText = "m_EditorVersion: "; - var match = Regex.Match(version, editorText + "[0-9.a-z]*"); - - string src = match.Value.Substring(editorText.Length); - src = src.Replace('.', '_'); - src = src.Substring(0, src.IndexOf('_') + 2); - - var unityVersions = Enum.GetValues(typeof(UnityVersion)).Cast(); - foreach (var unityVersion in unityVersions) - { - if (Enum.GetName(typeof(UnityVersion), unityVersion).Contains(src)) - { - return unityVersion; - } - } - - return DEFAULT_UNITY_VERSION; - } } } diff --git a/UnityEngineAnalyzer.CLI/UnityEngineAnalyzer.CLI.csproj b/UnityEngineAnalyzer.CLI/UnityEngineAnalyzer.CLI.csproj index 48e3142..76e8c8d 100644 --- a/UnityEngineAnalyzer.CLI/UnityEngineAnalyzer.CLI.csproj +++ b/UnityEngineAnalyzer.CLI/UnityEngineAnalyzer.CLI.csproj @@ -118,6 +118,7 @@ + diff --git a/UnityEngineAnalyzer.CLI/UnityVersionResolver.cs b/UnityEngineAnalyzer.CLI/UnityVersionResolver.cs new file mode 100644 index 0000000..cb53e73 --- /dev/null +++ b/UnityEngineAnalyzer.CLI/UnityVersionResolver.cs @@ -0,0 +1,65 @@ +using System; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; + +namespace UnityEngineAnalyzer.CLI +{ + //NOTE: This class would benefit from UnitTests specially: TryParseUnityVersion + internal class UnityVersionResolver + { + private const UnityVersion DEFAULT_UNITY_VERSION = UnityVersion.LATEST; + + public UnityVersion ResolveVersion(Options options) + { + if (options.Version != UnityVersion.NONE) + { + return options.Version; + } + + //THIS ONLY WORKS ON UNITY >= 5, before that ProjectVersion.txt did not exists + if (ResolveProjectVersionFilePath(options) != null) + { + var projectVersionString = File.ReadAllText(ResolveProjectVersionFilePath(options)); + return TryParseUnityVersion(projectVersionString); + } + + return DEFAULT_UNITY_VERSION; + } + + private string ResolveProjectVersionFilePath(Options options) + { + var projectPath = new FileInfo(options.ProjectFile).Directory; + var path = Path.Combine(projectPath.FullName, "ProjectSettings", "ProjectVersion.txt"); + var projectVersionFile = new FileInfo(path); + + if (projectVersionFile.Exists) + { + return projectVersionFile.FullName; + } + + return null; + } + + private UnityVersion TryParseUnityVersion(string version) + { + string editorText = "m_EditorVersion: "; + var match = Regex.Match(version, editorText + "[0-9.a-z]*"); + + string src = match.Value.Substring(editorText.Length); + src = src.Replace('.', '_'); + src = src.Substring(0, src.IndexOf('_') + 2); + + var unityVersions = Enum.GetValues(typeof(UnityVersion)).Cast(); + foreach (var unityVersion in unityVersions) + { + if (Enum.GetName(typeof(UnityVersion), unityVersion).Contains(src)) + { + return unityVersion; + } + } + + return DEFAULT_UNITY_VERSION; + } + } +} From 5249fa3a9b51e6207b97bf5d14a72f535a3fa787 Mon Sep 17 00:00:00 2001 From: Kasperki Date: Thu, 19 Oct 2017 18:52:28 +0300 Subject: [PATCH 39/46] Updated DiagnosticDescriptors with new UnityVersion check. --- .../DiagnosticDescriptors.cs | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs index 34b4358..3e68c71 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs @@ -40,23 +40,32 @@ public static class DiagnosticDescriptors static DiagnosticDescriptors() { + //** UNITY ** + + //GC DoNotUseOnGUI = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseOnGUI, DiagnosticCategories.GC, DiagnosticSeverity.Info); DoNotUseStringMethods = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseStringMethods, DiagnosticCategories.GC, DiagnosticSeverity.Info); DoNotUseCoroutines = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseCoroutines, DiagnosticCategories.GC, DiagnosticSeverity.Info); - EmptyMonoBehaviourMethod = CreateDiagnosticDescriptor(DiagnosticIDs.EmptyMonoBehaviourMethod, DiagnosticCategories.Miscellaneous, DiagnosticSeverity.Warning); 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); + + //Performance DoNotUseFindMethodsInUpdate = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseFindMethodsInUpdate, DiagnosticCategories.Performance, DiagnosticSeverity.Warning); DoNotUseFindMethodsInUpdateRecursive = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseFindMethodsInUpdate, DiagnosticCategories.Performance, DiagnosticSeverity.Warning); - DoNotUseRemoting = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseRemoting, DiagnosticCategories.AOT, DiagnosticSeverity.Info); - DoNotUseReflectionEmit = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseReflectionEmit, DiagnosticCategories.AOT, DiagnosticSeverity.Info); - TypeGetType = CreateDiagnosticDescriptor(DiagnosticIDs.TypeGetType, DiagnosticCategories.AOT, DiagnosticSeverity.Info); - DoNotUseForEachInUpdate = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseForEachInUpdate, 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); - UseNonAllocMethods = CreateDiagnosticDescriptor(DiagnosticIDs.PhysicsUseNonAllocMethods, DiagnosticCategories.GC, DiagnosticSeverity.Warning, UnityVersion.UNITY_5_3); - CameraMainIsSlow = CreateDiagnosticDescriptor(DiagnosticIDs.CameraMainIsSlow, DiagnosticCategories.GC, DiagnosticSeverity.Warning); + + //Miscellaneous + EmptyMonoBehaviourMethod = CreateDiagnosticDescriptor(DiagnosticIDs.EmptyMonoBehaviourMethod, DiagnosticCategories.Miscellaneous, DiagnosticSeverity.Warning); + + //** AOT ** + DoNotUseRemoting = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseRemoting, DiagnosticCategories.AOT, DiagnosticSeverity.Info); + DoNotUseReflectionEmit = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseReflectionEmit, DiagnosticCategories.AOT, DiagnosticSeverity.Info); + TypeGetType = CreateDiagnosticDescriptor(DiagnosticIDs.TypeGetType, DiagnosticCategories.AOT, DiagnosticSeverity.Info); } private static DiagnosticDescriptor CreateDiagnosticDescriptor(string id, string category, DiagnosticSeverity severity, UnityVersion first = UnityVersion.UNITY_1_0, UnityVersion latest = UnityVersion.LATEST, bool isEnabledByDefault = true) From a933c1769ed1f64c26b3dce2c6d26b698a11f0a8 Mon Sep 17 00:00:00 2001 From: Kasperki Date: Thu, 19 Oct 2017 18:57:29 +0300 Subject: [PATCH 40/46] Updated readme.md --- readme.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/readme.md b/readme.md index 48609ee..175809c 100644 --- a/readme.md +++ b/readme.md @@ -20,6 +20,9 @@ In order to use the Command Line Interface (CLI), download the latest release of 1. (Optional) minimal severity for reports * Use command `-s Info/Warning/Error` to defined used minimal severity for reporting * Default is Warning +1. (Optional) Unity version for check + * Use command `-v UNITY_2017_1/UNITY_5_5/UNITY_4_0/...` to Unity version + * For default analyzer will try to find ProjectVersion.txt file and parse version automatically. Example: From 05ff37cd0e18a8c5d8899a374a3be4b246c3ce16 Mon Sep 17 00:00:00 2001 From: Donald Wu Date: Mon, 23 Oct 2017 16:55:50 +0800 Subject: [PATCH 41/46] 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 42/46] 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 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 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 43/46] 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 44/46] 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 45/46] 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 46/46] 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.