From bf9f360f3472a8ae7d6d37e01ae76b10da75ae35 Mon Sep 17 00:00:00 2001 From: Vinny Date: Fri, 4 Nov 2016 19:26:42 -0700 Subject: [PATCH 01/14] 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/14] 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/14] 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/14] 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/14] 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/14] 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/14] 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/14] 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 af7b247bb0d734da109a93e342cceab86828e493 Mon Sep 17 00:00:00 2001 From: Vinny Date: Wed, 11 Jan 2017 18:29:34 -0500 Subject: [PATCH 09/14] 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 10/14] 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 11/14] 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 12/14] 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 13/14] 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 14/14] 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; }