├── .gitattributes ├── .gitignore ├── Documents └── configuration.png ├── LICENSE ├── UnityEngineAnalyzer.CLI ├── AnalyzerReport.cs ├── App.config ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── Reporting │ ├── ConsoleAnalyzerExporter.cs │ ├── DiagnosticInfo.cs │ ├── IAnalyzerExporter.cs │ └── JsonAnalyzerExporter.cs ├── SolutionAnalyzer.cs ├── UnityEngineAnalyzer.CLI.csproj ├── UnityReport.html └── packages.config ├── UnityEngineAnalyzer.sln ├── UnityEngineAnalyzer ├── UnityEngineAnalyzer.Test │ ├── AOT │ │ ├── DoNotUseReflectionEmitAnalyzerTests.cs │ │ ├── DoNotUseRemotingAnalyzerTests.cs │ │ └── TypeGetTypeAnalyzerTests.cs │ ├── CompareTag │ │ └── UseCompareTagAnalyzerTests.cs │ ├── Coroutines │ │ └── DoNotUseCoroutinesAnalyzerTests.cs │ ├── EmptyMonoBehaviourMethods │ │ └── EmptyMonoBehaviourMethodsAnalyzerTests.cs │ ├── FindMethodsInUpdate │ │ └── DoNotUseFindMethodsInUpdateAnalyzerTests.cs │ ├── ForEachInUpdate │ │ └── DoNotUseForeachInUpdateAnalyzerTests.cs │ ├── MetadataReferenceHelper.cs │ ├── OnGUI │ │ └── DoNotUseOnGUIAnalyzerTests.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── StringMethods │ │ └── DoNotUseStringMethodsAnalyzerTests.cs │ ├── UnityEngineAnalyzer.Test.csproj │ └── packages.config ├── UnityEngineAnalyzer.Vsix │ ├── LICENSE.txt │ ├── UnityEngineAnalyzer.Vsix.csproj │ └── source.extension.vsixmanifest └── UnityEngineAnalyzer │ ├── AOT │ ├── DoNotUseReflectionEmitAnalyzer.cs │ ├── DoNotUseReflectionEmitResources.Designer.cs │ ├── DoNotUseReflectionEmitResources.resx │ ├── DoNotUseRemotingAnalyzer.cs │ ├── DoNotUseRemotingResources.Designer.cs │ ├── DoNotUseRemotingResources.resx │ ├── TypeGetTypeAnalyzer.cs │ ├── TypeGetTypeResources.Designer.cs │ └── TypeGetTypeResources.resx │ ├── CompareTag │ ├── UseCompareTagAnalyzer.cs │ ├── UseCompareTagResources.Designer.cs │ └── UseCompareTagResources.resx │ ├── Coroutines │ ├── DoNotUseCoroutinesAnalyzer.cs │ ├── DoNotUseCoroutinesResources.Designer.cs │ └── DoNotUseCoroutinesResources.resx │ ├── Diagnostic.nuspec │ ├── DiagnosticCategories.cs │ ├── DiagnosticDescriptors.cs │ ├── DiagnosticIDs.cs │ ├── EmptyMonoBehaviourMethods │ ├── EmptyMonoBehaviourMethodsAnalyzer.cs │ ├── EmptyMonoBehaviourMethodsResources.Designer.cs │ └── EmptyMonoBehaviourMethodsResources.resx │ ├── FindMethodsInUpdate │ ├── DoNotUseFindMethodsInUpdateAnalyzer.cs │ ├── DoNotUseFindMethodsInUpdateResources.Designer.cs │ └── DoNotUseFindMethodsInUpdateResources.resx │ ├── ForEachInUpdate │ ├── DoNotUseForEachInUpdate.cs │ ├── DoNotUseForEachInUpdateResources.Designer.cs │ └── DoNotUseForEachInUpdateResources.resx │ ├── MonoBehaviourInfo.cs │ ├── OnGUI │ ├── DoNotUseOnGUIAnalyzer.cs │ ├── DoNotUseOnGUIResources.Designer.cs │ └── DoNotUseOnGUIResources.resx │ ├── Properties │ └── AssemblyInfo.cs │ ├── ReadMe.txt │ ├── RolsynExtensions.cs │ ├── StringMethods │ ├── DoNotUseStringMethodsAnalyzer.cs │ ├── DoNotUseStringMethodsResources.Designer.cs │ └── DoNotUseStringMethodsResources.resx │ ├── UnityEngineAnalyzer.csproj │ ├── packages.config │ └── tools │ ├── install.ps1 │ └── uninstall.ps1 └── readme.md /.gitattributes: -------------------------------------------------------------------------------- 1 | 2 | * text=auto 3 | 4 | *.cs diff=csharp 5 | 6 | # Custom for Visual Studio 7 | *.sln text eol=crlf 8 | *.csproj text eol=crlf 9 | *.vbproj text eol=crlf 10 | *.fsproj text eol=crlf 11 | *.dbproj text eol=crlf 12 | 13 | *.vcxproj text eol=crlf 14 | *.vcxitems text eol=crlf 15 | *.props text eol=crlf 16 | *.filters text eol=crlf 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | ### VisualStudio ### 3 | ## Ignore Visual Studio temporary files, build results, and 4 | ## files generated by popular Visual Studio add-ons. 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | 26 | # Visual Studio 2015 cache/options directory 27 | .vs/ 28 | # Uncomment if you have tasks that create the project's static files in wwwroot 29 | #wwwroot/ 30 | 31 | # MSTest test Results 32 | [Tt]est[Rr]esult*/ 33 | [Bb]uild[Ll]og.* 34 | 35 | # NUNIT 36 | *.VisualState.xml 37 | TestResult.xml 38 | 39 | # Build Results of an ATL Project 40 | [Dd]ebugPS/ 41 | [Rr]eleasePS/ 42 | dlldata.c 43 | 44 | # DNX 45 | project.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | # TODO: Comment the next line if you want to checkin your web deploy settings 143 | # but database connection strings (with potential passwords) will be unencrypted 144 | *.pubxml 145 | *.publishproj 146 | 147 | # NuGet Packages 148 | *.nupkg 149 | # The packages folder can be ignored because of Package Restore 150 | **/packages/* 151 | # except build/, which is used as an MSBuild target. 152 | !**/packages/build/ 153 | # Uncomment if necessary however generally it will be regenerated when needed 154 | #!**/packages/repositories.config 155 | # NuGet v3's project.json files produces more ignoreable files 156 | *.nuget.props 157 | *.nuget.targets 158 | 159 | # Microsoft Azure Build Output 160 | csx/ 161 | *.build.csdef 162 | 163 | # Microsoft Azure Emulator 164 | ecf/ 165 | rcf/ 166 | 167 | # Microsoft Azure ApplicationInsights config file 168 | ApplicationInsights.config 169 | 170 | # Windows Store app package directory 171 | AppPackages/ 172 | BundleArtifacts/ 173 | 174 | # Visual Studio cache files 175 | # files ending in .cache can be ignored 176 | *.[Cc]ache 177 | # but keep track of directories ending in .cache 178 | !*.[Cc]ache/ 179 | 180 | # Others 181 | ClientBin/ 182 | ~$* 183 | *~ 184 | *.dbmdl 185 | *.dbproj.schemaview 186 | *.pfx 187 | *.publishsettings 188 | node_modules/ 189 | orleans.codegen.cs 190 | 191 | # RIA/Silverlight projects 192 | Generated_Code/ 193 | 194 | # Backup & report files from converting an old project file 195 | # to a newer Visual Studio version. Backup files are not needed, 196 | # because we have git ;-) 197 | _UpgradeReport_Files/ 198 | Backup*/ 199 | UpgradeLog*.XML 200 | UpgradeLog*.htm 201 | 202 | # SQL Server files 203 | *.mdf 204 | *.ldf 205 | 206 | # Business Intelligence projects 207 | *.rdl.data 208 | *.bim.layout 209 | *.bim_*.settings 210 | 211 | # Microsoft Fakes 212 | FakesAssemblies/ 213 | 214 | # GhostDoc plugin setting file 215 | *.GhostDoc.xml 216 | 217 | # Node.js Tools for Visual Studio 218 | .ntvs_analysis.dat 219 | 220 | # Visual Studio 6 build log 221 | *.plg 222 | 223 | # Visual Studio 6 workspace options file 224 | *.opt 225 | 226 | # Visual Studio LightSwitch build output 227 | **/*.HTMLClient/GeneratedArtifacts 228 | **/*.DesktopClient/GeneratedArtifacts 229 | **/*.DesktopClient/ModelManifest.xml 230 | **/*.Server/GeneratedArtifacts 231 | **/*.Server/ModelManifest.xml 232 | _Pvt_Extensions 233 | 234 | # Paket dependency manager 235 | .paket/paket.exe 236 | 237 | # FAKE - F# Make 238 | .fake/ 239 | 240 | 241 | ### Windows ### 242 | # Windows image file caches 243 | Thumbs.db 244 | ehthumbs.db 245 | 246 | # Folder config file 247 | Desktop.ini 248 | 249 | # Recycle Bin used on file shares 250 | $RECYCLE.BIN/ 251 | 252 | # Windows Installer files 253 | *.cab 254 | *.msi 255 | *.msm 256 | *.msp 257 | 258 | # Windows shortcuts 259 | *.lnk 260 | -------------------------------------------------------------------------------- /Documents/configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meng-hui/UnityEngineAnalyzer/8ce89373ea7ff1965b51cb728253274cfb2b1cc6/Documents/configuration.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Meng Hui Koh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer.CLI/AnalyzerReport.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using Microsoft.CodeAnalysis; 5 | using UnityEngineAnalyzer.CLI.Reporting; 6 | 7 | namespace UnityEngineAnalyzer.CLI 8 | { 9 | public class AnalyzerReport 10 | { 11 | //TODO: Add support for Solutions with multiple Projects 12 | 13 | private readonly List _exporters = new List(); 14 | 15 | public void AddExporter(IAnalyzerExporter exporter) 16 | { 17 | _exporters.Add(exporter); 18 | } 19 | 20 | public void AppendDiagnostics(IEnumerable diagnosticResults) 21 | { 22 | if (_exporters.Count == 0) 23 | { 24 | return; 25 | } 26 | 27 | foreach (var diagnostic in diagnosticResults) 28 | { 29 | var locationSpan = diagnostic.Location.SourceSpan; 30 | var lineSpan = diagnostic.Location.SourceTree.GetLineSpan(locationSpan); 31 | 32 | var diagnosticInfo = new DiagnosticInfo 33 | { 34 | Id = diagnostic.Id, 35 | Message = diagnostic.GetMessage(), 36 | FileName = diagnostic.Location.SourceTree.FilePath, 37 | LineNumber = lineSpan.StartLinePosition.Line, 38 | Severity = (DiagnosticInfoSeverity)diagnostic.Severity 39 | }; 40 | 41 | 42 | foreach (var exporter in _exporters) 43 | { 44 | exporter.AppendDiagnostic(diagnosticInfo); 45 | } 46 | } 47 | } 48 | 49 | public void FinalizeReport(TimeSpan duration) 50 | { 51 | foreach (var exporter in _exporters) 52 | { 53 | exporter.Finish(duration); 54 | } 55 | } 56 | 57 | public void InitializeReport(FileInfo projectFile) 58 | { 59 | foreach (var exporter in _exporters) 60 | { 61 | exporter.InitializeExporter(projectFile); 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer.CLI/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer.CLI/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using UnityEngineAnalyzer.CLI.Reporting; 7 | 8 | namespace UnityEngineAnalyzer.CLI 9 | { 10 | public class Program 11 | { 12 | public static void Main(string[] args) 13 | { 14 | try 15 | { 16 | if (args.Length <= 0) 17 | { 18 | return; 19 | } 20 | 21 | var startTime = DateTime.Now; 22 | 23 | var fileName = args[0]; 24 | var fileInfo = new FileInfo(fileName); 25 | 26 | //NOTE: This could be configurable via the CLI at some point 27 | var report = new AnalyzerReport(); 28 | report.AddExporter(new ConsoleAnalyzerExporter()); 29 | report.AddExporter(new JsonAnalyzerExporter()); 30 | 31 | 32 | report.InitializeReport(fileInfo); 33 | 34 | var tasks = new List(); 35 | if (fileInfo.Exists) 36 | { 37 | var solutionAnalyzer = new SolutionAnalyzer(); 38 | var analyzeTask = solutionAnalyzer.LoadAnadAnalyzeProject(fileInfo, report); 39 | tasks.Add(analyzeTask); 40 | } 41 | 42 | Task.WaitAll(tasks.ToArray()); 43 | 44 | var endTime = DateTime.Now; 45 | var duration = endTime - startTime; 46 | 47 | report.FinalizeReport(duration); 48 | 49 | Console.WriteLine("Press any key to exit..."); 50 | Console.ReadKey(); 51 | } 52 | catch (Exception generalException) 53 | { 54 | 55 | Console.WriteLine("There was an exception running the analysis"); 56 | Console.WriteLine(generalException.ToString()); 57 | } 58 | 59 | 60 | 61 | } 62 | 63 | 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer.CLI/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("UnityEngineAnalyzer.CLI")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("UnityEngineAnalyzer.CLI")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("9c2b02d9-9dcf-4617-aef5-c00b2a332b83")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer.CLI/Reporting/ConsoleAnalyzerExporter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace UnityEngineAnalyzer.CLI.Reporting 5 | { 6 | public class ConsoleAnalyzerExporter : IAnalyzerExporter 7 | { 8 | private const string ConsoleSeparator = "\t"; 9 | private const DiagnosticInfoSeverity MinimalSeverity = DiagnosticInfoSeverity.Warning; 10 | 11 | public void AppendDiagnostic(DiagnosticInfo diagnosticInfo) 12 | { 13 | if (diagnosticInfo.Severity < MinimalSeverity) 14 | { 15 | return; 16 | } 17 | 18 | Console.Write(diagnosticInfo.Id); 19 | Console.Write(ConsoleSeparator); 20 | 21 | Console.ForegroundColor = ConsoleColorFromSeverity(diagnosticInfo.Severity); 22 | Console.Write(diagnosticInfo.Severity.ToString()); 23 | Console.Write(ConsoleSeparator); 24 | 25 | Console.ForegroundColor = ConsoleColor.Cyan; 26 | Console.Write(diagnosticInfo.Message); 27 | Console.ResetColor(); 28 | Console.Write(ConsoleSeparator); 29 | Console.WriteLine(@"{0}({1})",diagnosticInfo.FileName,diagnosticInfo.LineNumber); 30 | } 31 | 32 | private ConsoleColor ConsoleColorFromSeverity(DiagnosticInfoSeverity severity) 33 | { 34 | switch (severity) 35 | { 36 | case DiagnosticInfoSeverity.Hidden: 37 | return ConsoleColor.Gray; 38 | case DiagnosticInfoSeverity.Info: 39 | return ConsoleColor.Green; 40 | case DiagnosticInfoSeverity.Warning: 41 | return ConsoleColor.Yellow; 42 | case DiagnosticInfoSeverity.Error: 43 | return ConsoleColor.Red; 44 | default: 45 | return ConsoleColor.White; 46 | } 47 | } 48 | 49 | public void Finish(TimeSpan duration) 50 | { 51 | Console.ForegroundColor = ConsoleColor.Green; 52 | Console.WriteLine("Console Export Finished ({0})", duration); 53 | Console.ResetColor(); 54 | } 55 | 56 | public void InitializeExporter(FileInfo projectFile) 57 | { 58 | Console.ForegroundColor = ConsoleColor.Green; 59 | Console.WriteLine("Unity Syntax Analyzer"); 60 | Console.WriteLine(); 61 | Console.WriteLine("Analyzing: {0}", projectFile.FullName); 62 | Console.WriteLine(); 63 | Console.ResetColor(); 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /UnityEngineAnalyzer.CLI/Reporting/DiagnosticInfo.cs: -------------------------------------------------------------------------------- 1 | namespace UnityEngineAnalyzer.CLI.Reporting 2 | { 3 | public enum DiagnosticInfoSeverity 4 | { 5 | Hidden = 0, 6 | Info = 1, 7 | Warning = 2, 8 | Error = 3 9 | } 10 | 11 | public class DiagnosticInfo 12 | { 13 | public string Id { get; set; } 14 | public string Message { get; set; } 15 | public string FileName { get; set; } 16 | public int LineNumber { get; set; } 17 | public DiagnosticInfoSeverity Severity { get; set; } 18 | } 19 | } -------------------------------------------------------------------------------- /UnityEngineAnalyzer.CLI/Reporting/IAnalyzerExporter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace UnityEngineAnalyzer.CLI.Reporting 5 | { 6 | public interface IAnalyzerExporter 7 | { 8 | void AppendDiagnostic(DiagnosticInfo diagnosticInfo); 9 | void Finish(TimeSpan duration); 10 | void InitializeExporter(FileInfo projectFile); 11 | } 12 | } -------------------------------------------------------------------------------- /UnityEngineAnalyzer.CLI/Reporting/JsonAnalyzerExporter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using Newtonsoft.Json; 5 | 6 | namespace UnityEngineAnalyzer.CLI.Reporting 7 | { 8 | public class JsonAnalyzerExporter : IAnalyzerExporter 9 | { 10 | private const string JsonReportFileName = "report.json"; 11 | private const string HtmlReportFileName = "UnityReport.html"; 12 | private const DiagnosticInfoSeverity MinimalSeverity = DiagnosticInfoSeverity.Warning; 13 | 14 | 15 | private JsonTextWriter _jsonWriter; 16 | private readonly JsonSerializer _jsonSerializer = new JsonSerializer(); 17 | private string _destinationReportFile; 18 | 19 | 20 | public void AppendDiagnostic(DiagnosticInfo diagnosticInfo) 21 | { 22 | if (diagnosticInfo.Severity >= MinimalSeverity) 23 | { 24 | _jsonSerializer.Serialize(_jsonWriter, diagnosticInfo); 25 | } 26 | } 27 | 28 | public void Finish(TimeSpan duration) 29 | { 30 | _jsonWriter.WriteEndArray(); 31 | _jsonWriter.WriteEndObject(); 32 | _jsonWriter.Close(); 33 | 34 | 35 | File.Copy(HtmlReportFileName, _destinationReportFile, true); 36 | 37 | //NOTE: This code might be temporary as it assumes that the CLI is being executed interactively 38 | //Process.Start(_destinationReportFile); 39 | } 40 | 41 | public void InitializeExporter(FileInfo projectFile) 42 | { 43 | if (!projectFile.Exists) 44 | { 45 | throw new ArgumentException("Project file does not exist"); 46 | } 47 | 48 | _destinationReportFile = Path.Combine(projectFile.DirectoryName, HtmlReportFileName); 49 | var jsonFilePath = Path.Combine(projectFile.DirectoryName, JsonReportFileName); 50 | var jsonFile = new FileInfo(jsonFilePath); 51 | 52 | if (jsonFile.Exists) 53 | { 54 | jsonFile.Delete(); 55 | } 56 | 57 | TextWriter textWriter = new StreamWriter(jsonFile.FullName); 58 | _jsonWriter = new JsonTextWriter(textWriter); 59 | 60 | _jsonWriter.WriteStartObject(); 61 | _jsonWriter.WritePropertyName("File"); 62 | _jsonWriter.WriteValue(projectFile.FullName); 63 | _jsonWriter.WritePropertyName("Date"); 64 | _jsonWriter.WriteValue(DateTime.Now); 65 | _jsonWriter.WritePropertyName("Diagnostics"); 66 | _jsonWriter.WriteStartArray(); 67 | 68 | 69 | _jsonSerializer.Formatting = Formatting.Indented; 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /UnityEngineAnalyzer.CLI/SolutionAnalyzer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Immutable; 3 | using System.IO; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Microsoft.CodeAnalysis; 7 | using Microsoft.CodeAnalysis.Diagnostics; 8 | using Microsoft.CodeAnalysis.MSBuild; 9 | using UnityEngineAnalyzer.ForEachInUpdate; 10 | 11 | namespace UnityEngineAnalyzer.CLI 12 | { 13 | public class SolutionAnalyzer 14 | { 15 | public async Task LoadAnadAnalyzeProject(FileInfo projectFile, AnalyzerReport report) //TODO: Add async suffix 16 | { 17 | var workspace = MSBuildWorkspace.Create(); 18 | 19 | var project = await workspace.OpenProjectAsync(projectFile.FullName, CancellationToken.None); 20 | 21 | var analyzers = this.GetAnalyzers(); 22 | 23 | await AnalyzeProject(project, analyzers, report); 24 | } 25 | 26 | private ImmutableArray GetAnalyzers() 27 | { 28 | var listBuilder = ImmutableArray.CreateBuilder(); 29 | 30 | var assembly = typeof(DoNotUseForEachInUpdate).Assembly; 31 | var allTypes = assembly.DefinedTypes; 32 | 33 | foreach (var type in allTypes) 34 | { 35 | if (type.BaseType == typeof(DiagnosticAnalyzer)) 36 | { 37 | var instance = Activator.CreateInstance(type) as DiagnosticAnalyzer; 38 | listBuilder.Add(instance); 39 | } 40 | } 41 | 42 | 43 | var analyzers = listBuilder.ToImmutable(); 44 | return analyzers; 45 | } 46 | 47 | private async Task AnalyzeProject(Project project, ImmutableArray analyzers, AnalyzerReport report) 48 | { 49 | var compilation = await project.GetCompilationAsync(); 50 | 51 | var diagnosticResults = await compilation.WithAnalyzers(analyzers).GetAnalyzerDiagnosticsAsync(); 52 | 53 | report.AppendDiagnostics(diagnosticResults); 54 | 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer.CLI/UnityEngineAnalyzer.CLI.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {9C2B02D9-9DCF-4617-AEF5-C00B2A332B83} 8 | Exe 9 | Properties 10 | UnityEngineAnalyzer.CLI 11 | UnityEngineAnalyzer.CLI 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | ..\packages\Microsoft.CodeAnalysis.Common.1.3.2\lib\net45\Microsoft.CodeAnalysis.dll 38 | True 39 | 40 | 41 | ..\packages\Microsoft.CodeAnalysis.CSharp.1.3.2\lib\net45\Microsoft.CodeAnalysis.CSharp.dll 42 | True 43 | 44 | 45 | ..\packages\Microsoft.CodeAnalysis.CSharp.Workspaces.1.3.2\lib\net45\Microsoft.CodeAnalysis.CSharp.Workspaces.dll 46 | True 47 | 48 | 49 | ..\packages\Microsoft.CodeAnalysis.Workspaces.Common.1.3.2\lib\net45\Microsoft.CodeAnalysis.Workspaces.dll 50 | True 51 | 52 | 53 | ..\packages\Microsoft.CodeAnalysis.Workspaces.Common.1.3.2\lib\net45\Microsoft.CodeAnalysis.Workspaces.Desktop.dll 54 | True 55 | 56 | 57 | ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll 58 | True 59 | 60 | 61 | 62 | ..\packages\System.Collections.Immutable.1.1.37\lib\dotnet\System.Collections.Immutable.dll 63 | True 64 | 65 | 66 | ..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.AttributedModel.dll 67 | True 68 | 69 | 70 | ..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.Convention.dll 71 | True 72 | 73 | 74 | ..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.Hosting.dll 75 | True 76 | 77 | 78 | ..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.Runtime.dll 79 | True 80 | 81 | 82 | ..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.TypedParts.dll 83 | True 84 | 85 | 86 | 87 | ..\packages\System.Reflection.Metadata.1.2.0\lib\portable-net45+win8\System.Reflection.Metadata.dll 88 | True 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | {bfb2bb34-fed8-48cc-9b83-a6e38ba5666c} 114 | UnityEngineAnalyzer 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | PreserveNewest 124 | 125 | 126 | 127 | 134 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer.CLI/UnityReport.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Unity Engine Analyzer Report 10 | 11 | 12 | 13 | 14 | 18 | 19 | 20 | 21 | 22 |
23 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
IdSeverityMessageFileLine Number
{{diagnostic.Id}}{{diagnostic.Severity}}{{diagnostic.Message}}{{diagnostic.FileName}}{{diagnostic.LineNumber}}
48 |
49 | 50 | 51 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer.CLI/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityEngineAnalyzer", "UnityEngineAnalyzer\UnityEngineAnalyzer\UnityEngineAnalyzer.csproj", "{BFB2BB34-FED8-48CC-9B83-A6E38BA5666C}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityEngineAnalyzer.Test", "UnityEngineAnalyzer\UnityEngineAnalyzer.Test\UnityEngineAnalyzer.Test.csproj", "{76CAC24F-3E52-43A1-A696-6848EB9C1E7B}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityEngineAnalyzer.Vsix", "UnityEngineAnalyzer\UnityEngineAnalyzer.Vsix\UnityEngineAnalyzer.Vsix.csproj", "{23A41489-857C-4FC7-9B96-2F20D8EE380B}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityEngineAnalyzer.CLI", "UnityEngineAnalyzer.CLI\UnityEngineAnalyzer.CLI.csproj", "{9C2B02D9-9DCF-4617-AEF5-C00B2A332B83}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {BFB2BB34-FED8-48CC-9B83-A6E38BA5666C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {BFB2BB34-FED8-48CC-9B83-A6E38BA5666C}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {BFB2BB34-FED8-48CC-9B83-A6E38BA5666C}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {BFB2BB34-FED8-48CC-9B83-A6E38BA5666C}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {76CAC24F-3E52-43A1-A696-6848EB9C1E7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {76CAC24F-3E52-43A1-A696-6848EB9C1E7B}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {76CAC24F-3E52-43A1-A696-6848EB9C1E7B}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {76CAC24F-3E52-43A1-A696-6848EB9C1E7B}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {23A41489-857C-4FC7-9B96-2F20D8EE380B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {23A41489-857C-4FC7-9B96-2F20D8EE380B}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {23A41489-857C-4FC7-9B96-2F20D8EE380B}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {23A41489-857C-4FC7-9B96-2F20D8EE380B}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {9C2B02D9-9DCF-4617-AEF5-C00B2A332B83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {9C2B02D9-9DCF-4617-AEF5-C00B2A332B83}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {9C2B02D9-9DCF-4617-AEF5-C00B2A332B83}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {9C2B02D9-9DCF-4617-AEF5-C00B2A332B83}.Release|Any CPU.Build.0 = Release|Any CPU 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | EndGlobal 41 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer.Test/AOT/DoNotUseReflectionEmitAnalyzerTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.Diagnostics; 3 | using NUnit.Framework; 4 | using RoslynNUnitLight; 5 | using UnityEngineAnalyzer.AOT; 6 | 7 | namespace UnityEngineAnalyzer.Test.AOT 8 | { 9 | [TestFixture] 10 | sealed class DoNotUseReflectionEmitAnalyzerTests : AnalyzerTestFixture 11 | { 12 | protected override string LanguageName => LanguageNames.CSharp; 13 | protected override DiagnosticAnalyzer CreateAnalyzer() => new DoNotUseReflectionEmitAnalyzer(); 14 | 15 | [Test] 16 | public void UsingReflectionEmit() 17 | { 18 | const string code = @" 19 | [|using System.Reflection.Emit;|] 20 | 21 | class C { } 22 | "; 23 | HasDiagnostic(code, DiagnosticIDs.DoNotUseReflectionEmit); 24 | } 25 | 26 | [Test] 27 | public void UsingReflectionEmitNested() 28 | { 29 | const string code = @" 30 | namespace N 31 | { 32 | [|using System.Reflection.Emit;|] 33 | 34 | class C { } 35 | }"; 36 | HasDiagnostic(code, DiagnosticIDs.DoNotUseReflectionEmit); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer.Test/AOT/DoNotUseRemotingAnalyzerTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.Diagnostics; 3 | using NUnit.Framework; 4 | using RoslynNUnitLight; 5 | using UnityEngineAnalyzer.AOT; 6 | 7 | namespace UnityEngineAnalyzer.Test.AOT 8 | { 9 | [TestFixture] 10 | sealed class DoNotUseRemotingAnalyzerTests : AnalyzerTestFixture 11 | { 12 | protected override string LanguageName => LanguageNames.CSharp; 13 | protected override DiagnosticAnalyzer CreateAnalyzer() => new DoNotUseRemotingAnalyzer(); 14 | 15 | [Test] 16 | public void UsingRemoting() 17 | { 18 | const string code = @" 19 | [|using System.Runtime.Remoting;|] 20 | 21 | class C { } 22 | "; 23 | HasDiagnostic(code, DiagnosticIDs.DoNotUseRemoting); 24 | } 25 | 26 | [Test] 27 | public void UsingRemotingNested() 28 | { 29 | const string code = @" 30 | namespace N 31 | { 32 | [|using System.Runtime.Remoting;|] 33 | 34 | class C { } 35 | }"; 36 | HasDiagnostic(code, DiagnosticIDs.DoNotUseRemoting); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer.Test/AOT/TypeGetTypeAnalyzerTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.Diagnostics; 3 | using Microsoft.CodeAnalysis.Text; 4 | using NUnit.Framework; 5 | using RoslynNUnitLight; 6 | using UnityEngineAnalyzer.AOT; 7 | 8 | namespace UnityEngineAnalyzer.Test.AOT 9 | { 10 | [TestFixture] 11 | sealed class TypeGetTypeAnalyzerTests : AnalyzerTestFixture 12 | { 13 | protected override string LanguageName => LanguageNames.CSharp; 14 | protected override DiagnosticAnalyzer CreateAnalyzer() => new TypeGetTypeAnalyzer(); 15 | 16 | [Test] 17 | public void TypeGetTypeIsUsed() 18 | { 19 | const string code = @" 20 | using UnityEngine; 21 | using System; 22 | 23 | class C : MonoBehaviour 24 | { 25 | void Start() 26 | { 27 | var theType = [|Type.GetType("""")|]; 28 | } 29 | }"; 30 | Document document; 31 | TextSpan span; 32 | 33 | var references = MetadataReferenceHelper.UsingUnityEngine; 34 | 35 | if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, references, out document, out span)) 36 | { 37 | this.HasDiagnostic(document, span, DiagnosticIDs.TypeGetType); 38 | } 39 | else 40 | { 41 | Assert.Fail("Could not load the Test code in the unit test"); 42 | } 43 | 44 | 45 | 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer.Test/CompareTag/UseCompareTagAnalyzerTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.Diagnostics; 3 | using Microsoft.CodeAnalysis.Text; 4 | using NUnit.Framework; 5 | using RoslynNUnitLight; 6 | using UnityEngineAnalyzer.CompareTag; 7 | 8 | namespace UnityEngineAnalyzer.Test.CompareTag 9 | { 10 | [TestFixture] 11 | sealed class UseCompareTagAnalyzerTests : AnalyzerTestFixture 12 | { 13 | protected override string LanguageName => LanguageNames.CSharp; 14 | protected override DiagnosticAnalyzer CreateAnalyzer() => new UseCompareTagAnalyzer(); 15 | 16 | [Test] 17 | public void UseTagForComparison() 18 | { 19 | const string code = @" 20 | using UnityEngine; 21 | 22 | class C : MonoBehaviour 23 | { 24 | void Start() 25 | { 26 | var a = [|tag == ""Enemy""|]; 27 | } 28 | }"; 29 | 30 | Document document; 31 | TextSpan span; 32 | TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); 33 | 34 | HasDiagnostic(document, span, DiagnosticIDs.UseCompareTag); 35 | } 36 | 37 | [Test] 38 | public void UseGameObjectTagForComparison() 39 | { 40 | const string code = @" 41 | using UnityEngine; 42 | 43 | class C : MonoBehaviour 44 | { 45 | void Start() 46 | { 47 | var a = [|gameObject.tag == ""Enemy""|]; 48 | } 49 | }"; 50 | 51 | Document document; 52 | TextSpan span; 53 | TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); 54 | 55 | HasDiagnostic(document, span, DiagnosticIDs.UseCompareTag); 56 | } 57 | 58 | [Test] 59 | public void UseMonoBehaviourTagForComparison() 60 | { 61 | const string code = @" 62 | using UnityEngine; 63 | 64 | class CC : MonoBehaviour { } 65 | 66 | class C : MonoBehaviour 67 | { 68 | private CC cc; 69 | void Start() 70 | { 71 | var a = [|""Enemy"".Equals(cc.tag)|]; 72 | } 73 | }"; 74 | 75 | Document document; 76 | TextSpan span; 77 | TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); 78 | 79 | HasDiagnostic(document, span, DiagnosticIDs.UseCompareTag); 80 | } 81 | 82 | [Test] 83 | public void OtherEqualsComparison() 84 | { 85 | const string code = @" 86 | using UnityEngine; 87 | 88 | class C : MonoBehaviour 89 | { 90 | void Start() 91 | { 92 | var a = [|""Player"".Equals(""Enemy"")|]; 93 | } 94 | }"; 95 | 96 | Document document; 97 | TextSpan span; 98 | TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); 99 | 100 | NoDiagnostic(document, DiagnosticIDs.UseCompareTag); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Coroutines/DoNotUseCoroutinesAnalyzerTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.Diagnostics; 3 | using Microsoft.CodeAnalysis.Text; 4 | using NUnit.Framework; 5 | using RoslynNUnitLight; 6 | using UnityEngineAnalyzer.Coroutines; 7 | 8 | namespace UnityEngineAnalyzer.Test.Coroutines 9 | { 10 | [TestFixture] 11 | sealed class DoNotUseCoroutinesAnalyzerTests : AnalyzerTestFixture 12 | { 13 | protected override string LanguageName => LanguageNames.CSharp; 14 | protected override DiagnosticAnalyzer CreateAnalyzer() => new DoNotUseCoroutinesAnalyzer(); 15 | 16 | [Test] 17 | public void StartCoroutineUsedInMonoBehaviourClass() 18 | { 19 | const string code = @" 20 | using UnityEngine; 21 | 22 | class C : MonoBehaviour 23 | { 24 | void M() 25 | { 26 | [|StartCoroutine(""MyCoroutine"")|]; 27 | } 28 | }"; 29 | Document document; 30 | TextSpan span; 31 | TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); 32 | 33 | HasDiagnostic(document, span, DiagnosticIDs.DoNotUseCoroutines); 34 | } 35 | 36 | [Test] 37 | public void StartCoroutineUsedByMonoBehaviourClass() 38 | { 39 | const string code = @" 40 | using UnityEngine; 41 | 42 | class CC : MonoBehaviour { } 43 | 44 | class C : MonoBehaviour 45 | { 46 | private CC cc; 47 | void M() 48 | { 49 | [|cc.StartCoroutine(""MyCoroutine"")|]; 50 | } 51 | }"; 52 | Document document; 53 | TextSpan span; 54 | TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); 55 | 56 | HasDiagnostic(document, span, DiagnosticIDs.DoNotUseCoroutines); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer.Test/EmptyMonoBehaviourMethods/EmptyMonoBehaviourMethodsAnalyzerTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.Diagnostics; 3 | using Microsoft.CodeAnalysis.Text; 4 | using NUnit.Framework; 5 | using RoslynNUnitLight; 6 | using UnityEngineAnalyzer.EmptyMonoBehaviourMethods; 7 | 8 | namespace UnityEngineAnalyzer.Test.EmptyMonoBehaviourMethods 9 | { 10 | [TestFixture] 11 | sealed class EmptyMonoBehaviourMethodsAnalyzerTests : AnalyzerTestFixture 12 | { 13 | protected override string LanguageName => LanguageNames.CSharp; 14 | protected override DiagnosticAnalyzer CreateAnalyzer() => new EmptyMonoBehaviourMethodsAnalyzer(); 15 | 16 | [Test] 17 | public void EmptyUpdateInMonoBehaviour() 18 | { 19 | const string code = @" 20 | using UnityEngine; 21 | 22 | class C : MonoBehaviour 23 | { 24 | [|void Update() { }|] 25 | }"; 26 | Document document; 27 | TextSpan span; 28 | TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); 29 | 30 | HasDiagnostic(document, span, DiagnosticIDs.EmptyMonoBehaviourMethod); 31 | } 32 | 33 | [Test] 34 | public void EmptyUpdateInNormalClass() 35 | { 36 | const string code = @" 37 | using UnityEngine; 38 | 39 | class C 40 | { 41 | [|void Update() { }|] 42 | }"; 43 | Document document; 44 | TextSpan span; 45 | TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); 46 | 47 | NoDiagnostic(document, DiagnosticIDs.EmptyMonoBehaviourMethod); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer.Test/FindMethodsInUpdate/DoNotUseFindMethodsInUpdateAnalyzerTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.Diagnostics; 3 | using Microsoft.CodeAnalysis.Text; 4 | using NUnit.Framework; 5 | using RoslynNUnitLight; 6 | using UnityEngineAnalyzer.FindMethodsInUpdate; 7 | 8 | 9 | //using Microsoft.CodeAnalysis.Workspaces; 10 | 11 | namespace UnityEngineAnalyzer.Test.FindMethodsInUpdate 12 | { 13 | [TestFixture] 14 | sealed class DoNotUseFindMethodsInUpdateAnalyzerTests : AnalyzerTestFixture 15 | { 16 | 17 | protected override string LanguageName => LanguageNames.CSharp; 18 | protected override DiagnosticAnalyzer CreateAnalyzer() => new DoNotUseFindMethodsInUpdateAnalyzer(); 19 | 20 | [Test] 21 | public void GameObjectFindInUpdate() 22 | { 23 | var code = @" 24 | using UnityEngine; 25 | 26 | class C : MonoBehaviour 27 | { 28 | void Update() 29 | { 30 | [|GameObject.Find(""param"")|]; 31 | 32 | //var result = GameObject.Find(""param""); 33 | } 34 | }"; 35 | 36 | Document document; 37 | TextSpan span; 38 | 39 | if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, 40 | out document, out span)) 41 | { 42 | HasDiagnostic(document, span, DiagnosticIDs.DoNotUseFindMethodsInUpdate); 43 | } 44 | else 45 | { 46 | Assert.Fail("Could not load unit test code"); 47 | } 48 | } 49 | 50 | [Test] 51 | public void GameObjectFindInStart() 52 | { 53 | const string code = @" 54 | using UnityEngine; 55 | 56 | class C : MonoBehaviour 57 | { 58 | void Start() 59 | { 60 | [|GameObject.Find("")|]; 61 | } 62 | }"; 63 | Document document; 64 | TextSpan span; 65 | TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); 66 | 67 | NoDiagnostic(document, DiagnosticIDs.EmptyMonoBehaviourMethod); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer.Test/ForEachInUpdate/DoNotUseForeachInUpdateAnalyzerTests.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using Microsoft.CodeAnalysis; 3 | using Microsoft.CodeAnalysis.Diagnostics; 4 | using Microsoft.CodeAnalysis.Text; 5 | using NUnit.Framework; 6 | using RoslynNUnitLight; 7 | using UnityEngineAnalyzer.FindMethodsInUpdate; 8 | using UnityEngineAnalyzer.ForEachInUpdate; 9 | 10 | //using Microsoft.CodeAnalysis.Workspaces; 11 | 12 | namespace UnityEngineAnalyzer.Test.ForEachInUpdate 13 | { 14 | [TestFixture] 15 | sealed class DoNotUseForeachInUpdateAnalyzerTests : AnalyzerTestFixture 16 | { 17 | 18 | protected override string LanguageName => LanguageNames.CSharp; 19 | protected override DiagnosticAnalyzer CreateAnalyzer() => new DoNotUseForEachInUpdate(); 20 | 21 | [Test] 22 | public void ForEachInUpdate() 23 | { 24 | var code = @" 25 | using UnityEngine; 26 | 27 | class C : MonoBehaviour 28 | { 29 | void Update() 30 | { 31 | var colors = new[] {""red"", ""white"", ""blue""}; 32 | var result = string.Empty; 33 | [|foreach|] (var color in colors) 34 | { 35 | result += color; 36 | } 37 | } 38 | }"; 39 | 40 | 41 | 42 | Document document; 43 | TextSpan span; 44 | 45 | if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, 46 | out document, out span)) 47 | { 48 | HasDiagnostic(document, span, DiagnosticIDs.DoNotUseForEachInUpdate); 49 | } 50 | else 51 | { 52 | Assert.Fail("Could not load unit test code"); 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer.Test/MetadataReferenceHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.CodeAnalysis; 3 | using System.Collections.Immutable; 4 | using System.IO; 5 | using System.Linq; 6 | 7 | 8 | namespace UnityEngineAnalyzer.Test 9 | { 10 | static class MetadataReferenceHelper 11 | { 12 | public static readonly ImmutableList UsingUnityEngine = 13 | ImmutableList.Create(GetUnityMetadataReference(), GetSystem(), GetSystemCore()); 14 | 15 | private static MetadataReference GetUnityMetadataReference() 16 | { 17 | var unityEnginePath = Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramW6432%"), @"Unity\Editor\Data\Managed", "UnityEngine.dll"); 18 | 19 | return MetadataReference.CreateFromFile(unityEnginePath); 20 | } 21 | 22 | private static MetadataReference GetSystem() 23 | { 24 | var assemblyPath = typeof(object).Assembly.Location; 25 | return MetadataReference.CreateFromFile(assemblyPath); 26 | } 27 | 28 | private static MetadataReference GetSystemCore() 29 | { 30 | var assemblyPath = typeof(Enumerable).Assembly.Location; 31 | return MetadataReference.CreateFromFile(assemblyPath); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer.Test/OnGUI/DoNotUseOnGUIAnalyzerTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.Diagnostics; 3 | using Microsoft.CodeAnalysis.Text; 4 | using NUnit.Framework; 5 | using RoslynNUnitLight; 6 | using UnityEngineAnalyzer.OnGUI; 7 | 8 | namespace UnityEngineAnalyzer.Test.OnGUI 9 | { 10 | [TestFixture] 11 | sealed class DoNotUseOnGUIAnalyzerTests : AnalyzerTestFixture 12 | { 13 | protected override string LanguageName => LanguageNames.CSharp; 14 | protected override DiagnosticAnalyzer CreateAnalyzer() => new DoNotUseOnGUIAnalyzer(); 15 | 16 | [Test] 17 | public void OnGUIUsedInMonoBehaviour() 18 | { 19 | const string code = @" 20 | using UnityEngine; 21 | 22 | class C : MonoBehaviour 23 | { 24 | void [|OnGUI|]() { } 25 | }"; 26 | 27 | Document document; 28 | TextSpan span; 29 | TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); 30 | 31 | HasDiagnostic(document, span, DiagnosticIDs.DoNotUseOnGUI); 32 | } 33 | 34 | [Test] 35 | public void OnGUIUsedInClass() 36 | { 37 | const string code = @" 38 | class C 39 | { 40 | void [|OnGUI|]() { } 41 | }"; 42 | 43 | NoDiagnostic(code, DiagnosticIDs.DoNotUseOnGUI); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("UnityEngineAnalyzer.Test")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("UnityEngineAnalyzer.Test")] 12 | [assembly: AssemblyCopyright("Copyright © Meng Hui Koh 2016")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // Version information for an assembly consists of the following four values: 22 | // 23 | // Major Version 24 | // Minor Version 25 | // Build Number 26 | // Revision 27 | // 28 | // You can specify all the values or you can default the Build and Revision Numbers 29 | // by using the '*' as shown below: 30 | // [assembly: AssemblyVersion("1.0.*")] 31 | [assembly: AssemblyVersion("1.0.0.0")] 32 | [assembly: AssemblyFileVersion("1.0.0.0")] -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer.Test/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer.Vsix/LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Meng Hui Koh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer.Vsix/UnityEngineAnalyzer.Vsix.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 14.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | Debug 10 | AnyCPU 11 | 2.0 12 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 13 | {23A41489-857C-4FC7-9B96-2F20D8EE380B} 14 | Library 15 | Properties 16 | UnityEngineAnalyzer.Vsix 17 | UnityEngineAnalyzer.Vsix 18 | v4.5.2 19 | false 20 | false 21 | false 22 | false 23 | false 24 | false 25 | Roslyn 26 | 27 | 28 | true 29 | full 30 | false 31 | bin\Debug\ 32 | DEBUG;TRACE 33 | prompt 34 | 4 35 | 36 | 37 | pdbonly 38 | true 39 | bin\Release\ 40 | TRACE 41 | prompt 42 | 4 43 | 44 | 45 | Program 46 | $(DevEnvDir)devenv.exe 47 | /rootsuffix Roslyn 48 | 49 | 50 | 51 | Designer 52 | 53 | 54 | 55 | 56 | {BFB2BB34-FED8-48CC-9B83-A6E38BA5666C} 57 | UnityEngineAnalyzer 58 | 59 | 60 | 61 | 62 | Always 63 | true 64 | 65 | 66 | 67 | 68 | 75 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer.Vsix/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | UnityEngineAnalyzer 6 | UnityEngineAnalyzer aims to provide Roslyn diagnostics for common problems and performance issues in Unity3D code. 7 | LICENSE.txt 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/AOT/DoNotUseReflectionEmitAnalyzer.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp; 3 | using Microsoft.CodeAnalysis.CSharp.Syntax; 4 | using Microsoft.CodeAnalysis.Diagnostics; 5 | using System.Collections.Immutable; 6 | 7 | namespace UnityEngineAnalyzer.AOT 8 | { 9 | [DiagnosticAnalyzer(LanguageNames.CSharp)] 10 | public sealed class DoNotUseReflectionEmitAnalyzer : DiagnosticAnalyzer 11 | { 12 | public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.DoNotUseReflectionEmit); 13 | 14 | public override void Initialize(AnalysisContext context) 15 | { 16 | context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.UsingDirective); 17 | } 18 | 19 | private static void AnalyzeNode(SyntaxNodeAnalysisContext context) 20 | { 21 | // retrieve using syntax node 22 | var syntax = context.Node as UsingDirectiveSyntax; 23 | // and check if it is System.Reflection.Emit 24 | if (syntax.Name.ToString().Equals("System.Reflection.Emit")) 25 | { 26 | var diagnostic = Diagnostic.Create(DiagnosticDescriptors.DoNotUseReflectionEmit, syntax.GetLocation()); 27 | context.ReportDiagnostic(diagnostic); 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/AOT/DoNotUseReflectionEmitResources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace UnityEngineAnalyzer.AOT { 12 | using System; 13 | using System.Reflection; 14 | 15 | 16 | /// 17 | /// A strongly-typed resource class, for looking up localized strings, etc. 18 | /// 19 | // This class was auto-generated by the StronglyTypedResourceBuilder 20 | // class via a tool like ResGen or Visual Studio. 21 | // To add or remove a member, edit your .ResX file then rerun ResGen 22 | // with the /str option, or rebuild your VS project. 23 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 24 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 25 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 26 | internal class DoNotUseReflectionEmitResources { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal DoNotUseReflectionEmitResources() { 34 | } 35 | 36 | /// 37 | /// Returns the cached ResourceManager instance used by this class. 38 | /// 39 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 40 | internal static global::System.Resources.ResourceManager ResourceManager { 41 | get { 42 | if (object.ReferenceEquals(resourceMan, null)) { 43 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityEngineAnalyzer.AOT.DoNotUseReflectionEmitResources", typeof(DoNotUseReflectionEmitResources).GetTypeInfo().Assembly); 44 | resourceMan = temp; 45 | } 46 | return resourceMan; 47 | } 48 | } 49 | 50 | /// 51 | /// Overrides the current thread's CurrentUICulture property for all 52 | /// resource lookups using this strongly typed resource class. 53 | /// 54 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 55 | internal static global::System.Globalization.CultureInfo Culture { 56 | get { 57 | return resourceCulture; 58 | } 59 | set { 60 | resourceCulture = value; 61 | } 62 | } 63 | 64 | /// 65 | /// Looks up a localized string similar to AOT Limitation: System.Reflection.Emit is not supported.. 66 | /// 67 | internal static string Description { 68 | get { 69 | return ResourceManager.GetString("Description", resourceCulture); 70 | } 71 | } 72 | 73 | /// 74 | /// Looks up a localized string similar to AOT Limitation: System.Reflection.Emit is not supported.. 75 | /// 76 | internal static string MessageFormat { 77 | get { 78 | return ResourceManager.GetString("MessageFormat", resourceCulture); 79 | } 80 | } 81 | 82 | /// 83 | /// Looks up a localized string similar to AOT Limitation: System.Reflection.Emit is not supported.. 84 | /// 85 | internal static string Title { 86 | get { 87 | return ResourceManager.GetString("Title", resourceCulture); 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/AOT/DoNotUseReflectionEmitResources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | AOT Limitation: System.Reflection.Emit is not supported. 122 | An optional longer localizable description of the diagnostic. 123 | 124 | 125 | AOT Limitation: System.Reflection.Emit is not supported. 126 | The format-able message the diagnostic displays. 127 | 128 | 129 | AOT Limitation: System.Reflection.Emit is not supported. 130 | The title of the diagnostic. 131 | 132 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/AOT/DoNotUseRemotingAnalyzer.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp; 3 | using Microsoft.CodeAnalysis.CSharp.Syntax; 4 | using Microsoft.CodeAnalysis.Diagnostics; 5 | using System.Collections.Immutable; 6 | 7 | namespace UnityEngineAnalyzer.AOT 8 | { 9 | [DiagnosticAnalyzer(LanguageNames.CSharp)] 10 | public sealed class DoNotUseRemotingAnalyzer : DiagnosticAnalyzer 11 | { 12 | public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.DoNotUseRemoting); 13 | 14 | public override void Initialize(AnalysisContext context) 15 | { 16 | context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.UsingDirective); 17 | } 18 | 19 | private static void AnalyzeNode(SyntaxNodeAnalysisContext context) 20 | { 21 | // retrieve using syntax node 22 | var syntax = context.Node as UsingDirectiveSyntax; 23 | // and check if it is System.Runtime.Remoting 24 | if (syntax.Name.ToString().Equals("System.Runtime.Remoting")) 25 | { 26 | var diagnostic = Diagnostic.Create(DiagnosticDescriptors.DoNotUseRemoting, syntax.GetLocation()); 27 | context.ReportDiagnostic(diagnostic); 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/AOT/DoNotUseRemotingResources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace UnityEngineAnalyzer.AOT { 12 | using System; 13 | using System.Reflection; 14 | 15 | 16 | /// 17 | /// A strongly-typed resource class, for looking up localized strings, etc. 18 | /// 19 | // This class was auto-generated by the StronglyTypedResourceBuilder 20 | // class via a tool like ResGen or Visual Studio. 21 | // To add or remove a member, edit your .ResX file then rerun ResGen 22 | // with the /str option, or rebuild your VS project. 23 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 24 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 25 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 26 | internal class DoNotUseRemotingResources { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal DoNotUseRemotingResources() { 34 | } 35 | 36 | /// 37 | /// Returns the cached ResourceManager instance used by this class. 38 | /// 39 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 40 | internal static global::System.Resources.ResourceManager ResourceManager { 41 | get { 42 | if (object.ReferenceEquals(resourceMan, null)) { 43 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityEngineAnalyzer.AOT.DoNotUseRemotingResources", typeof(DoNotUseRemotingResources).GetTypeInfo().Assembly); 44 | resourceMan = temp; 45 | } 46 | return resourceMan; 47 | } 48 | } 49 | 50 | /// 51 | /// Overrides the current thread's CurrentUICulture property for all 52 | /// resource lookups using this strongly typed resource class. 53 | /// 54 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 55 | internal static global::System.Globalization.CultureInfo Culture { 56 | get { 57 | return resourceCulture; 58 | } 59 | set { 60 | resourceCulture = value; 61 | } 62 | } 63 | 64 | /// 65 | /// Looks up a localized string similar to AOT Limitation: System.Runtime.Remoting is not supported. . 66 | /// 67 | internal static string Description { 68 | get { 69 | return ResourceManager.GetString("Description", resourceCulture); 70 | } 71 | } 72 | 73 | /// 74 | /// Looks up a localized string similar to AOT Limitation: System.Runtime.Remoting is not supported. 75 | /// 76 | internal static string MessageFormat { 77 | get { 78 | return ResourceManager.GetString("MessageFormat", resourceCulture); 79 | } 80 | } 81 | 82 | /// 83 | /// Looks up a localized string similar to AOT Limitation: System.Runtime.Remoting is not supported. 84 | /// 85 | internal static string Title { 86 | get { 87 | return ResourceManager.GetString("Title", resourceCulture); 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/AOT/DoNotUseRemotingResources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | AOT Limitation: System.Runtime.Remoting is not supported. 122 | An optional longer localizable description of the diagnostic. 123 | 124 | 125 | AOT Limitation: System.Runtime.Remoting is not supported 126 | The format-able message the diagnostic displays. 127 | 128 | 129 | AOT Limitation: System.Runtime.Remoting is not supported 130 | The title of the diagnostic. 131 | 132 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/AOT/TypeGetTypeAnalyzer.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp; 3 | using Microsoft.CodeAnalysis.CSharp.Syntax; 4 | using Microsoft.CodeAnalysis.Diagnostics; 5 | using System.Collections.Immutable; 6 | 7 | namespace UnityEngineAnalyzer.AOT 8 | { 9 | [DiagnosticAnalyzer(LanguageNames.CSharp)] 10 | public sealed class TypeGetTypeAnalyzer : DiagnosticAnalyzer 11 | { 12 | public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.TypeGetType); 13 | public override void Initialize(AnalysisContext context) 14 | { 15 | context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.InvocationExpression); 16 | } 17 | 18 | private static void AnalyzeNode(SyntaxNodeAnalysisContext context) 19 | { 20 | var invocationExpression = context.Node as InvocationExpressionSyntax; 21 | var methodSymbol = context.SemanticModel.GetSymbolInfo(invocationExpression).Symbol as IMethodSymbol; 22 | if (methodSymbol == null) { return; } 23 | 24 | if (methodSymbol.Name.Equals("GetType") && 25 | methodSymbol.ContainingSymbol.ToString().Equals("System.Type")) 26 | { 27 | var diagnostic = Diagnostic.Create(DiagnosticDescriptors.TypeGetType, invocationExpression.GetLocation()); 28 | context.ReportDiagnostic(diagnostic); 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/AOT/TypeGetTypeResources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace UnityEngineAnalyzer.AOT { 12 | using System; 13 | using System.Reflection; 14 | 15 | 16 | /// 17 | /// A strongly-typed resource class, for looking up localized strings, etc. 18 | /// 19 | // This class was auto-generated by the StronglyTypedResourceBuilder 20 | // class via a tool like ResGen or Visual Studio. 21 | // To add or remove a member, edit your .ResX file then rerun ResGen 22 | // with the /str option, or rebuild your VS project. 23 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 24 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 25 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 26 | internal class TypeGetTypeResources { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal TypeGetTypeResources() { 34 | } 35 | 36 | /// 37 | /// Returns the cached ResourceManager instance used by this class. 38 | /// 39 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 40 | internal static global::System.Resources.ResourceManager ResourceManager { 41 | get { 42 | if (object.ReferenceEquals(resourceMan, null)) { 43 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityEngineAnalyzer.AOT.TypeGetTypeResources", typeof(TypeGetTypeResources).GetTypeInfo().Assembly); 44 | resourceMan = temp; 45 | } 46 | return resourceMan; 47 | } 48 | } 49 | 50 | /// 51 | /// Overrides the current thread's CurrentUICulture property for all 52 | /// resource lookups using this strongly typed resource class. 53 | /// 54 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 55 | internal static global::System.Globalization.CultureInfo Culture { 56 | get { 57 | return resourceCulture; 58 | } 59 | set { 60 | resourceCulture = value; 61 | } 62 | } 63 | 64 | /// 65 | /// Looks up a localized string similar to AOT Limitation: Only works for looking up existing types.. 66 | /// 67 | internal static string Description { 68 | get { 69 | return ResourceManager.GetString("Description", resourceCulture); 70 | } 71 | } 72 | 73 | /// 74 | /// Looks up a localized string similar to AOT Limitation: Only works for looking up existing types.. 75 | /// 76 | internal static string MessageFormat { 77 | get { 78 | return ResourceManager.GetString("MessageFormat", resourceCulture); 79 | } 80 | } 81 | 82 | /// 83 | /// Looks up a localized string similar to AOT Limitation: Only works for looking up existing types.. 84 | /// 85 | internal static string Title { 86 | get { 87 | return ResourceManager.GetString("Title", resourceCulture); 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/AOT/TypeGetTypeResources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | AOT Limitation: Only works for looking up existing types. 122 | An optional longer localizable description of the diagnostic. 123 | 124 | 125 | AOT Limitation: Only works for looking up existing types. 126 | The format-able message the diagnostic displays. 127 | 128 | 129 | AOT Limitation: Only works for looking up existing types. 130 | The title of the diagnostic. 131 | 132 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/CompareTag/UseCompareTagAnalyzer.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp; 3 | using Microsoft.CodeAnalysis.CSharp.Syntax; 4 | using Microsoft.CodeAnalysis.Diagnostics; 5 | using System.Collections.Immutable; 6 | 7 | namespace UnityEngineAnalyzer.CompareTag 8 | { 9 | //TODO: Create a CodeFix provider for this 10 | 11 | [DiagnosticAnalyzer(LanguageNames.CSharp)] 12 | public sealed class UseCompareTagAnalyzer : DiagnosticAnalyzer 13 | { 14 | private static readonly ImmutableHashSet ContainingSymbols = ImmutableHashSet.Create("UnityEngine.Component", "UnityEngine.GameObject"); 15 | 16 | public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.UseCompareTag); 17 | public override void Initialize(AnalysisContext context) 18 | { 19 | context.RegisterSyntaxNodeAction(AnalyzeInvocationExpressionNode, SyntaxKind.InvocationExpression); 20 | context.RegisterSyntaxNodeAction(AnalyzeBinaryExpressionNode, SyntaxKind.EqualsExpression, SyntaxKind.NotEqualsExpression); 21 | } 22 | 23 | private static void AnalyzeInvocationExpressionNode(SyntaxNodeAnalysisContext context) 24 | { 25 | // look for an InvocationExpressionSyntax of the form tag.Equals("") or "".Equals(tag) 26 | var invocationExpression = context.Node as InvocationExpressionSyntax; 27 | // check that number of arguments is one 28 | if (invocationExpression.ArgumentList?.Arguments.Count != 1) { return; } 29 | // retrieve the MemberAccessExpression and check that is it "Equals", check that number of arguments is one 30 | var equalsMemberAccessExpression = invocationExpression.Expression as MemberAccessExpressionSyntax; 31 | if (!(equalsMemberAccessExpression?.Name.Identifier.Text.Equals("Equals") ?? false)) { return; } 32 | 33 | // at this point we have an .Equals member access with one argument 34 | // check on both sides if there is a tag member access 35 | if (ProcessPotentialTagMemberAccessExpression(context, equalsMemberAccessExpression.Expression) || 36 | ProcessPotentialTagMemberAccessExpression(context, invocationExpression.ArgumentList.Arguments[0].Expression)) 37 | { 38 | ReportDiagnostic(context, invocationExpression.GetLocation()); 39 | } 40 | } 41 | 42 | private static void AnalyzeBinaryExpressionNode(SyntaxNodeAnalysisContext context) 43 | { 44 | // look for tag == "" or tag != "" 45 | var binaryExpression = context.Node as BinaryExpressionSyntax; 46 | if (ProcessPotentialTagMemberAccessExpression(context, binaryExpression.Left) || 47 | ProcessPotentialTagMemberAccessExpression(context, binaryExpression.Right)) 48 | { 49 | ReportDiagnostic(context, binaryExpression.GetLocation()); 50 | } 51 | } 52 | 53 | private static bool ProcessPotentialTagMemberAccessExpression(SyntaxNodeAnalysisContext context, ExpressionSyntax expression) 54 | { 55 | // check for member access or identifier access 56 | if (expression is MemberAccessExpressionSyntax || expression is IdentifierNameSyntax) 57 | { 58 | // check for property access 59 | var propertySymbol = context.SemanticModel.GetSymbolInfo(expression).Symbol as IPropertySymbol; 60 | // check that property accessed is tag and belongs to UnityEngine 61 | if (propertySymbol?.Name.Equals("tag") ?? false && //TODO: Either fix this statement or remove this check 62 | ContainingSymbols.Contains(propertySymbol.ContainingSymbol.ToString())) 63 | { return true; } 64 | } 65 | 66 | return false; 67 | } 68 | 69 | private static void ReportDiagnostic(SyntaxNodeAnalysisContext context, Location location) 70 | { 71 | var diagnostic = Diagnostic.Create(DiagnosticDescriptors.UseCompareTag, location); 72 | context.ReportDiagnostic(diagnostic); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/CompareTag/UseCompareTagResources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace UnityEngineAnalyzer.CompareTag { 12 | using System; 13 | using System.Reflection; 14 | 15 | 16 | /// 17 | /// A strongly-typed resource class, for looking up localized strings, etc. 18 | /// 19 | // This class was auto-generated by the StronglyTypedResourceBuilder 20 | // class via a tool like ResGen or Visual Studio. 21 | // To add or remove a member, edit your .ResX file then rerun ResGen 22 | // with the /str option, or rebuild your VS project. 23 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 24 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 25 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 26 | internal class UseCompareTagResources { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal UseCompareTagResources() { 34 | } 35 | 36 | /// 37 | /// Returns the cached ResourceManager instance used by this class. 38 | /// 39 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 40 | internal static global::System.Resources.ResourceManager ResourceManager { 41 | get { 42 | if (object.ReferenceEquals(resourceMan, null)) { 43 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityEngineAnalyzer.CompareTag.UseCompareTagResources", typeof(UseCompareTagResources).GetTypeInfo().Assembly); 44 | resourceMan = temp; 45 | } 46 | return resourceMan; 47 | } 48 | } 49 | 50 | /// 51 | /// Overrides the current thread's CurrentUICulture property for all 52 | /// resource lookups using this strongly typed resource class. 53 | /// 54 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 55 | internal static global::System.Globalization.CultureInfo Culture { 56 | get { 57 | return resourceCulture; 58 | } 59 | set { 60 | resourceCulture = value; 61 | } 62 | } 63 | 64 | /// 65 | /// Looks up a localized string similar to Using CompareTag for tag comparison does not cause allocations.. 66 | /// 67 | internal static string Description { 68 | get { 69 | return ResourceManager.GetString("Description", resourceCulture); 70 | } 71 | } 72 | 73 | /// 74 | /// Looks up a localized string similar to Using CompareTag for tag comparison does not cause allocations.. 75 | /// 76 | internal static string MessageFormat { 77 | get { 78 | return ResourceManager.GetString("MessageFormat", resourceCulture); 79 | } 80 | } 81 | 82 | /// 83 | /// Looks up a localized string similar to Use CompareTag.. 84 | /// 85 | internal static string Title { 86 | get { 87 | return ResourceManager.GetString("Title", resourceCulture); 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/CompareTag/UseCompareTagResources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | Comparing a Tag using Equals operator can cause unecessary memory allocation. Use the CompareTag function Instead. 122 | An optional longer localizable description of the diagnostic. 123 | 124 | 125 | Use CompareTag instead of using the Equals operator 126 | The format-able message the diagnostic displays. 127 | 128 | 129 | Use CompareTag 130 | The title of the diagnostic. 131 | 132 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/Coroutines/DoNotUseCoroutinesAnalyzer.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp; 3 | using Microsoft.CodeAnalysis.CSharp.Syntax; 4 | using Microsoft.CodeAnalysis.Diagnostics; 5 | using System.Collections.Immutable; 6 | 7 | namespace UnityEngineAnalyzer.Coroutines 8 | { 9 | [DiagnosticAnalyzer(LanguageNames.CSharp)] 10 | public sealed class DoNotUseCoroutinesAnalyzer : DiagnosticAnalyzer 11 | { 12 | public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.DoNotUseCoroutines); 13 | public override void Initialize(AnalysisContext context) 14 | { 15 | context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.InvocationExpression); 16 | } 17 | 18 | private static void AnalyzeNode(SyntaxNodeAnalysisContext context) 19 | { 20 | // check if we have a method invocation 21 | var invocationExpression = context.Node as InvocationExpressionSyntax; 22 | var methodSymbol = context.SemanticModel.GetSymbolInfo(invocationExpression).Symbol as IMethodSymbol; 23 | if (methodSymbol == null) { return; } 24 | 25 | // check that the method is StartCoroutine from UnityEngine 26 | if (methodSymbol.Name.Equals("StartCoroutine") && 27 | methodSymbol.ContainingSymbol.ToString().Equals("UnityEngine.MonoBehaviour")) 28 | { 29 | var diagnostic = Diagnostic.Create(DiagnosticDescriptors.DoNotUseCoroutines, invocationExpression.GetLocation()); 30 | context.ReportDiagnostic(diagnostic); 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/Coroutines/DoNotUseCoroutinesResources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace UnityEngineAnalyzer.Coroutines { 12 | using System; 13 | using System.Reflection; 14 | 15 | 16 | /// 17 | /// A strongly-typed resource class, for looking up localized strings, etc. 18 | /// 19 | // This class was auto-generated by the StronglyTypedResourceBuilder 20 | // class via a tool like ResGen or Visual Studio. 21 | // To add or remove a member, edit your .ResX file then rerun ResGen 22 | // with the /str option, or rebuild your VS project. 23 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 24 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 25 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 26 | internal class DoNotUseCoroutinesResources { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal DoNotUseCoroutinesResources() { 34 | } 35 | 36 | /// 37 | /// Returns the cached ResourceManager instance used by this class. 38 | /// 39 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 40 | internal static global::System.Resources.ResourceManager ResourceManager { 41 | get { 42 | if (object.ReferenceEquals(resourceMan, null)) { 43 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityEngineAnalyzer.Coroutines.DoNotUseCoroutinesResources", typeof(DoNotUseCoroutinesResources).GetTypeInfo().Assembly); 44 | resourceMan = temp; 45 | } 46 | return resourceMan; 47 | } 48 | } 49 | 50 | /// 51 | /// Overrides the current thread's CurrentUICulture property for all 52 | /// resource lookups using this strongly typed resource class. 53 | /// 54 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 55 | internal static global::System.Globalization.CultureInfo Culture { 56 | get { 57 | return resourceCulture; 58 | } 59 | set { 60 | resourceCulture = value; 61 | } 62 | } 63 | 64 | /// 65 | /// Looks up a localized string similar to Use of coroutines is known to cause some allocations.. 66 | /// 67 | internal static string Description { 68 | get { 69 | return ResourceManager.GetString("Description", resourceCulture); 70 | } 71 | } 72 | 73 | /// 74 | /// Looks up a localized string similar to Use of coroutines is known to cause some allocations.. 75 | /// 76 | internal static string MessageFormat { 77 | get { 78 | return ResourceManager.GetString("MessageFormat", resourceCulture); 79 | } 80 | } 81 | 82 | /// 83 | /// Looks up a localized string similar to Use of coroutines is known to cause some allocations.. 84 | /// 85 | internal static string Title { 86 | get { 87 | return ResourceManager.GetString("Title", resourceCulture); 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/Coroutines/DoNotUseCoroutinesResources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | Use of coroutines is known to cause some allocations. 122 | An optional longer localizable description of the diagnostic. 123 | 124 | 125 | Use of coroutines is known to cause some allocations. 126 | The format-able message the diagnostic displays. 127 | 128 | 129 | Use of coroutines is known to cause some allocations. 130 | The title of the diagnostic. 131 | 132 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/Diagnostic.nuspec: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | UnityEngineAnalyzer 5 | 1.0.0.0 6 | UnityEngineAnalyzer 7 | Meng Hui Koh 8 | Meng Hui Koh 9 | https://raw.githubusercontent.com/meng-hui/UnityEngineAnalyzer/master/LICENSE 10 | https://github.com/meng-hui/UnityEngineAnalyzer 11 | false 12 | 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. 13 | Initial Release. 14 | Copyright © Meng Hui Koh 2016 15 | UnityEngineAnalyzer, analyzers, UnityEngine, Roslyn 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticCategories.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace UnityEngineAnalyzer 3 | { 4 | static class DiagnosticCategories 5 | { 6 | public const string GC = "GC"; 7 | public const string StringMethods = "String Methods"; 8 | public const string Miscellaneous = "Miscellaneous"; 9 | public const string Performance = "Performance"; 10 | 11 | public const string AOT = "AOT"; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace UnityEngineAnalyzer 3 | { 4 | public static class DiagnosticIDs 5 | { 6 | public const string DoNotUseOnGUI = "UEA0001"; 7 | public const string DoNotUseStringMethods = "UEA0002"; 8 | public const string EmptyMonoBehaviourMethod = "UEA0003"; 9 | public const string UseCompareTag = "UEA0004"; 10 | public const string DoNotUseFindMethodsInUpdate = "UEA0005"; 11 | public const string DoNotUseCoroutines = "UEA0006"; 12 | public const string DoNotUseForEachInUpdate = "UEA0007"; 13 | 14 | //NOTES: These should probably be on their own analyzer - as they are not specific to Unity 15 | public const string DoNotUseRemoting = "AOT0001"; 16 | public const string DoNotUseReflectionEmit = "AOT0002"; 17 | public const string TypeGetType = "AOT0003"; 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/EmptyMonoBehaviourMethods/EmptyMonoBehaviourMethodsAnalyzer.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp; 3 | using Microsoft.CodeAnalysis.CSharp.Syntax; 4 | using Microsoft.CodeAnalysis.Diagnostics; 5 | using System.Collections.Immutable; 6 | 7 | namespace UnityEngineAnalyzer.EmptyMonoBehaviourMethods 8 | { 9 | [DiagnosticAnalyzer(LanguageNames.CSharp)] 10 | public sealed class EmptyMonoBehaviourMethodsAnalyzer : DiagnosticAnalyzer 11 | { 12 | private static readonly ImmutableHashSet MonoBehaviourMethods = ImmutableHashSet.Create( 13 | "Awake", 14 | "FixedUpdate", 15 | "LateUpdate", 16 | "OnAnimatorIK", 17 | "OnAnimatorMove", 18 | "OnApplicationFocus", 19 | "OnApplicationPause", 20 | "OnApplicationQuit", 21 | "OnAudioFilterRead", 22 | "OnBecameInvisible", 23 | "OnBecameVisible", 24 | "OnCollisionEnter", 25 | "OnCollisionEnter2D", 26 | "OnCollisionExit", 27 | "OnCollisionExit2D", 28 | "OnCollisionStay", 29 | "OnCollisionStay2D", 30 | "OnConnectedToServer", 31 | "OnControllerColliderHit", 32 | "OnDestroy", 33 | "OnDisable", 34 | "OnDisconnectedFromServer", 35 | "OnDrawGizmos", 36 | "OnDrawGizmosSelected", 37 | "OnEnable", 38 | "OnFailedToConnect", 39 | "OnFailedToConnectToMasterServer", 40 | "OnGUI", 41 | "OnJointBreak", 42 | "OnLevelWasLoaded", 43 | "OnMasterServerEvent", 44 | "OnMouseDown", 45 | "OnMouseDrag", 46 | "OnMouseEnter", 47 | "OnMouseExit", 48 | "OnMouseOver", 49 | "OnMouseUp", 50 | "OnMouseUpAsButton", 51 | "OnNetworkInstantiate", 52 | "OnParticleCollision", 53 | "OnPlayerConnected", 54 | "OnPlayerDisconnected", 55 | "OnPostRender", 56 | "OnPreCull", 57 | "OnPreRender", 58 | "OnRenderImage", 59 | "OnRenderObject", 60 | "OnSerializeNetworkView", 61 | "OnServerInitialized", 62 | "OnTransformChildrenChanged", 63 | "OnTransformParentChanged", 64 | "OnTriggerEnter", 65 | "OnTriggerEnter2D", 66 | "OnTriggerExit", 67 | "OnTriggerExit2D", 68 | "OnTriggerStay", 69 | "OnTriggerStay2D", 70 | "OnValidate", 71 | "OnWillRenderObject", 72 | "Reset", 73 | "Start", 74 | "Update"); 75 | 76 | public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.EmptyMonoBehaviourMethod); 77 | 78 | public override void Initialize(AnalysisContext context) 79 | { 80 | context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.Method); 81 | //NOTE: It might be more officient to find classes and then determine if they are a MonoBehaviour rather than look at every method 82 | } 83 | 84 | private static async void AnalyzeSymbol(SymbolAnalysisContext context) 85 | { 86 | // retrieve method symbol 87 | var methodSymbol = context.Symbol as IMethodSymbol; 88 | // check if method name is a MonoBehaviour method name 89 | if (!MonoBehaviourMethods.Contains(methodSymbol.Name)) { return; } 90 | // check the syntax that has this method 91 | if (methodSymbol.DeclaringSyntaxReferences.Length != 1) { return; } 92 | 93 | // retrieve the method syntax from the method symbol 94 | var methodSyntax = await methodSymbol.DeclaringSyntaxReferences[0].GetSyntaxAsync() as MethodDeclarationSyntax; 95 | // from the method syntax, check if there is a body and if there are statements in it 96 | if (methodSyntax?.Body?.Statements.Any() ?? true) { return; } 97 | 98 | // at this point, we have a method with a MonoBehaviour method name and an empty body 99 | // finally, check if this method is contained in a class which extends UnityEngine.MonoBehaviour 100 | var containingClass = methodSymbol.ContainingType; 101 | var baseClass = containingClass.BaseType; 102 | if (baseClass.ContainingNamespace.Name.Equals("UnityEngine") && 103 | baseClass.Name.Equals("MonoBehaviour")) 104 | { 105 | var diagnostic = Diagnostic.Create(DiagnosticDescriptors.EmptyMonoBehaviourMethod, methodSyntax.GetLocation(), containingClass.Name, methodSymbol.Name); 106 | context.ReportDiagnostic(diagnostic); 107 | } 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/EmptyMonoBehaviourMethods/EmptyMonoBehaviourMethodsResources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace UnityEngineAnalyzer.EmptyMonoBehaviourMethods { 12 | using System; 13 | using System.Reflection; 14 | 15 | 16 | /// 17 | /// A strongly-typed resource class, for looking up localized strings, etc. 18 | /// 19 | // This class was auto-generated by the StronglyTypedResourceBuilder 20 | // class via a tool like ResGen or Visual Studio. 21 | // To add or remove a member, edit your .ResX file then rerun ResGen 22 | // with the /str option, or rebuild your VS project. 23 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 24 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 25 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 26 | internal class EmptyMonoBehaviourMethodsResources { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal EmptyMonoBehaviourMethodsResources() { 34 | } 35 | 36 | /// 37 | /// Returns the cached ResourceManager instance used by this class. 38 | /// 39 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 40 | internal static global::System.Resources.ResourceManager ResourceManager { 41 | get { 42 | if (object.ReferenceEquals(resourceMan, null)) { 43 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityEngineAnalyzer.EmptyMonoBehaviourMethods.EmptyMonoBehaviourMethodsResources", typeof(EmptyMonoBehaviourMethodsResources).GetTypeInfo().Assembly); 44 | resourceMan = temp; 45 | } 46 | return resourceMan; 47 | } 48 | } 49 | 50 | /// 51 | /// Overrides the current thread's CurrentUICulture property for all 52 | /// resource lookups using this strongly typed resource class. 53 | /// 54 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 55 | internal static global::System.Globalization.CultureInfo Culture { 56 | get { 57 | return resourceCulture; 58 | } 59 | set { 60 | resourceCulture = value; 61 | } 62 | } 63 | 64 | /// 65 | /// Looks up a localized string similar to Empty MonoBehaviour methods causes some overhead.. 66 | /// 67 | internal static string Description { 68 | get { 69 | return ResourceManager.GetString("Description", resourceCulture); 70 | } 71 | } 72 | 73 | /// 74 | /// Looks up a localized string similar to '{0}' has an empty MonoBehaviour method '{1}'. Empty MonoBehaviour methods causes some overhead.. 75 | /// 76 | internal static string MessageFormat { 77 | get { 78 | return ResourceManager.GetString("MessageFormat", resourceCulture); 79 | } 80 | } 81 | 82 | /// 83 | /// Looks up a localized string similar to Remove empty MonoBehaviour methods.. 84 | /// 85 | internal static string Title { 86 | get { 87 | return ResourceManager.GetString("Title", resourceCulture); 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/EmptyMonoBehaviourMethods/EmptyMonoBehaviourMethodsResources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | Empty MonoBehaviour methods causes some overhead. 122 | An optional longer localizable description of the diagnostic. 123 | 124 | 125 | '{0}' has an empty MonoBehaviour method '{1}'. Empty MonoBehaviour methods causes some overhead. 126 | The format-able message the diagnostic displays. 127 | 128 | 129 | Remove empty MonoBehaviour methods. 130 | The title of the diagnostic. 131 | 132 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/FindMethodsInUpdate/DoNotUseFindMethodsInUpdateAnalyzer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.CodeAnalysis; 4 | using Microsoft.CodeAnalysis.CSharp; 5 | using Microsoft.CodeAnalysis.CSharp.Syntax; 6 | using Microsoft.CodeAnalysis.Diagnostics; 7 | using System.Collections.Immutable; 8 | using System.Diagnostics; 9 | using System.Linq; 10 | 11 | namespace UnityEngineAnalyzer.FindMethodsInUpdate 12 | { 13 | [DiagnosticAnalyzer(LanguageNames.CSharp)] 14 | public sealed class DoNotUseFindMethodsInUpdateAnalyzer : DiagnosticAnalyzer 15 | { 16 | private static readonly ImmutableHashSet FindMethodNames = ImmutableHashSet.Create( 17 | "Find", 18 | "FindGameObjectsWithTag", 19 | "FindGameObjectWithTag", 20 | "FindWithTag", 21 | "FindObjectOfType", 22 | "FindObjectsOfType", 23 | "FindObjectsOfTypeAll", 24 | "FindObjectsOfTypeIncludingAssets", 25 | "FindSceneObjectsOfType", 26 | "GetComponent", 27 | "GetComponentInChildren", 28 | "GetComponentInParent", 29 | "GetComponents", 30 | "GetComponentsInChildren", 31 | "GetComponentsInParent", 32 | "FindChild"); 33 | 34 | private static readonly ImmutableHashSet ContainingSymbols = ImmutableHashSet.Create( 35 | "UnityEngine.GameObject", 36 | "UnityEngine.Object", 37 | "UnityEngine.Component", 38 | "UnityEngine.Transform"); 39 | 40 | private Dictionary _indirectCallers; 41 | 42 | 43 | public override ImmutableArray SupportedDiagnostics 44 | { 45 | get 46 | { 47 | return ImmutableArray.Create( 48 | DiagnosticDescriptors.DoNotUseFindMethodsInUpdate, 49 | DiagnosticDescriptors.DoNotUseFindMethodsInUpdateRecursive); 50 | } 51 | } 52 | 53 | public override void Initialize(AnalysisContext context) 54 | { 55 | context.RegisterSyntaxNodeAction(AnalyzeClassSyntax, SyntaxKind.ClassDeclaration); 56 | } 57 | 58 | public void AnalyzeClassSyntax(SyntaxNodeAnalysisContext context) 59 | { 60 | var monoBehaviourInfo = new MonoBehaviourInfo(context); 61 | 62 | var searched = new Dictionary(); 63 | _indirectCallers = new Dictionary(); 64 | 65 | monoBehaviourInfo.ForEachUpdateMethod((updateMethod) => 66 | { 67 | //Debug.WriteLine("Found an update call! " + updateMethod); 68 | 69 | var findCalls = SearchForFindCalls(context, updateMethod, searched, true); 70 | 71 | foreach (var findCall in findCalls) 72 | { 73 | if (!_indirectCallers.ContainsKey(findCall)) 74 | { 75 | var diagnostic = Diagnostic.Create(DiagnosticDescriptors.DoNotUseFindMethodsInUpdate, 76 | findCall.GetLocation(), findCall, monoBehaviourInfo.ClassName, updateMethod.Identifier); 77 | context.ReportDiagnostic(diagnostic); 78 | } 79 | else 80 | { 81 | var endPoint = _indirectCallers[findCall]; 82 | 83 | var diagnostic = Diagnostic.Create(DiagnosticDescriptors.DoNotUseFindMethodsInUpdateRecursive, 84 | findCall.GetLocation(), monoBehaviourInfo.ClassName, updateMethod.Identifier, findCall, endPoint); 85 | context.ReportDiagnostic(diagnostic); 86 | } 87 | 88 | } 89 | }); 90 | } 91 | 92 | //TODO: Try to simplify this method - it's very hard to follow 93 | private IEnumerable SearchForFindCalls(SyntaxNodeAnalysisContext context, 94 | MethodDeclarationSyntax method, IDictionary searched, bool isRoot) 95 | { 96 | var invocations = method.DescendantNodes().OfType(); 97 | 98 | foreach (var invocation in invocations) 99 | { 100 | SymbolInfo symbolInfo; 101 | if (!context.TryGetSymbolInfo(invocation, out symbolInfo)) 102 | { 103 | continue; 104 | } 105 | 106 | var methodSymbol = symbolInfo.Symbol as IMethodSymbol; 107 | 108 | if (methodSymbol != null) 109 | { 110 | if (searched.ContainsKey(methodSymbol)) 111 | { 112 | if (searched[methodSymbol]) 113 | { 114 | yield return invocation; 115 | } 116 | } 117 | else 118 | { 119 | if (FindMethodNames.Contains(methodSymbol.Name) && 120 | ContainingSymbols.Contains(methodSymbol.ContainingSymbol.ToString())) 121 | { 122 | searched.Add(methodSymbol, true); 123 | yield return invocation; 124 | } 125 | else 126 | { 127 | var methodDeclarations = methodSymbol.DeclaringSyntaxReferences; 128 | 129 | searched.Add(methodSymbol, false); //let's assume there won't be any calls 130 | 131 | foreach (var methodDeclaration in methodDeclarations) 132 | { 133 | var theMethodSyntax = methodDeclaration.GetSyntax() as MethodDeclarationSyntax; 134 | 135 | if (theMethodSyntax != null) 136 | { 137 | var childFindCallers = SearchForFindCalls(context, theMethodSyntax, searched, false); 138 | 139 | if (childFindCallers != null && childFindCallers.Any()) 140 | { 141 | searched[methodSymbol] = true; //update the searched dictionary with new info 142 | 143 | if (isRoot) 144 | { 145 | _indirectCallers.Add(invocation, childFindCallers.First()); 146 | } 147 | 148 | yield return invocation; 149 | break; 150 | } 151 | } 152 | } 153 | } 154 | } 155 | } 156 | } 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/FindMethodsInUpdate/DoNotUseFindMethodsInUpdateResources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace UnityEngineAnalyzer.FindMethodsInUpdate { 12 | using System; 13 | using System.Reflection; 14 | 15 | 16 | /// 17 | /// A strongly-typed resource class, for looking up localized strings, etc. 18 | /// 19 | // This class was auto-generated by the StronglyTypedResourceBuilder 20 | // class via a tool like ResGen or Visual Studio. 21 | // To add or remove a member, edit your .ResX file then rerun ResGen 22 | // with the /str option, or rebuild your VS project. 23 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 24 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 25 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 26 | internal class DoNotUseFindMethodsInUpdateResources { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal DoNotUseFindMethodsInUpdateResources() { 34 | } 35 | 36 | /// 37 | /// Returns the cached ResourceManager instance used by this class. 38 | /// 39 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 40 | internal static global::System.Resources.ResourceManager ResourceManager { 41 | get { 42 | if (object.ReferenceEquals(resourceMan, null)) { 43 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityEngineAnalyzer.FindMethodsInUpdate.DoNotUseFindMethodsInUpdateResources", typeof(DoNotUseFindMethodsInUpdateResources).GetTypeInfo().Assembly); 44 | resourceMan = temp; 45 | } 46 | return resourceMan; 47 | } 48 | } 49 | 50 | /// 51 | /// Overrides the current thread's CurrentUICulture property for all 52 | /// resource lookups using this strongly typed resource class. 53 | /// 54 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 55 | internal static global::System.Globalization.CultureInfo Culture { 56 | get { 57 | return resourceCulture; 58 | } 59 | set { 60 | resourceCulture = value; 61 | } 62 | } 63 | 64 | /// 65 | /// 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. 66 | /// 67 | internal static string Description { 68 | get { 69 | return ResourceManager.GetString("Description", resourceCulture); 70 | } 71 | } 72 | 73 | /// 74 | /// 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.. 75 | /// 76 | internal static string MessageFormat { 77 | get { 78 | return ResourceManager.GetString("MessageFormat", resourceCulture); 79 | } 80 | } 81 | 82 | /// 83 | /// 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.. 84 | /// 85 | internal static string MessageFormatRecursive { 86 | get { 87 | return ResourceManager.GetString("MessageFormatRecursive", resourceCulture); 88 | } 89 | } 90 | 91 | /// 92 | /// Looks up a localized string similar to Cache the result of Find or GetComponent in Start or Awake.. 93 | /// 94 | internal static string Title { 95 | get { 96 | return ResourceManager.GetString("Title", resourceCulture); 97 | } 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/FindMethodsInUpdate/DoNotUseFindMethodsInUpdateResources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | Using Find or GetComponent in Update methods can impact performance. Cache the result on Start or Awake methods 122 | An optional longer localizable description of the diagnostic. 123 | 124 | 125 | The method {0} is called from {1}.{2} which could cause performance problems. Cache the result from {0} in Start or Awake instead. 126 | The format-able message the diagnostic displays. 127 | 128 | 129 | The method {0}.{1} calls {2} which eventually calls {3} which could impact performance. Cache the result from {3} in Start or Awake instead. 130 | 131 | 132 | Cache the result of Find or GetComponent in Start or Awake. 133 | The title of the diagnostic. 134 | 135 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/ForEachInUpdate/DoNotUseForEachInUpdate.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.Immutable; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using Microsoft.CodeAnalysis; 6 | using Microsoft.CodeAnalysis.CSharp; 7 | using Microsoft.CodeAnalysis.CSharp.Syntax; 8 | using Microsoft.CodeAnalysis.Diagnostics; 9 | 10 | namespace UnityEngineAnalyzer.ForEachInUpdate 11 | { 12 | [DiagnosticAnalyzer(LanguageNames.CSharp)] 13 | public sealed class DoNotUseForEachInUpdate : DiagnosticAnalyzer 14 | { 15 | 16 | public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.DoNotUseForEachInUpdate); 17 | public override void Initialize(AnalysisContext context) 18 | { 19 | context.RegisterSyntaxNodeAction(AnalyzeClassSyntax, SyntaxKind.ClassDeclaration); 20 | } 21 | 22 | public static void AnalyzeClassSyntax(SyntaxNodeAnalysisContext context) 23 | { 24 | var monoBehaviourInfo = new MonoBehaviourInfo(context); 25 | 26 | var searched = new Dictionary(); 27 | monoBehaviourInfo.ForEachUpdateMethod((updateMethod) => 28 | { 29 | var forEachStatements = SearchForForEach(context, updateMethod, searched); 30 | 31 | foreach (var forEachStatement in forEachStatements) 32 | { 33 | Debug.WriteLine("Found a bad call! " + forEachStatement); 34 | 35 | var location = forEachStatement.ForEachKeyword.GetLocation(); 36 | var diagnostic = Diagnostic.Create(DiagnosticDescriptors.DoNotUseForEachInUpdate, location, monoBehaviourInfo.ClassName ,updateMethod.Identifier); 37 | context.ReportDiagnostic(diagnostic); 38 | } 39 | }); 40 | } 41 | 42 | 43 | private static IEnumerable SearchForForEach(SyntaxNodeAnalysisContext context, MethodDeclarationSyntax method, IDictionary searched) 44 | { 45 | var invocations = method.DescendantNodes().OfType(); 46 | 47 | foreach (var invocation in invocations) 48 | { 49 | yield return invocation; 50 | } 51 | 52 | //TODO: Keep Searching recurively to other methods... 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/ForEachInUpdate/DoNotUseForEachInUpdateResources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace UnityEngineAnalyzer.ForEachInUpdate { 12 | using System; 13 | using System.Reflection; 14 | 15 | 16 | /// 17 | /// A strongly-typed resource class, for looking up localized strings, etc. 18 | /// 19 | // This class was auto-generated by the StronglyTypedResourceBuilder 20 | // class via a tool like ResGen or Visual Studio. 21 | // To add or remove a member, edit your .ResX file then rerun ResGen 22 | // with the /str option, or rebuild your VS project. 23 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 24 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 25 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 26 | internal class DoNotUseForEachInUpdateResources { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal DoNotUseForEachInUpdateResources() { 34 | } 35 | 36 | /// 37 | /// Returns the cached ResourceManager instance used by this class. 38 | /// 39 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 40 | internal static global::System.Resources.ResourceManager ResourceManager { 41 | get { 42 | if (object.ReferenceEquals(resourceMan, null)) { 43 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityEngineAnalyzer.ForEachInUpdate.DoNotUseForEachInUpdateResources", typeof(DoNotUseForEachInUpdateResources).GetTypeInfo().Assembly); 44 | resourceMan = temp; 45 | } 46 | return resourceMan; 47 | } 48 | } 49 | 50 | /// 51 | /// Overrides the current thread's CurrentUICulture property for all 52 | /// resource lookups using this strongly typed resource class. 53 | /// 54 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 55 | internal static global::System.Globalization.CultureInfo Culture { 56 | get { 57 | return resourceCulture; 58 | } 59 | set { 60 | resourceCulture = value; 61 | } 62 | } 63 | 64 | /// 65 | /// Looks up a localized string similar to Using ForEach in an Update could be slower and use more memory than a For loop. 66 | /// 67 | internal static string Description { 68 | get { 69 | return ResourceManager.GetString("Description", resourceCulture); 70 | } 71 | } 72 | 73 | /// 74 | /// Looks up a localized string similar to The method {0}.{1} is using a ForEach loop. 75 | /// 76 | internal static string MessageFormat { 77 | get { 78 | return ResourceManager.GetString("MessageFormat", resourceCulture); 79 | } 80 | } 81 | 82 | /// 83 | /// Looks up a localized string similar to Avoid using ForEach loops in Update methods. 84 | /// 85 | internal static string Title { 86 | get { 87 | return ResourceManager.GetString("Title", resourceCulture); 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/ForEachInUpdate/DoNotUseForEachInUpdateResources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | Using ForEach in an Update could be slower and use more memory than a For loop 122 | An optional longer localizable description of the diagnostic. 123 | 124 | 125 | The method {0}.{1} is using a ForEach loop 126 | The format-able message the diagnostic displays. 127 | 128 | 129 | Avoid using ForEach loops in Update methods 130 | The title of the diagnostic. 131 | 132 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/MonoBehaviourInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Immutable; 3 | using System.Linq; 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.CSharp.Syntax; 6 | using Microsoft.CodeAnalysis.Diagnostics; 7 | 8 | namespace UnityEngineAnalyzer 9 | { 10 | class MonoBehaviourInfo 11 | { 12 | 13 | private readonly INamedTypeSymbol _classSymbol; 14 | private readonly ClassDeclarationSyntax _classDeclaration; 15 | 16 | private static readonly ImmutableHashSet UpdateMethodNames = ImmutableHashSet.Create( 17 | "OnGUI", 18 | "Update", 19 | "FixedUpdate", 20 | "LateUpdate"); 21 | 22 | public MonoBehaviourInfo(SyntaxNodeAnalysisContext analysisContext) 23 | { 24 | _classDeclaration = analysisContext.Node as ClassDeclarationSyntax; 25 | _classSymbol = analysisContext.SemanticModel.GetDeclaredSymbol(_classDeclaration) as INamedTypeSymbol; 26 | 27 | if (_classSymbol != null) 28 | { 29 | this.ClassName = _classSymbol.Name; 30 | } 31 | } 32 | 33 | public string ClassName { get; private set; } 34 | 35 | public void ForEachUpdateMethod(Action callback) 36 | { 37 | if (this.IsMonoBehaviour()) 38 | { 39 | var methods = _classDeclaration.Members.OfType(); 40 | 41 | foreach (var method in methods) 42 | { 43 | if (UpdateMethodNames.Contains(method.Identifier.ValueText)) 44 | { 45 | callback(method); 46 | } 47 | } 48 | } 49 | } 50 | 51 | public bool IsMonoBehaviour() 52 | { 53 | return IsMonoBehavior(_classSymbol); 54 | } 55 | 56 | private static bool IsMonoBehavior(INamedTypeSymbol classDeclaration) 57 | { 58 | if (classDeclaration.BaseType == null) 59 | { 60 | return false; 61 | } 62 | 63 | var baseClass = classDeclaration.BaseType; 64 | 65 | if (baseClass.ContainingNamespace.Name.Equals("UnityEngine") && baseClass.Name.Equals("MonoBehaviour")) 66 | { 67 | return true; 68 | } 69 | 70 | return IsMonoBehavior(baseClass); //determine if the BaseClass extends mono behavior 71 | 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/OnGUI/DoNotUseOnGUIAnalyzer.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis.Diagnostics; 2 | using Microsoft.CodeAnalysis; 3 | using System.Collections.Immutable; 4 | 5 | namespace UnityEngineAnalyzer.OnGUI 6 | { 7 | [DiagnosticAnalyzer(LanguageNames.CSharp)] 8 | public sealed class DoNotUseOnGUIAnalyzer : DiagnosticAnalyzer 9 | { 10 | public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.DoNotUseOnGUI); 11 | 12 | public override void Initialize(AnalysisContext context) 13 | { 14 | context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.Method); 15 | } 16 | 17 | private static void AnalyzeSymbol(SymbolAnalysisContext context) 18 | { 19 | // check if the method is name OnGUI 20 | var methodSymbol = context.Symbol as IMethodSymbol; 21 | if (!methodSymbol.Name.Equals("OnGUI")) { return; } 22 | 23 | // check that it is contained in a class extended by UnityEngine.MonoBehaviour 24 | var containingClass = methodSymbol.ContainingType; 25 | var baseClass = containingClass.BaseType; 26 | if (baseClass.ContainingNamespace.Name.Equals("UnityEngine") && 27 | baseClass.Name.Equals("MonoBehaviour")) 28 | { 29 | var diagnostic = Diagnostic.Create(DiagnosticDescriptors.DoNotUseOnGUI, methodSymbol.Locations[0], containingClass.Name); 30 | context.ReportDiagnostic(diagnostic); 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/OnGUI/DoNotUseOnGUIResources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace UnityEngineAnalyzer.OnGUI { 12 | using System; 13 | using System.Reflection; 14 | 15 | 16 | /// 17 | /// A strongly-typed resource class, for looking up localized strings, etc. 18 | /// 19 | // This class was auto-generated by the StronglyTypedResourceBuilder 20 | // class via a tool like ResGen or Visual Studio. 21 | // To add or remove a member, edit your .ResX file then rerun ResGen 22 | // with the /str option, or rebuild your VS project. 23 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 24 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 25 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 26 | internal class DoNotUseOnGUIResources { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal DoNotUseOnGUIResources() { 34 | } 35 | 36 | /// 37 | /// Returns the cached ResourceManager instance used by this class. 38 | /// 39 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 40 | internal static global::System.Resources.ResourceManager ResourceManager { 41 | get { 42 | if (object.ReferenceEquals(resourceMan, null)) { 43 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityEngineAnalyzer.OnGUI.DoNotUseOnGUIResources", typeof(DoNotUseOnGUIResources).GetTypeInfo().Assembly); 44 | resourceMan = temp; 45 | } 46 | return resourceMan; 47 | } 48 | } 49 | 50 | /// 51 | /// Overrides the current thread's CurrentUICulture property for all 52 | /// resource lookups using this strongly typed resource class. 53 | /// 54 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 55 | internal static global::System.Globalization.CultureInfo Culture { 56 | get { 57 | return resourceCulture; 58 | } 59 | set { 60 | resourceCulture = value; 61 | } 62 | } 63 | 64 | /// 65 | /// Looks up a localized string similar to Usage of OnGUI is known to cause GC and/or performance issues.. 66 | /// 67 | internal static string Description { 68 | get { 69 | return ResourceManager.GetString("Description", resourceCulture); 70 | } 71 | } 72 | 73 | /// 74 | /// Looks up a localized string similar to '{0}' implements OnGUI. This is known to cause GC and/or performance issues.. 75 | /// 76 | internal static string MessageFormat { 77 | get { 78 | return ResourceManager.GetString("MessageFormat", resourceCulture); 79 | } 80 | } 81 | 82 | /// 83 | /// Looks up a localized string similar to Do not use OnGUI in MonoBehaviours.. 84 | /// 85 | internal static string Title { 86 | get { 87 | return ResourceManager.GetString("Title", resourceCulture); 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/OnGUI/DoNotUseOnGUIResources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | Usage of OnGUI is known to cause GC and/or performance issues. 122 | An optional longer localizable description of the diagnostic. 123 | 124 | 125 | '{0}' implements OnGUI. This is known to cause GC and/or performance issues. 126 | The format-able message the diagnostic displays. 127 | 128 | 129 | Do not use OnGUI in MonoBehaviours. 130 | The title of the diagnostic. 131 | 132 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("UnityEngineAnalyzer")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("UnityEngineAnalyzer")] 12 | [assembly: AssemblyCopyright("Copyright © Meng Hui Koh 2016")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // Version information for an assembly consists of the following four values: 22 | // 23 | // Major Version 24 | // Minor Version 25 | // Build Number 26 | // Revision 27 | // 28 | // You can specify all the values or you can default the Build and Revision Numbers 29 | // by using the '*' as shown below: 30 | [assembly: AssemblyVersion("1.0.0")] 31 | [assembly: AssemblyFileVersion("1.0.0.0")] 32 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/ReadMe.txt: -------------------------------------------------------------------------------- 1 |  2 | Building this project will produce an analyzer .dll, as well as the 3 | following two ways you may wish to package that analyzer: 4 | * A NuGet package (.nupkg file) that will add your assembly as a 5 | project-local analyzer that participates in builds. 6 | * A VSIX extension (.vsix file) that will apply your analyzer to all projects 7 | and works just in the IDE. 8 | 9 | To debug your analyzer, make sure the default project is the VSIX project and 10 | start debugging. This will deploy the analyzer as a VSIX into another instance 11 | of Visual Studio, which is useful for debugging, even if you intend to produce 12 | a NuGet package. 13 | 14 | 15 | TRYING OUT YOUR NUGET PACKAGE 16 | 17 | To try out the NuGet package: 18 | 1. Create a local NuGet feed by following the instructions here: 19 | > http://docs.nuget.org/docs/creating-packages/hosting-your-own-nuget-feeds 20 | 2. Copy the .nupkg file into that folder. 21 | 3. Open the target project in Visual Studio 2015. 22 | 4. Right-click on the project node in Solution Explorer and choose Manage 23 | NuGet Packages. 24 | 5. Select the NuGet feed you created on the left. 25 | 6. Choose your analyzer from the list and click Install. 26 | 27 | If you want to automatically deploy the .nupkg file to the local feed folder 28 | when you build this project, follow these steps: 29 | 1. Right-click on this project in Solution Explorer and choose 'Unload Project'. 30 | 2. Right-click on this project and click "Edit". 31 | 3. Scroll down to the "AfterBuild" target. 32 | 4. In the "Exec" task, change the value inside "Command" after the -OutputDirectory 33 | path to point to your local NuGet feed folder. -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/RolsynExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Microsoft.CodeAnalysis; 8 | using Microsoft.CodeAnalysis.CSharp; 9 | using Microsoft.CodeAnalysis.CSharp.Syntax; 10 | using Microsoft.CodeAnalysis.Diagnostics; 11 | 12 | namespace UnityEngineAnalyzer 13 | { 14 | public static class RolsynExtensions 15 | { 16 | public static bool TryGetSymbolInfo(this SyntaxNodeAnalysisContext context, SyntaxNode node, out SymbolInfo symbolInfo) 17 | { 18 | try 19 | { 20 | //NOTE: The Call below fixes many issues where the symbol cannot be found - but there are still cases where an argumentexception is thrown 21 | // which seems to resemble this issue: https://github.com/dotnet/roslyn/issues/11193 22 | 23 | var semanticModel = SemanticModelFor(context.SemanticModel, node); 24 | 25 | symbolInfo = semanticModel.GetSymbolInfo(node); //context.SemanticModel.GetSymbolInfo(node); 26 | return true; 27 | } 28 | catch (Exception generalException) 29 | { 30 | Debug.WriteLine("Unable to find Symbol: " + node); 31 | Debug.WriteLine(generalException); 32 | } 33 | 34 | symbolInfo = default(SymbolInfo); 35 | return false; 36 | } 37 | 38 | internal static SemanticModel SemanticModelFor(SemanticModel semanticModel, SyntaxNode expression) 39 | { 40 | if (ReferenceEquals(semanticModel.SyntaxTree, expression.SyntaxTree)) 41 | { 42 | return semanticModel; 43 | } 44 | 45 | //NOTE: there may be a performance boost if we cache some of the semantic models 46 | return semanticModel.Compilation.GetSemanticModel(expression.SyntaxTree); 47 | } 48 | 49 | public static bool IsDerived(this ClassDeclarationSyntax classDeclaration) 50 | { 51 | return (classDeclaration.BaseList != null && classDeclaration.BaseList.Types.Count > 0); 52 | } 53 | 54 | public static bool IsSealed(this ClassDeclarationSyntax classDeclaration) 55 | { 56 | return classDeclaration.Modifiers.Any(m => m.Kind() == SyntaxKind.SealedKeyword); 57 | } 58 | 59 | public static bool IsSealed(this MethodDeclarationSyntax methodDeclaration) 60 | { 61 | return methodDeclaration.Modifiers.Any(m => m.Kind() == SyntaxKind.SealedKeyword); 62 | } 63 | 64 | public static bool IsOverriden(this MethodDeclarationSyntax methodDeclaration) 65 | { 66 | return methodDeclaration.Modifiers.Any(m => m.Kind() == SyntaxKind.OverrideKeyword); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/DoNotUseStringMethodsAnalyzer.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp; 3 | using Microsoft.CodeAnalysis.CSharp.Syntax; 4 | using Microsoft.CodeAnalysis.Diagnostics; 5 | using System.Collections.Immutable; 6 | using System.Linq; 7 | 8 | namespace UnityEngineAnalyzer.StringMethods 9 | { 10 | [DiagnosticAnalyzer(LanguageNames.CSharp)] 11 | public sealed class DoNotUseStringMethodsAnalyzer : DiagnosticAnalyzer 12 | { 13 | private static readonly ImmutableHashSet StringMethods = ImmutableHashSet.Create("SendMessage", "SendMessageUpwards", "BroadcastMessage", "Invoke", "InvokeRepeating"); 14 | private static readonly ImmutableHashSet Namespaces = ImmutableHashSet.Create("UnityEngine.Component", "UnityEngine.GameObject", "UnityEngine.MonoBehaviour"); 15 | 16 | public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.DoNotUseStringMethods); 17 | 18 | public override void Initialize(AnalysisContext context) 19 | { 20 | context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.InvocationExpression); 21 | } 22 | 23 | private static void AnalyzeNode(SyntaxNodeAnalysisContext context) 24 | { 25 | var invocation = context.Node as InvocationExpressionSyntax; 26 | if (invocation == null) 27 | { 28 | return; 29 | } 30 | 31 | 32 | string name = null; 33 | if (invocation.Expression is MemberAccessExpressionSyntax) 34 | { 35 | name = ((MemberAccessExpressionSyntax)invocation.Expression).Name.Identifier.ToString(); 36 | } 37 | else if (invocation.Expression is IdentifierNameSyntax) 38 | { 39 | name = ((IdentifierNameSyntax)invocation.Expression).ToString(); 40 | } 41 | else if (invocation.Expression is GenericNameSyntax) 42 | { 43 | name = ((GenericNameSyntax)invocation.Expression).Identifier.ToString(); 44 | } 45 | 46 | 47 | // check if any of the "string" methods are used 48 | if (!StringMethods.Contains(name)) { return; } 49 | 50 | 51 | // check if the method is the one from UnityEngine 52 | var symbolInfo = context.SemanticModel.GetSymbolInfo(invocation); 53 | var methodSymbol = symbolInfo.Symbol as IMethodSymbol; 54 | 55 | if (Namespaces.Any(ns => methodSymbol?.ToString().StartsWith(ns) ?? false)) 56 | { 57 | var diagnostic = Diagnostic.Create(DiagnosticDescriptors.DoNotUseStringMethods, invocation.GetLocation()); 58 | context.ReportDiagnostic(diagnostic); 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/DoNotUseStringMethodsResources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace UnityEngineAnalyzer.StringMethods { 12 | using System; 13 | using System.Reflection; 14 | 15 | 16 | /// 17 | /// A strongly-typed resource class, for looking up localized strings, etc. 18 | /// 19 | // This class was auto-generated by the StronglyTypedResourceBuilder 20 | // class via a tool like ResGen or Visual Studio. 21 | // To add or remove a member, edit your .ResX file then rerun ResGen 22 | // with the /str option, or rebuild your VS project. 23 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 24 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 25 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 26 | internal class DoNotUseStringMethodsResources { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal DoNotUseStringMethodsResources() { 34 | } 35 | 36 | /// 37 | /// Returns the cached ResourceManager instance used by this class. 38 | /// 39 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 40 | internal static global::System.Resources.ResourceManager ResourceManager { 41 | get { 42 | if (object.ReferenceEquals(resourceMan, null)) { 43 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityEngineAnalyzer.StringMethods.DoNotUseStringMethodsResources", typeof(DoNotUseStringMethodsResources).GetTypeInfo().Assembly); 44 | resourceMan = temp; 45 | } 46 | return resourceMan; 47 | } 48 | } 49 | 50 | /// 51 | /// Overrides the current thread's CurrentUICulture property for all 52 | /// resource lookups using this strongly typed resource class. 53 | /// 54 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 55 | internal static global::System.Globalization.CultureInfo Culture { 56 | get { 57 | return resourceCulture; 58 | } 59 | set { 60 | resourceCulture = value; 61 | } 62 | } 63 | 64 | /// 65 | /// 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.. 66 | /// 67 | internal static string Description { 68 | get { 69 | return ResourceManager.GetString("Description", resourceCulture); 70 | } 71 | } 72 | 73 | /// 74 | /// 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.. 75 | /// 76 | internal static string MessageFormat { 77 | get { 78 | return ResourceManager.GetString("MessageFormat", resourceCulture); 79 | } 80 | } 81 | 82 | /// 83 | /// Looks up a localized string similar to Do not use SendMessage, SendMessageUpwards or BroadcastMessage.. 84 | /// 85 | internal static string Title { 86 | get { 87 | return ResourceManager.GetString("Title", resourceCulture); 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/StringMethods/DoNotUseStringMethodsResources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 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. 122 | An optional longer localizable description of the diagnostic. 123 | 124 | 125 | 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. 126 | The format-able message the diagnostic displays. 127 | 128 | 129 | Do not use SendMessage, SendMessageUpwards or BroadcastMessage. 130 | The title of the diagnostic. 131 | 132 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/tools/install.ps1: -------------------------------------------------------------------------------- 1 | param($installPath, $toolsPath, $package, $project) 2 | 3 | $analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers" ) * -Resolve 4 | 5 | foreach($analyzersPath in $analyzersPaths) 6 | { 7 | # Install the language agnostic analyzers. 8 | if (Test-Path $analyzersPath) 9 | { 10 | foreach ($analyzerFilePath in Get-ChildItem $analyzersPath -Filter *.dll) 11 | { 12 | if($project.Object.AnalyzerReferences) 13 | { 14 | $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName) 15 | } 16 | } 17 | } 18 | } 19 | 20 | # $project.Type gives the language name like (C# or VB.NET) 21 | $languageFolder = "" 22 | if($project.Type -eq "C#") 23 | { 24 | $languageFolder = "cs" 25 | } 26 | if($project.Type -eq "VB.NET") 27 | { 28 | $languageFolder = "vb" 29 | } 30 | if($languageFolder -eq "") 31 | { 32 | return 33 | } 34 | 35 | foreach($analyzersPath in $analyzersPaths) 36 | { 37 | # Install language specific analyzers. 38 | $languageAnalyzersPath = join-path $analyzersPath $languageFolder 39 | if (Test-Path $languageAnalyzersPath) 40 | { 41 | foreach ($analyzerFilePath in Get-ChildItem $languageAnalyzersPath -Filter *.dll) 42 | { 43 | if($project.Object.AnalyzerReferences) 44 | { 45 | $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName) 46 | } 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /UnityEngineAnalyzer/UnityEngineAnalyzer/tools/uninstall.ps1: -------------------------------------------------------------------------------- 1 | param($installPath, $toolsPath, $package, $project) 2 | 3 | $analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers" ) * -Resolve 4 | 5 | foreach($analyzersPath in $analyzersPaths) 6 | { 7 | # Uninstall the language agnostic analyzers. 8 | if (Test-Path $analyzersPath) 9 | { 10 | foreach ($analyzerFilePath in Get-ChildItem $analyzersPath -Filter *.dll) 11 | { 12 | if($project.Object.AnalyzerReferences) 13 | { 14 | $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName) 15 | } 16 | } 17 | } 18 | } 19 | 20 | # $project.Type gives the language name like (C# or VB.NET) 21 | $languageFolder = "" 22 | if($project.Type -eq "C#") 23 | { 24 | $languageFolder = "cs" 25 | } 26 | if($project.Type -eq "VB.NET") 27 | { 28 | $languageFolder = "vb" 29 | } 30 | if($languageFolder -eq "") 31 | { 32 | return 33 | } 34 | 35 | foreach($analyzersPath in $analyzersPaths) 36 | { 37 | # Uninstall language specific analyzers. 38 | $languageAnalyzersPath = join-path $analyzersPath $languageFolder 39 | if (Test-Path $languageAnalyzersPath) 40 | { 41 | foreach ($analyzerFilePath in Get-ChildItem $languageAnalyzersPath -Filter *.dll) 42 | { 43 | if($project.Object.AnalyzerReferences) 44 | { 45 | try 46 | { 47 | $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName) 48 | } 49 | catch 50 | { 51 | 52 | } 53 | } 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | UnityEngineAnalyzer 2 | =================== 3 | 4 | This project has been archived. You should use and contribute to the Microsoft supported analyzers ([Microsoft.Unity.Analyzers](https://github.com/microsoft/Microsoft.Unity.Analyzers)) 5 | 6 | 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. 7 | 8 | 9 | Comand Line Interface 10 | --------------------- 11 | 12 | In order to use the Command Line Interface (CLI), download the latest release of UnityEngineAnalyzer then unzip the archive. 13 | 14 | 1. Open a Command Prompt or Powershell Window 15 | 1. Run `UnityEngineAnalyzer.CLI.exe ` 16 | 1. Observe the analysis results 17 | 1. (Optional) In the same location as the project file are `report.json` and `UnityReport.html` files containig the results of the analysis 18 | 19 | 20 | Example: 21 | 22 | `> UnityEngineAnalyzer.CLI.exe C:\Code\MyGame.CSharp.csproj` 23 | 24 | 25 | Visual Studio Integration 26 | ------------------------- 27 | 28 | In Visual Studio 2015, go to `Tools > Nuget Package Manager > Manage Nuget Packages for Solution...`. Search for and install `UnityEngineAnalyzer` 29 | 30 | Configuration 31 | ------------- 32 | 33 | Right-click `Analyzers` to modify the severity or to disable the rule completely. 34 | 35 | ![](https://raw.githubusercontent.com/meng-hui/UnityEngineAnalyzer/master/Documents/configuration.png) 36 | 37 | Limitations 38 | ----------- 39 | 40 | - HTML Report requires FireFox or XOR (Corss Origin Request) enabled in other browsers 41 | - It doesn't have rules for all of [Mono's AOT Limitations](https://developer.xamarin.com/guides/ios/advanced_topics/limitations/) 42 | - IL2CPP might change the limitations of AOT compilation 43 | 44 | Below is a sample of all the rules available in this analyzer 45 | 46 | ```C# 47 | // AOT0001: System.Runtime.Remoting is not suppported 48 | using System.Runtime.Remoting; 49 | // AOT0002: System.Reflection.Emit is not supported 50 | using System.Reflection.Emit; 51 | using UnityEngine; 52 | 53 | class FooBehaviour : MonoBehaviour 54 | { 55 | void Start() 56 | { 57 | // AOT0003: Reflection only works for looking up existing types 58 | Type.GetType(""); 59 | 60 | // UEA0002: Using string methods can lead to code that is hard to maintain 61 | SendMessage(""); 62 | 63 | // UEA0006: Use of coroutines cause some allocations 64 | StartCoroutine(""); 65 | } 66 | 67 | // UEA0001: Using OnGUI causes allocations and GC spikes 68 | void OnGUI() 69 | { 70 | 71 | } 72 | 73 | // UEA0003: Empty MonoBehaviour methods are executed and incur a small overhead 74 | void FixedUpdate() 75 | { 76 | 77 | } 78 | 79 | void OnTriggerEnter(Collider other) 80 | { 81 | // UEA0004: Using CompareTag for tag comparison does not cause allocations 82 | if (other.tag == "") 83 | { 84 | 85 | } 86 | } 87 | 88 | void Update() 89 | { 90 | // UEA0005: Warning to cache the result of find in Start or Awake 91 | GameObject.Find(""); 92 | } 93 | } 94 | ``` 95 | 96 | License 97 | ------- 98 | 99 | See [LICENSE](https://raw.githubusercontent.com/meng-hui/UnityEngineAnalyzer/master/LICENSE) 100 | --------------------------------------------------------------------------------