diff --git a/.gitignore b/.gitignore index 170873f..2f2f9a3 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,9 @@ *.user *.userosscache *.sln.docstates +AutoAddUnityEngineAnalyzer/obj/ +AutoAddUnityEngineAnalyzer/Library/ +AutoAddUnityEngineAnalyzer/Temp/ # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs @@ -257,3 +260,6 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk +.DS_Store +*.csproj +*.sln diff --git a/AutoAddUnityEngineAnalyzer/Assets/Editor/AutoAddUnityEngineAnalyzer.cs b/AutoAddUnityEngineAnalyzer/Assets/Editor/AutoAddUnityEngineAnalyzer.cs new file mode 100644 index 0000000..656d7ad --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/Assets/Editor/AutoAddUnityEngineAnalyzer.cs @@ -0,0 +1,68 @@ + +using UnityEditor; +using UnityEngine; +using System.IO; +using System.Xml.Linq; +using System.Linq; + +namespace UnityEngineAnalyzer +{ + + public class AutoAddUnityEngineAnalyzer : AssetPostprocessor + { + /// + /// Put your UnityEngineAnalyzer.dll in your unity project, + /// and modify this path relative to your project. + /// + public const string UnityEngineAnalyzerPath = "Tools\\VisualStudio\\UnityEngineAnalyzer\\UnityEngineAnalyzer.dll"; + + static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) + { + TryAddUnityEngineAnalyzer(); + } + + + private static void TryAddUnityEngineAnalyzer() + { + string dataPath = Application.dataPath + "/../"; + + var csprojPaths = Directory.GetFiles(dataPath, "*.csproj"); + + foreach(var oneCsProjPath in csprojPaths) + { + if(!string.IsNullOrEmpty(oneCsProjPath)) + { + XDocument doc = XDocument.Load(oneCsProjPath); + var defaultNamespace = doc.Root.GetDefaultNamespace(); + + var unityEngineAnalyzer = doc.Descendants(defaultNamespace + "Analyzer"). + Where(x => x.Attribute("Include"). + Value.Contains("UnityEngineAnalyzer.dll")). + FirstOrDefault(); + + if(unityEngineAnalyzer == null) + { + Debug.Log("can not find UnityEngineAnalyzer in oneCsProjPath=" + oneCsProjPath); + + try + { + doc.Root. + Add( + new XElement(defaultNamespace + "ItemGroup", + new XElement(defaultNamespace + "Analyzer", + new XAttribute("Include", UnityEngineAnalyzerPath)))); + + doc.Save(oneCsProjPath); + Debug.Log("did add UnityEngineAnalyzer in oneCsProjPath=" + oneCsProjPath); + } + catch (System.Exception ex) + { + Debug.LogError("exception caught in adding UnityEngineAnalyzer in oneCsProjPath=" + oneCsProjPath + "\nexception=" + ex); + } + } + } + } + } + } + +} \ No newline at end of file diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/AudioManager.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/AudioManager.asset new file mode 100644 index 0000000..da61125 --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/AudioManager.asset @@ -0,0 +1,17 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!11 &1 +AudioManager: + m_ObjectHideFlags: 0 + m_Volume: 1 + Rolloff Scale: 1 + Doppler Factor: 1 + Default Speaker Mode: 2 + m_SampleRate: 0 + m_DSPBufferSize: 0 + m_VirtualVoiceCount: 512 + m_RealVoiceCount: 32 + m_SpatializerPlugin: + m_AmbisonicDecoderPlugin: + m_DisableAudio: 0 + m_VirtualizeEffects: 1 diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/ClusterInputManager.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/ClusterInputManager.asset new file mode 100644 index 0000000..e7886b2 --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/ClusterInputManager.asset @@ -0,0 +1,6 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!236 &1 +ClusterInputManager: + m_ObjectHideFlags: 0 + m_Inputs: [] diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/DynamicsManager.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/DynamicsManager.asset new file mode 100644 index 0000000..1931946 --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/DynamicsManager.asset @@ -0,0 +1,19 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!55 &1 +PhysicsManager: + m_ObjectHideFlags: 0 + serializedVersion: 3 + m_Gravity: {x: 0, y: -9.81, z: 0} + m_DefaultMaterial: {fileID: 0} + m_BounceThreshold: 2 + m_SleepThreshold: 0.005 + m_DefaultContactOffset: 0.01 + m_DefaultSolverIterations: 6 + m_DefaultSolverVelocityIterations: 1 + m_QueriesHitBackfaces: 0 + m_QueriesHitTriggers: 1 + m_EnableAdaptiveForce: 0 + m_EnablePCM: 1 + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + m_AutoSimulation: 1 diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/EditorBuildSettings.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/EditorBuildSettings.asset new file mode 100644 index 0000000..6dc24f7 --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/EditorBuildSettings.asset @@ -0,0 +1,7 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1045 &1 +EditorBuildSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Scenes: [] diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/EditorSettings.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/EditorSettings.asset new file mode 100644 index 0000000..c0c814f --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/EditorSettings.asset @@ -0,0 +1,16 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!159 &1 +EditorSettings: + m_ObjectHideFlags: 0 + serializedVersion: 4 + m_ExternalVersionControlSupport: Visible Meta Files + m_SerializationMode: 2 + m_DefaultBehaviorMode: 1 + m_SpritePackerMode: 4 + m_SpritePackerPaddingPower: 1 + m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd + m_ProjectGenerationRootNamespace: + m_UserGeneratedProjectSuffix: + m_CollabEditorSettings: + inProgressEnabled: 1 diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/GraphicsSettings.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/GraphicsSettings.asset new file mode 100644 index 0000000..74d7b53 --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/GraphicsSettings.asset @@ -0,0 +1,61 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!30 &1 +GraphicsSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_Deferred: + m_Mode: 1 + m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0} + m_DeferredReflections: + m_Mode: 1 + m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0} + m_ScreenSpaceShadows: + m_Mode: 1 + m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0} + m_LegacyDeferred: + m_Mode: 1 + m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0} + m_DepthNormals: + m_Mode: 1 + m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0} + m_MotionVectors: + m_Mode: 1 + m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0} + m_LightHalo: + m_Mode: 1 + m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0} + m_LensFlare: + m_Mode: 1 + m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0} + m_AlwaysIncludedShaders: + - {fileID: 7, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15104, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15105, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} + m_PreloadedShaders: [] + m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, + type: 0} + m_CustomRenderPipeline: {fileID: 0} + m_TransparencySortMode: 0 + m_TransparencySortAxis: {x: 0, y: 0, z: 1} + m_DefaultRenderingPath: 1 + m_DefaultMobileRenderingPath: 1 + m_TierSettings: [] + m_LightmapStripping: 0 + m_FogStripping: 0 + m_InstancingStripping: 0 + m_LightmapKeepPlain: 1 + m_LightmapKeepDirCombined: 1 + m_LightmapKeepDynamicPlain: 1 + m_LightmapKeepDynamicDirCombined: 1 + m_LightmapKeepShadowMask: 1 + m_LightmapKeepSubtractive: 1 + m_FogKeepLinear: 1 + m_FogKeepExp: 1 + m_FogKeepExp2: 1 + m_AlbedoSwatchInfos: [] + m_LightsUseLinearIntensity: 0 + m_LightsUseColorTemperature: 0 diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/InputManager.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/InputManager.asset new file mode 100644 index 0000000..17c8f53 --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/InputManager.asset @@ -0,0 +1,295 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!13 &1 +InputManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Axes: + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: left + positiveButton: right + altNegativeButton: a + altPositiveButton: d + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: down + positiveButton: up + altNegativeButton: s + altPositiveButton: w + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left ctrl + altNegativeButton: + altPositiveButton: mouse 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left alt + altNegativeButton: + altPositiveButton: mouse 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left shift + altNegativeButton: + altPositiveButton: mouse 2 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: space + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse X + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse Y + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse ScrollWheel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 2 + joyNum: 0 + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 0 + type: 2 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 1 + type: 2 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 0 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 1 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 2 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 3 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: return + altNegativeButton: + altPositiveButton: joystick button 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: enter + altNegativeButton: + altPositiveButton: space + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Cancel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: escape + altNegativeButton: + altPositiveButton: joystick button 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/NavMeshAreas.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/NavMeshAreas.asset new file mode 100644 index 0000000..6dd520f --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/NavMeshAreas.asset @@ -0,0 +1,89 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!126 &1 +NavMeshProjectSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + areas: + - name: Walkable + cost: 1 + - name: Not Walkable + cost: 1 + - name: Jump + cost: 2 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + m_LastAgentTypeID: -887442657 + m_Settings: + - serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.75 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + m_SettingNames: + - Humanoid diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/NetworkManager.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/NetworkManager.asset new file mode 100644 index 0000000..5dc6a83 --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/NetworkManager.asset @@ -0,0 +1,8 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!149 &1 +NetworkManager: + m_ObjectHideFlags: 0 + m_DebugLevel: 0 + m_Sendrate: 15 + m_AssetToPrefab: {} diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/Physics2DSettings.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/Physics2DSettings.asset new file mode 100644 index 0000000..e3b2d0b --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/Physics2DSettings.asset @@ -0,0 +1,36 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!19 &1 +Physics2DSettings: + m_ObjectHideFlags: 0 + serializedVersion: 3 + m_Gravity: {x: 0, y: -9.81} + m_DefaultMaterial: {fileID: 0} + m_VelocityIterations: 8 + m_PositionIterations: 3 + m_VelocityThreshold: 1 + m_MaxLinearCorrection: 0.2 + m_MaxAngularCorrection: 8 + m_MaxTranslationSpeed: 100 + m_MaxRotationSpeed: 360 + m_BaumgarteScale: 0.2 + m_BaumgarteTimeOfImpactScale: 0.75 + m_TimeToSleep: 0.5 + m_LinearSleepTolerance: 0.01 + m_AngularSleepTolerance: 2 + m_DefaultContactOffset: 0.01 + m_AutoSimulation: 1 + m_QueriesHitTriggers: 1 + m_QueriesStartInColliders: 1 + m_ChangeStopsCallbacks: 0 + m_CallbacksOnDisable: 1 + m_AlwaysShowColliders: 0 + m_ShowColliderSleep: 1 + m_ShowColliderContacts: 0 + m_ShowColliderAABB: 0 + m_ContactArrowScale: 0.2 + m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412} + m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432} + m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745} + m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804} + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/ProjectSettings.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/ProjectSettings.asset new file mode 100644 index 0000000..7581a9f --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/ProjectSettings.asset @@ -0,0 +1,597 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!129 &1 +PlayerSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + productGUID: f0b4bd3f2cfa848008759759325afe07 + AndroidProfiler: 0 + defaultScreenOrientation: 4 + targetDevice: 2 + useOnDemandResources: 0 + accelerometerFrequency: 60 + companyName: DefaultCompany + productName: AutoAddUnityEngineAnalyzer + defaultCursor: {fileID: 0} + cursorHotspot: {x: 0, y: 0} + m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1} + m_ShowUnitySplashScreen: 1 + m_ShowUnitySplashLogo: 1 + m_SplashScreenOverlayOpacity: 1 + m_SplashScreenAnimation: 1 + m_SplashScreenLogoStyle: 1 + m_SplashScreenDrawMode: 0 + m_SplashScreenBackgroundAnimationZoom: 1 + m_SplashScreenLogoAnimationZoom: 1 + m_SplashScreenBackgroundLandscapeAspect: 1 + m_SplashScreenBackgroundPortraitAspect: 1 + m_SplashScreenBackgroundLandscapeUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenBackgroundPortraitUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenLogos: [] + m_SplashScreenBackgroundLandscape: {fileID: 0} + m_SplashScreenBackgroundPortrait: {fileID: 0} + m_VirtualRealitySplashScreen: {fileID: 0} + m_HolographicTrackingLossScreen: {fileID: 0} + defaultScreenWidth: 1024 + defaultScreenHeight: 768 + defaultScreenWidthWeb: 960 + defaultScreenHeightWeb: 600 + m_StereoRenderingPath: 0 + m_ActiveColorSpace: 0 + m_MTRendering: 1 + m_MobileMTRendering: 0 + m_StackTraceTypes: 010000000100000001000000010000000100000001000000 + iosShowActivityIndicatorOnLoading: -1 + androidShowActivityIndicatorOnLoading: -1 + tizenShowActivityIndicatorOnLoading: -1 + iosAppInBackgroundBehavior: 0 + displayResolutionDialog: 1 + iosAllowHTTPDownload: 1 + allowedAutorotateToPortrait: 1 + allowedAutorotateToPortraitUpsideDown: 1 + allowedAutorotateToLandscapeRight: 1 + allowedAutorotateToLandscapeLeft: 1 + useOSAutorotation: 1 + use32BitDisplayBuffer: 1 + disableDepthAndStencilBuffers: 0 + defaultIsFullScreen: 1 + defaultIsNativeResolution: 1 + runInBackground: 0 + captureSingleScreen: 0 + muteOtherAudioSources: 0 + Prepare IOS For Recording: 0 + Force IOS Speakers When Recording: 0 + submitAnalytics: 1 + usePlayerLog: 1 + bakeCollisionMeshes: 0 + forceSingleInstance: 0 + resizableWindow: 0 + useMacAppStoreValidation: 0 + macAppStoreCategory: public.app-category.games + gpuSkinning: 0 + graphicsJobs: 0 + xboxPIXTextureCapture: 0 + xboxEnableAvatar: 0 + xboxEnableKinect: 0 + xboxEnableKinectAutoTracking: 0 + xboxEnableFitness: 0 + visibleInBackground: 1 + allowFullscreenSwitch: 1 + graphicsJobMode: 0 + macFullscreenMode: 2 + d3d9FullscreenMode: 1 + d3d11FullscreenMode: 1 + xboxSpeechDB: 0 + xboxEnableHeadOrientation: 0 + xboxEnableGuest: 0 + xboxEnablePIXSampling: 0 + n3dsDisableStereoscopicView: 0 + n3dsEnableSharedListOpt: 1 + n3dsEnableVSync: 0 + ignoreAlphaClear: 0 + xboxOneResolution: 0 + xboxOneMonoLoggingLevel: 0 + xboxOneLoggingLevel: 1 + xboxOneDisableEsram: 0 + videoMemoryForVertexBuffers: 0 + psp2PowerMode: 0 + psp2AcquireBGM: 1 + wiiUTVResolution: 0 + wiiUGamePadMSAA: 1 + wiiUSupportsNunchuk: 0 + wiiUSupportsClassicController: 0 + wiiUSupportsBalanceBoard: 0 + wiiUSupportsMotionPlus: 0 + wiiUSupportsProController: 0 + wiiUAllowScreenCapture: 1 + wiiUControllerCount: 0 + m_SupportedAspectRatios: + 4:3: 1 + 5:4: 1 + 16:10: 1 + 16:9: 1 + Others: 1 + bundleVersion: 1.0 + preloadedAssets: [] + metroInputSource: 0 + m_HolographicPauseOnTrackingLoss: 1 + xboxOneDisableKinectGpuReservation: 0 + xboxOneEnable7thCore: 0 + vrSettings: + cardboard: + depthFormat: 0 + enableTransitionView: 0 + daydream: + depthFormat: 0 + useSustainedPerformanceMode: 0 + hololens: + depthFormat: 1 + protectGraphicsMemory: 0 + useHDRDisplay: 0 + targetPixelDensity: 0 + resolutionScalingMode: 0 + applicationIdentifier: {} + buildNumber: {} + AndroidBundleVersionCode: 1 + AndroidMinSdkVersion: 16 + AndroidTargetSdkVersion: 0 + AndroidPreferredInstallLocation: 1 + aotOptions: + stripEngineCode: 1 + iPhoneStrippingLevel: 0 + iPhoneScriptCallOptimization: 0 + ForceInternetPermission: 0 + ForceSDCardPermission: 0 + CreateWallpaper: 0 + APKExpansionFiles: 0 + keepLoadedShadersAlive: 0 + StripUnusedMeshComponents: 0 + VertexChannelCompressionMask: + serializedVersion: 2 + m_Bits: 238 + iPhoneSdkVersion: 988 + iOSTargetOSVersionString: + tvOSSdkVersion: 0 + tvOSRequireExtendedGameController: 0 + tvOSTargetOSVersionString: + uIPrerenderedIcon: 0 + uIRequiresPersistentWiFi: 0 + uIRequiresFullScreen: 1 + uIStatusBarHidden: 1 + uIExitOnSuspend: 0 + uIStatusBarStyle: 0 + iPhoneSplashScreen: {fileID: 0} + iPhoneHighResSplashScreen: {fileID: 0} + iPhoneTallHighResSplashScreen: {fileID: 0} + iPhone47inSplashScreen: {fileID: 0} + iPhone55inPortraitSplashScreen: {fileID: 0} + iPhone55inLandscapeSplashScreen: {fileID: 0} + iPadPortraitSplashScreen: {fileID: 0} + iPadHighResPortraitSplashScreen: {fileID: 0} + iPadLandscapeSplashScreen: {fileID: 0} + iPadHighResLandscapeSplashScreen: {fileID: 0} + appleTVSplashScreen: {fileID: 0} + tvOSSmallIconLayers: [] + tvOSLargeIconLayers: [] + tvOSTopShelfImageLayers: [] + tvOSTopShelfImageWideLayers: [] + iOSLaunchScreenType: 0 + iOSLaunchScreenPortrait: {fileID: 0} + iOSLaunchScreenLandscape: {fileID: 0} + iOSLaunchScreenBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreenFillPct: 100 + iOSLaunchScreenSize: 100 + iOSLaunchScreenCustomXibPath: + iOSLaunchScreeniPadType: 0 + iOSLaunchScreeniPadImage: {fileID: 0} + iOSLaunchScreeniPadBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreeniPadFillPct: 100 + iOSLaunchScreeniPadSize: 100 + iOSLaunchScreeniPadCustomXibPath: + iOSDeviceRequirements: [] + iOSURLSchemes: [] + iOSBackgroundModes: 0 + iOSMetalForceHardShadows: 0 + metalEditorSupport: 0 + metalAPIValidation: 1 + iOSRenderExtraFrameOnPause: 0 + appleDeveloperTeamID: + iOSManualSigningProvisioningProfileID: + tvOSManualSigningProvisioningProfileID: + appleEnableAutomaticSigning: 0 + AndroidTargetDevice: 0 + AndroidSplashScreenScale: 0 + androidSplashScreen: {fileID: 0} + AndroidKeystoreName: + AndroidKeyaliasName: + AndroidTVCompatibility: 1 + AndroidIsGame: 1 + androidEnableBanner: 1 + m_AndroidBanners: + - width: 320 + height: 180 + banner: {fileID: 0} + androidGamepadSupportLevel: 0 + resolutionDialogBanner: {fileID: 0} + m_BuildTargetIcons: [] + m_BuildTargetBatching: [] + m_BuildTargetGraphicsAPIs: [] + m_BuildTargetVRSettings: [] + openGLRequireES31: 0 + openGLRequireES31AEP: 0 + webPlayerTemplate: APPLICATION:Default + m_TemplateCustomTags: {} + wiiUTitleID: 0005000011000000 + wiiUGroupID: 00010000 + wiiUCommonSaveSize: 4096 + wiiUAccountSaveSize: 2048 + wiiUOlvAccessKey: 0 + wiiUTinCode: 0 + wiiUJoinGameId: 0 + wiiUJoinGameModeMask: 0000000000000000 + wiiUCommonBossSize: 0 + wiiUAccountBossSize: 0 + wiiUAddOnUniqueIDs: [] + wiiUMainThreadStackSize: 3072 + wiiULoaderThreadStackSize: 1024 + wiiUSystemHeapSize: 128 + wiiUTVStartupScreen: {fileID: 0} + wiiUGamePadStartupScreen: {fileID: 0} + wiiUDrcBufferDisabled: 0 + wiiUProfilerLibPath: + playModeTestRunnerEnabled: 0 + actionOnDotNetUnhandledException: 1 + enableInternalProfiler: 0 + logObjCUncaughtExceptions: 1 + enableCrashReportAPI: 0 + cameraUsageDescription: + locationUsageDescription: + microphoneUsageDescription: + switchNetLibKey: + switchSocketMemoryPoolSize: 6144 + switchSocketAllocatorPoolSize: 128 + switchSocketConcurrencyLimit: 14 + switchScreenResolutionBehavior: 2 + switchUseCPUProfiler: 0 + switchApplicationID: 0x01004b9000490000 + switchNSODependencies: + switchTitleNames_0: + switchTitleNames_1: + switchTitleNames_2: + switchTitleNames_3: + switchTitleNames_4: + switchTitleNames_5: + switchTitleNames_6: + switchTitleNames_7: + switchTitleNames_8: + switchTitleNames_9: + switchTitleNames_10: + switchTitleNames_11: + switchPublisherNames_0: + switchPublisherNames_1: + switchPublisherNames_2: + switchPublisherNames_3: + switchPublisherNames_4: + switchPublisherNames_5: + switchPublisherNames_6: + switchPublisherNames_7: + switchPublisherNames_8: + switchPublisherNames_9: + switchPublisherNames_10: + switchPublisherNames_11: + switchIcons_0: {fileID: 0} + switchIcons_1: {fileID: 0} + switchIcons_2: {fileID: 0} + switchIcons_3: {fileID: 0} + switchIcons_4: {fileID: 0} + switchIcons_5: {fileID: 0} + switchIcons_6: {fileID: 0} + switchIcons_7: {fileID: 0} + switchIcons_8: {fileID: 0} + switchIcons_9: {fileID: 0} + switchIcons_10: {fileID: 0} + switchIcons_11: {fileID: 0} + switchSmallIcons_0: {fileID: 0} + switchSmallIcons_1: {fileID: 0} + switchSmallIcons_2: {fileID: 0} + switchSmallIcons_3: {fileID: 0} + switchSmallIcons_4: {fileID: 0} + switchSmallIcons_5: {fileID: 0} + switchSmallIcons_6: {fileID: 0} + switchSmallIcons_7: {fileID: 0} + switchSmallIcons_8: {fileID: 0} + switchSmallIcons_9: {fileID: 0} + switchSmallIcons_10: {fileID: 0} + switchSmallIcons_11: {fileID: 0} + switchManualHTML: + switchAccessibleURLs: + switchLegalInformation: + switchMainThreadStackSize: 1048576 + switchPresenceGroupId: + switchLogoHandling: 0 + switchReleaseVersion: 0 + switchDisplayVersion: 1.0.0 + switchStartupUserAccount: 0 + switchTouchScreenUsage: 0 + switchSupportedLanguagesMask: 0 + switchLogoType: 0 + switchApplicationErrorCodeCategory: + switchUserAccountSaveDataSize: 0 + switchUserAccountSaveDataJournalSize: 0 + switchApplicationAttribute: 0 + switchCardSpecSize: -1 + switchCardSpecClock: -1 + switchRatingsMask: 0 + switchRatingsInt_0: 0 + switchRatingsInt_1: 0 + switchRatingsInt_2: 0 + switchRatingsInt_3: 0 + switchRatingsInt_4: 0 + switchRatingsInt_5: 0 + switchRatingsInt_6: 0 + switchRatingsInt_7: 0 + switchRatingsInt_8: 0 + switchRatingsInt_9: 0 + switchRatingsInt_10: 0 + switchRatingsInt_11: 0 + switchLocalCommunicationIds_0: + switchLocalCommunicationIds_1: + switchLocalCommunicationIds_2: + switchLocalCommunicationIds_3: + switchLocalCommunicationIds_4: + switchLocalCommunicationIds_5: + switchLocalCommunicationIds_6: + switchLocalCommunicationIds_7: + switchParentalControl: 0 + switchAllowsScreenshot: 1 + switchDataLossConfirmation: 0 + switchSupportedNpadStyles: 3 + switchSocketConfigEnabled: 0 + switchTcpInitialSendBufferSize: 32 + switchTcpInitialReceiveBufferSize: 64 + switchTcpAutoSendBufferSizeMax: 256 + switchTcpAutoReceiveBufferSizeMax: 256 + switchUdpSendBufferSize: 9 + switchUdpReceiveBufferSize: 42 + switchSocketBufferEfficiency: 4 + switchSocketInitializeEnabled: 1 + switchNetworkInterfaceManagerInitializeEnabled: 1 + switchPlayerConnectionEnabled: 1 + ps4NPAgeRating: 12 + ps4NPTitleSecret: + ps4NPTrophyPackPath: + ps4ParentalLevel: 11 + ps4ContentID: ED1633-NPXX51362_00-0000000000000000 + ps4Category: 0 + ps4MasterVersion: 01.00 + ps4AppVersion: 01.00 + ps4AppType: 0 + ps4ParamSfxPath: + ps4VideoOutPixelFormat: 0 + ps4VideoOutInitialWidth: 1920 + ps4VideoOutBaseModeInitialWidth: 1920 + ps4VideoOutReprojectionRate: 120 + ps4PronunciationXMLPath: + ps4PronunciationSIGPath: + ps4BackgroundImagePath: + ps4StartupImagePath: + ps4SaveDataImagePath: + ps4SdkOverride: + ps4BGMPath: + ps4ShareFilePath: + ps4ShareOverlayImagePath: + ps4PrivacyGuardImagePath: + ps4NPtitleDatPath: + ps4RemotePlayKeyAssignment: -1 + ps4RemotePlayKeyMappingDir: + ps4PlayTogetherPlayerCount: 0 + ps4EnterButtonAssignment: 1 + ps4ApplicationParam1: 0 + ps4ApplicationParam2: 0 + ps4ApplicationParam3: 0 + ps4ApplicationParam4: 0 + ps4DownloadDataSize: 0 + ps4GarlicHeapSize: 2048 + ps4ProGarlicHeapSize: 2560 + ps4Passcode: 5PN2qmWqBlQ9wQj99nsQzldVI5ZuGXbE + ps4pnSessions: 1 + ps4pnPresence: 1 + ps4pnFriends: 1 + ps4pnGameCustomData: 1 + playerPrefsSupport: 0 + restrictedAudioUsageRights: 0 + ps4UseResolutionFallback: 0 + ps4ReprojectionSupport: 0 + ps4UseAudio3dBackend: 0 + ps4SocialScreenEnabled: 0 + ps4ScriptOptimizationLevel: 0 + ps4Audio3dVirtualSpeakerCount: 14 + ps4attribCpuUsage: 0 + ps4PatchPkgPath: + ps4PatchLatestPkgPath: + ps4PatchChangeinfoPath: + ps4PatchDayOne: 0 + ps4attribUserManagement: 0 + ps4attribMoveSupport: 0 + ps4attrib3DSupport: 0 + ps4attribShareSupport: 0 + ps4attribExclusiveVR: 0 + ps4disableAutoHideSplash: 0 + ps4videoRecordingFeaturesUsed: 0 + ps4contentSearchFeaturesUsed: 0 + ps4attribEyeToEyeDistanceSettingVR: 0 + ps4IncludedModules: [] + monoEnv: + psp2Splashimage: {fileID: 0} + psp2NPTrophyPackPath: + psp2NPSupportGBMorGJP: 0 + psp2NPAgeRating: 12 + psp2NPTitleDatPath: + psp2NPCommsID: + psp2NPCommunicationsID: + psp2NPCommsPassphrase: + psp2NPCommsSig: + psp2ParamSfxPath: + psp2ManualPath: + psp2LiveAreaGatePath: + psp2LiveAreaBackroundPath: + psp2LiveAreaPath: + psp2LiveAreaTrialPath: + psp2PatchChangeInfoPath: + psp2PatchOriginalPackage: + psp2PackagePassword: WRK5RhRXdCdG5nG5azdNMK66MuCV6GXi + psp2KeystoneFile: + psp2MemoryExpansionMode: 0 + psp2DRMType: 0 + psp2StorageType: 0 + psp2MediaCapacity: 0 + psp2DLCConfigPath: + psp2ThumbnailPath: + psp2BackgroundPath: + psp2SoundPath: + psp2TrophyCommId: + psp2TrophyPackagePath: + psp2PackagedResourcesPath: + psp2SaveDataQuota: 10240 + psp2ParentalLevel: 1 + psp2ShortTitle: Not Set + psp2ContentID: IV0000-ABCD12345_00-0123456789ABCDEF + psp2Category: 0 + psp2MasterVersion: 01.00 + psp2AppVersion: 01.00 + psp2TVBootMode: 0 + psp2EnterButtonAssignment: 2 + psp2TVDisableEmu: 0 + psp2AllowTwitterDialog: 1 + psp2Upgradable: 0 + psp2HealthWarning: 0 + psp2UseLibLocation: 0 + psp2InfoBarOnStartup: 0 + psp2InfoBarColor: 0 + psp2ScriptOptimizationLevel: 0 + psmSplashimage: {fileID: 0} + splashScreenBackgroundSourceLandscape: {fileID: 0} + splashScreenBackgroundSourcePortrait: {fileID: 0} + spritePackerPolicy: + webGLMemorySize: 256 + webGLExceptionSupport: 1 + webGLNameFilesAsHashes: 0 + webGLDataCaching: 0 + webGLDebugSymbols: 0 + webGLEmscriptenArgs: + webGLModulesDirectory: + webGLTemplate: APPLICATION:Default + webGLAnalyzeBuildSize: 0 + webGLUseEmbeddedResources: 0 + webGLUseWasm: 0 + webGLCompressionFormat: 1 + scriptingDefineSymbols: {} + platformArchitecture: {} + scriptingBackend: {} + incrementalIl2cppBuild: {} + additionalIl2CppArgs: + scriptingRuntimeVersion: 0 + apiCompatibilityLevelPerPlatform: {} + m_RenderingPath: 1 + m_MobileRenderingPath: 1 + metroPackageName: AutoAddUnityEngineAnalyzer + metroPackageVersion: + metroCertificatePath: + metroCertificatePassword: + metroCertificateSubject: + metroCertificateIssuer: + metroCertificateNotAfter: 0000000000000000 + metroApplicationDescription: AutoAddUnityEngineAnalyzer + wsaImages: {} + metroTileShortName: + metroCommandLineArgsFile: + metroTileShowName: 0 + metroMediumTileShowName: 0 + metroLargeTileShowName: 0 + metroWideTileShowName: 0 + metroDefaultTileSize: 1 + metroTileForegroundText: 2 + metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0} + metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, + a: 1} + metroSplashScreenUseBackgroundColor: 0 + platformCapabilities: {} + metroFTAName: + metroFTAFileTypes: [] + metroProtocolName: + metroCompilationOverrides: 1 + tizenProductDescription: + tizenProductURL: + tizenSigningProfileName: + tizenGPSPermissions: 0 + tizenMicrophonePermissions: 0 + tizenDeploymentTarget: + tizenDeploymentTargetType: -1 + tizenMinOSVersion: 1 + n3dsUseExtSaveData: 0 + n3dsCompressStaticMem: 1 + n3dsExtSaveDataNumber: 0x12345 + n3dsStackSize: 131072 + n3dsTargetPlatform: 2 + n3dsRegion: 7 + n3dsMediaSize: 0 + n3dsLogoStyle: 3 + n3dsTitle: GameName + n3dsProductCode: + n3dsApplicationId: 0xFF3FF + stvDeviceAddress: + stvProductDescription: + stvProductAuthor: + stvProductAuthorEmail: + stvProductLink: + stvProductCategory: 0 + XboxOneProductId: + XboxOneUpdateKey: + XboxOneSandboxId: + XboxOneContentId: + XboxOneTitleId: + XboxOneSCId: + XboxOneGameOsOverridePath: + XboxOnePackagingOverridePath: + XboxOneAppManifestOverridePath: + XboxOnePackageEncryption: 0 + XboxOnePackageUpdateGranularity: 2 + XboxOneDescription: + XboxOneLanguage: + - enus + XboxOneCapability: [] + XboxOneGameRating: {} + XboxOneIsContentPackage: 0 + XboxOneEnableGPUVariability: 0 + XboxOneSockets: {} + XboxOneSplashScreen: {fileID: 0} + XboxOneAllowedProductIds: [] + XboxOnePersistentLocalStorageSize: 0 + xboxOneScriptCompiler: 0 + vrEditorSettings: + daydream: + daydreamIconForeground: {fileID: 0} + daydreamIconBackground: {fileID: 0} + cloudServicesEnabled: {} + facebookSdkVersion: 7.9.4 + apiCompatibilityLevel: 2 + cloudProjectId: + projectName: + organizationId: + cloudEnabled: 0 + enableNativePlatformBackendsForNewInputSystem: 0 + disableOldInputManagerSupport: 0 diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/ProjectVersion.txt b/AutoAddUnityEngineAnalyzer/ProjectSettings/ProjectVersion.txt new file mode 100644 index 0000000..4b80f92 --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/ProjectVersion.txt @@ -0,0 +1 @@ +m_EditorVersion: 2017.1.2f1 diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/QualitySettings.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/QualitySettings.asset new file mode 100644 index 0000000..86c047f --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/QualitySettings.asset @@ -0,0 +1,193 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!47 &1 +QualitySettings: + m_ObjectHideFlags: 0 + serializedVersion: 5 + m_CurrentQuality: 5 + m_QualitySettings: + - serializedVersion: 2 + name: Very Low + pixelLightCount: 0 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 15 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + blendWeights: 1 + textureQuality: 1 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 0 + lodBias: 0.3 + maximumLODLevel: 0 + particleRaycastBudget: 4 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Low + pixelLightCount: 0 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 20 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + blendWeights: 2 + textureQuality: 0 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 0 + lodBias: 0.4 + maximumLODLevel: 0 + particleRaycastBudget: 16 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Medium + pixelLightCount: 1 + shadows: 1 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 20 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + blendWeights: 2 + textureQuality: 0 + anisotropicTextures: 1 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 1 + lodBias: 0.7 + maximumLODLevel: 0 + particleRaycastBudget: 64 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: High + pixelLightCount: 2 + shadows: 2 + shadowResolution: 1 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 40 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + blendWeights: 2 + textureQuality: 0 + anisotropicTextures: 1 + antiAliasing: 0 + softParticles: 0 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 1 + maximumLODLevel: 0 + particleRaycastBudget: 256 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Very High + pixelLightCount: 3 + shadows: 2 + shadowResolution: 2 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 70 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + blendWeights: 4 + textureQuality: 0 + anisotropicTextures: 2 + antiAliasing: 2 + softParticles: 1 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 1.5 + maximumLODLevel: 0 + particleRaycastBudget: 1024 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Ultra + pixelLightCount: 4 + shadows: 2 + shadowResolution: 2 + shadowProjection: 1 + shadowCascades: 4 + shadowDistance: 150 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + blendWeights: 4 + textureQuality: 0 + anisotropicTextures: 2 + antiAliasing: 2 + softParticles: 1 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 2 + maximumLODLevel: 0 + particleRaycastBudget: 4096 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + m_PerPlatformDefaultQuality: + Android: 2 + Nintendo 3DS: 5 + Nintendo Switch: 5 + PS4: 5 + PSM: 5 + PSP2: 2 + Samsung TV: 2 + Standalone: 5 + Tizen: 2 + Web: 5 + WebGL: 3 + WiiU: 5 + Windows Store Apps: 5 + XboxOne: 5 + iPhone: 2 + tvOS: 2 diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/TagManager.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/TagManager.asset new file mode 100644 index 0000000..1c92a78 --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/TagManager.asset @@ -0,0 +1,43 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!78 &1 +TagManager: + serializedVersion: 2 + tags: [] + layers: + - Default + - TransparentFX + - Ignore Raycast + - + - Water + - UI + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + m_SortingLayers: + - name: Default + uniqueID: 0 + locked: 0 diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/TimeManager.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/TimeManager.asset new file mode 100644 index 0000000..558a017 --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/TimeManager.asset @@ -0,0 +1,9 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!5 &1 +TimeManager: + m_ObjectHideFlags: 0 + Fixed Timestep: 0.02 + Maximum Allowed Timestep: 0.33333334 + m_TimeScale: 1 + Maximum Particle Timestep: 0.03 diff --git a/AutoAddUnityEngineAnalyzer/ProjectSettings/UnityConnectSettings.asset b/AutoAddUnityEngineAnalyzer/ProjectSettings/UnityConnectSettings.asset new file mode 100644 index 0000000..1cc5485 --- /dev/null +++ b/AutoAddUnityEngineAnalyzer/ProjectSettings/UnityConnectSettings.asset @@ -0,0 +1,34 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!310 &1 +UnityConnectSettings: + m_ObjectHideFlags: 0 + m_Enabled: 0 + m_TestMode: 0 + m_TestEventUrl: + m_TestConfigUrl: + m_TestInitMode: 0 + CrashReportingSettings: + m_EventUrl: https://perf-events.cloud.unity3d.com/api/events/crashes + m_Enabled: 0 + m_CaptureEditorExceptions: 1 + UnityPurchasingSettings: + m_Enabled: 0 + m_TestMode: 0 + UnityAnalyticsSettings: + m_Enabled: 0 + m_InitializeOnStartup: 1 + m_TestMode: 0 + m_TestEventUrl: + m_TestConfigUrl: + UnityAdsSettings: + m_Enabled: 0 + m_InitializeOnStartup: 1 + m_TestMode: 0 + m_EnabledPlatforms: 4294967295 + m_IosGameId: + m_AndroidGameId: + m_GameIds: {} + m_GameId: + PerformanceReportingSettings: + m_Enabled: 0 diff --git a/Documents/usage.png b/Documents/usage.png new file mode 100644 index 0000000..7cf1290 Binary files /dev/null and b/Documents/usage.png differ diff --git a/UnityEngineAnalyzer.CLI/UnityEngineAnalyzer.CLI.csproj b/UnityEngineAnalyzer.CLI/UnityEngineAnalyzer.CLI.csproj index 76e8c8d..a1ef17c 100644 --- a/UnityEngineAnalyzer.CLI/UnityEngineAnalyzer.CLI.csproj +++ b/UnityEngineAnalyzer.CLI/UnityEngineAnalyzer.CLI.csproj @@ -104,6 +104,9 @@ + + ..\..\..\..\..\..\..\Program Files\Unity2017.1.3p2\Editor\Data\Managed\UnityEngine.dll + diff --git a/UnityEngineAnalyzer.dll b/UnityEngineAnalyzer.dll new file mode 100644 index 0000000..4825925 Binary files /dev/null and b/UnityEngineAnalyzer.dll differ diff --git a/UnityEngineAnalyzer.sln b/UnityEngineAnalyzer.sln index c4bc4fa..f4e1cdf 100644 --- a/UnityEngineAnalyzer.sln +++ b/UnityEngineAnalyzer.sln @@ -1,14 +1,12 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 +# Visual Studio 15 +VisualStudioVersion = 15.0.26730.15 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityEngineAnalyzer", "UnityEngineAnalyzer\UnityEngineAnalyzer\UnityEngineAnalyzer.csproj", "{BFB2BB34-FED8-48CC-9B83-A6E38BA5666C}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityEngineAnalyzer.Test", "UnityEngineAnalyzer\UnityEngineAnalyzer.Test\UnityEngineAnalyzer.Test.csproj", "{76CAC24F-3E52-43A1-A696-6848EB9C1E7B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityEngineAnalyzer.Vsix", "UnityEngineAnalyzer\UnityEngineAnalyzer.Vsix\UnityEngineAnalyzer.Vsix.csproj", "{23A41489-857C-4FC7-9B96-2F20D8EE380B}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityEngineAnalyzer.CLI", "UnityEngineAnalyzer.CLI\UnityEngineAnalyzer.CLI.csproj", "{9C2B02D9-9DCF-4617-AEF5-C00B2A332B83}" EndProject Global @@ -25,10 +23,6 @@ Global {76CAC24F-3E52-43A1-A696-6848EB9C1E7B}.Debug|Any CPU.Build.0 = Debug|Any CPU {76CAC24F-3E52-43A1-A696-6848EB9C1E7B}.Release|Any CPU.ActiveCfg = Release|Any CPU {76CAC24F-3E52-43A1-A696-6848EB9C1E7B}.Release|Any CPU.Build.0 = Release|Any CPU - {23A41489-857C-4FC7-9B96-2F20D8EE380B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {23A41489-857C-4FC7-9B96-2F20D8EE380B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {23A41489-857C-4FC7-9B96-2F20D8EE380B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {23A41489-857C-4FC7-9B96-2F20D8EE380B}.Release|Any CPU.Build.0 = Release|Any CPU {9C2B02D9-9DCF-4617-AEF5-C00B2A332B83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9C2B02D9-9DCF-4617-AEF5-C00B2A332B83}.Debug|Any CPU.Build.0 = Debug|Any CPU {9C2B02D9-9DCF-4617-AEF5-C00B2A332B83}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -37,4 +31,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {AED667A6-0E4F-4396-A379-CE20111B4D2C} + EndGlobalSection EndGlobal diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Camera/DoNotUseCameraMainInUpdateAnalyzerTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Camera/DoNotUseCameraMainInUpdateAnalyzerTests.cs new file mode 100644 index 0000000..b5bdbbf --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Camera/DoNotUseCameraMainInUpdateAnalyzerTests.cs @@ -0,0 +1,86 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.FindMethodsInUpdate; + + +//using Microsoft.CodeAnalysis.Workspaces; + +namespace UnityEngineAnalyzer.Test.FindMethodsInUpdate +{ + [TestFixture] + sealed class DoNotUseCameraMainInUpdateAnalyzerTests : AnalyzerTestFixture + { + + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new DoNotUseCameraMainInUpdateAnalyzer(); + + [Test] + public void CameraMainInUpdate() + { + var code = @" +using UnityEngine; + +class C : MonoBehaviour +{ + void Update() + { + Camera main = [|Camera.main|]; + + //var result = GameObject.Find(""param""); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.DoNotUseCameraMainInUpdate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void CameraMainInUpdateRecursive() + { + var code = @" +using UnityEngine; + +class C : MonoBehaviour +{ + void Update() + { + [|MyMethod()|]; + //var result = GameObject.Find(""param""); + } + + void MyMethod() + { + Camera main = Camera.main; + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.DoNotUseCameraMainInUpdate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Delegates/ShouldCacheDelegateAnalyzerTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Delegates/ShouldCacheDelegateAnalyzerTests.cs new file mode 100644 index 0000000..46be844 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Delegates/ShouldCacheDelegateAnalyzerTests.cs @@ -0,0 +1,800 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.Delegates; + + +//using Microsoft.CodeAnalysis.Workspaces; + +namespace UnityEngineAnalyzer.Test.Delegates +{ + [TestFixture] + sealed class ShouldCacheDelegatesAnalyzerTests : AnalyzerTestFixture + { + + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new ShouldCacheDelegateAnalyzer(); + + [Test] + public void DidNotCacheDelegate() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public EventHandler e; + void Update() + { + e += [|OnCallBack|]; + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void DidNotCacheDelegateInAwake() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public EventHandler e; + void Awake() + { + e += [|OnCallBack|]; + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void DidNotCacheDelegate2() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public EventHandler e; + void Update() + { + e += this.[|OnCallBack|]; + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void DidNotCacheDelegate2InAwake() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public EventHandler e; + void Awake() + { + e += this.[|OnCallBack|]; + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void EventDidNotCacheDelegate() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public event EventHandler e; + void Update() + { + e += [|OnCallBack|]; + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void EventDidNotCacheDelegateInAwake() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public event EventHandler e; + void Awake() + { + e += [|OnCallBack|]; + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void EventDidNotCacheDelegate2() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public event EventHandler e; + void Update() + { + e -= [|OnCallBack|]; + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void EventDidNotCacheDelegate2InAwake() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public event EventHandler e; + void Awake() + { + e -= [|OnCallBack|]; + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void EventDidCacheDelegate() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public event EventHandler e; + private EventHandler m_cachedDelegate = OnCallBack; + + void Intialize() + { + m_cachedDelegate = OnCallBack; + } + + void Update() + { + e += [|m_cachedDelegate|]; + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + + [Test] + public void FunctionDidNotCacheDelegate() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public event EventHandler e; + void Update() + { + CallDelegate([|OnCallBack|]); + } + + private void CallDelegate(EventHandler handler) + { + + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void FunctionDidNotCacheDelegateInAwake() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public event EventHandler e; + void Awake() + { + CallDelegate([|OnCallBack|]); + } + + private void CallDelegate(EventHandler handler) + { + + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void FunctionDidCacheDelegate() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public event EventHandler e; + private EventHandler m_cachedDelegate; + + void Intialize() + { + m_cachedDelegate = OnCallBack; + } + + void Update() + { + CallDelegate([|m_cachedDelegate|]); + } + + private void CallDelegate(EventHandler handler) + { + + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void FunctionIsNotDelegate() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public event EventHandler e; + void Update() + { + Call([|ReturnInt()|]); + } + + private void Call(int intValue) + { + + } + + private int ReturnInt() + { + return 0; + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void FunctionIsNotDelegateInAwake() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public event EventHandler e; + void Awake() + { + Call([|ReturnInt()|]); + } + + private void Call(int intValue) + { + + } + + private int ReturnInt() + { + return 0; + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + + [Test] + public void FunctionIsNotDelegate2() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public event EventHandler e; + void Update() + { + Call(this.[|ReturnInt()|]); + } + + private void Call(int intValue) + { + + } + + private int ReturnInt() + { + return 0; + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void FunctionIsNotDelegate3() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public string s; + private EventHandler m_cachedDelegate = OnCallBack; + + void Intialize() + { + m_cachedDelegate = OnCallBack; + } + + void Update() + { + s += [|ToString()|]; + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + + [Test] + public void FunctionIsNotDelegateAndDidNotCacheDelegate() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public event EventHandler e; + void Update() + { + Call(ReturnInt(), [|OnCallBack|]); + } + + private void Call(int intValue, EventHandler h) + { + + } + + private int ReturnInt() + { + return 0; + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + + [Test] + public void FunctionIsNotDelegateAndDidNotCacheDelegateInAwake() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public event EventHandler e; + void Awake() + { + Call(ReturnInt(), [|OnCallBack|]); + } + + private void Call(int intValue, EventHandler h) + { + + } + + private int ReturnInt() + { + return 0; + } + + private void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void FunctionIsNotDelegateAndDidNotCacheDelegate_IgnoreStaticMethod() + { + var code = @" + +using System; +using UnityEngine; + +class C : MonoBehaviour +{ + public event EventHandler e; + void Update() + { + Call(ReturnInt(), [|OnCallBack|]); + } + + private void Call(int intValue, EventHandler h) + { + + } + + private int ReturnInt() + { + return 0; + } + + private static void OnCallBack(object sender, EventArgs e) + { + throw new NotImplementedException(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.ShouldCacheDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/EmptyMonoBehaviourMethods/EmptyMonoBehaviourMethodsCodeFixTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/EmptyMonoBehaviourMethods/EmptyMonoBehaviourMethodsCodeFixTests.cs new file mode 100644 index 0000000..6fd4ec4 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/EmptyMonoBehaviourMethods/EmptyMonoBehaviourMethodsCodeFixTests.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Text; +using NUnit.Core; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.EmptyMonoBehaviourMethods; + +namespace UnityEngineAnalyzer.Test.EmptyMonoBehaviourMethods +{ + [TestFixture] + sealed class EmptyMonoBehaviourMethodsCodeFixTests:CodeFixTestFixture + { + protected override string LanguageName => LanguageNames.CSharp; + protected override CodeFixProvider CreateProvider()=> new EmptyMonoBehaviourMethodsCodeFixer(); + + [Test] + public void RemoveEmptyMonoMethods() + { + const string rawStr = @" +using UnityEngine + +class C : MonoBehaviour +{ + [|void Update() { }|] +}"; + + const string expectStr = @" +using UnityEngine + +class C : MonoBehaviour +{ +}"; + + Document document; + TextSpan span; + TestHelpers.TryGetDocumentAndSpanFromMarkup(rawStr, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); + + TestCodeFix(document, span, expectStr, DiagnosticDescriptors.EmptyMonoBehaviourMethod); + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/FindMethodsInUpdate/DoNotUseFindMethodsInUpdateAnalyzerTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/FindMethodsInUpdate/DoNotUseFindMethodsInUpdateAnalyzerTests.cs index aa72bff..4a27712 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/FindMethodsInUpdate/DoNotUseFindMethodsInUpdateAnalyzerTests.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/FindMethodsInUpdate/DoNotUseFindMethodsInUpdateAnalyzerTests.cs @@ -47,6 +47,42 @@ void Update() } } + + [Test] + public void GameObjectFindInUpdateRecursive() + { + var code = @" +using UnityEngine; + +class C : MonoBehaviour +{ + void Update() + { + [|MyMethod()|]; + //var result = GameObject.Find(""param""); + } + + void MyMethod() + { + GameObject.Find(""param""); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.DoNotUseFindMethodsInUpdate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] public void GameObjectFindInStart() { diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/ForEachInUpdate/DoNotUseForeachInUpdateAnalyzerTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/ForEachInUpdate/DoNotUseForeachInUpdateAnalyzerTests.cs index 24649e0..541f781 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/ForEachInUpdate/DoNotUseForeachInUpdateAnalyzerTests.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/ForEachInUpdate/DoNotUseForeachInUpdateAnalyzerTests.cs @@ -11,46 +11,46 @@ namespace UnityEngineAnalyzer.Test.ForEachInUpdate { - [TestFixture] - sealed class DoNotUseForeachInUpdateAnalyzerTests : AnalyzerTestFixture - { +[TestFixture] +sealed class DoNotUseForeachInUpdateAnalyzerTests : AnalyzerTestFixture +{ - protected override string LanguageName => LanguageNames.CSharp; - protected override DiagnosticAnalyzer CreateAnalyzer() => new DoNotUseForEachInUpdate(); + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new DoNotUseForEachInUpdate(); - [Test] - public void ForEachInUpdate() - { - var code = @" + [Test] + public void ForEachInUpdate() + { + var code = @" using UnityEngine; class C : MonoBehaviour { - void Update() - { - var colors = new[] {""red"", ""white"", ""blue""}; - var result = string.Empty; - [|foreach|] (var color in colors) - { - result += color; - } +void Update() +{ + var colors = new[] {""red"", ""white"", ""blue""}; + var result = string.Empty; + [|foreach|] (var color in colors) + { + result += color; } + } }"; - - Document document; - TextSpan span; - if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, - out document, out span)) - { - HasDiagnostic(document, span, DiagnosticIDs.DoNotUseForEachInUpdate); - } - else - { - Assert.Fail("Could not load unit test code"); - } + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.DoNotUseForEachInUpdate); + } + else + { + Assert.Fail("Could not load unit test code"); } } } +} \ No newline at end of file diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/GCAlloc/DoNotBoxWhenInvokeAnalyzerTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/GCAlloc/DoNotBoxWhenInvokeAnalyzerTests.cs new file mode 100644 index 0000000..2bb8f8f --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/GCAlloc/DoNotBoxWhenInvokeAnalyzerTests.cs @@ -0,0 +1,262 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.GCAlloc; + + +//using Microsoft.CodeAnalysis.Workspaces; + +namespace UnityEngineAnalyzer.Test.GCAlloc +{ + [TestFixture] + sealed class DoNotBoxWhenInvokeAnalyzerTests : AnalyzerTestFixture + { + + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new DoNotBoxWhenInvokeAnalyzer(); + + [Test] + public void BoxWhenInvokeWithLiteral() + { + var code = @" + +class C +{ + private void Method(object p1, int p2) + { + } + + private void Caller() + { + Method([|234|], 2); + } +} + +"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, null, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.DoNotBoxWhenInvoke); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void BoxWhenInvokeWithIdentifier() + { + var code = @" + +class C +{ + private void Method(object p1, int p2) + { + } + + private void Caller() + { + int arg = 234; + Method([|arg|], 2); + } +} + +"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, null, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.DoNotBoxWhenInvoke); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void NoBoxWhenInvokeWithLiteral() + { + var code = @" + +class C +{ + private void Method(int p1, int p2) + { + } + + private void Caller() + { + Method([|234|], 2); + } +} + +"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, null, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.DoNotBoxWhenInvoke); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + + [Test] + public void NoBoxWhenInvokeWithIdentifier() + { + var code = @" + +class C +{ + private void Method(int p1, int p2) + { + } + + private void Caller() + { + int arg = 234; + Method([|arg|], 2); + } +} + +"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, null, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.DoNotBoxWhenInvoke); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void BoxIgnoredWhenConditionalInvokeWithLiteral() + { + var code = @" + +using System.Diagnostics; + +class C +{ + [Conditional(""SOME_CONDITION""), Conditional(""SOME_CONDITION2"")] + private void Method(object p1, int p2) + { + } + + private void Caller() + { + Method([|234|], 2); + } +} + +"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, null, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.DoNotBoxWhenInvoke); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + // For params argument, shoule be some external process: + // if type of params array is value type: won't box; + // if type of params array is reference type: box. + + [Test] + public void NoBoxInParams() + { + var code = @" +class C +{ + private void Method(params int[] argus) + { + } + + private void Caller() + { + int arg = 234; + Method([|arg, 2|]); + } +} +"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, null, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.DoNotBoxWhenInvoke); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void BoxInParams() + { + var code = @" +class C +{ + private void Method(params C[] argus) + { + } + + private void Caller() + { + int arg = 234; + Method([|arg|], [|2|]); + } +} +"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, null, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.DoNotBoxWhenInvoke); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/GCAlloc/DoNotGCAllocInUpdateAnalyzerTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/GCAlloc/DoNotGCAllocInUpdateAnalyzerTests.cs new file mode 100644 index 0000000..dd25ab1 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/GCAlloc/DoNotGCAllocInUpdateAnalyzerTests.cs @@ -0,0 +1,278 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.GCAlloc; + + +//using Microsoft.CodeAnalysis.Workspaces; + +namespace UnityEngineAnalyzer.Test.GCAlloc +{ + [TestFixture] + sealed class DoNotGCAllocInUpdateAnalyzerTests : AnalyzerTestFixture + { + + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new DoNotGCAllocInUpdateAnalyzer(); + + [Test] + public void GCAllocValueTypeInUpdate() + { + var code = @" +using UnityEngine; + +struct S {} + +class C : MonoBehaviour +{ + void Update() + { + S s = [|new S()|]; + + //var result = GameObject.Find(""param""); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.DoNotGCAllocInUpdate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void GCAllocInUpdate() + { + var code = @" +using UnityEngine; + +class B {} + +class C : MonoBehaviour +{ + void Update() + { + B b = [|new B()|]; + + //var result = GameObject.Find(""param""); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.DoNotGCAllocInUpdate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + + [Test] + public void GCAllocInBranchInUpdate() + { + var code = @" +using UnityEngine; + +class B {} + +class C : MonoBehaviour +{ + void Update() + { + B b; + if(b == null) + { + b = [|new B()|]; + }; + + //var result = GameObject.Find(""param""); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.DoNotGCAllocInUpdate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void GCAllocInUpdateRecursive() + { + var code = @" +using UnityEngine; + +class B {} + +class C : MonoBehaviour +{ + void Update() + { + [|MyMethod()|]; + //var result = GameObject.Find(""param""); + } + + void MyMethod() + { + B b = new B(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.DoNotGCAllocInUpdate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + + [Test] + public void GCAllocInUpdateRecursiveWithBranch() + { + var code = @" +using UnityEngine; + +class B {} + +class C : MonoBehaviour +{ + void Update() + { + if(true) + { + [|MyMethod()|]; + } + //var result = GameObject.Find(""param""); + } + + void MyMethod() + { + B b = new B(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.DoNotGCAllocInUpdate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void GCAllocInUpdateRecursiveWithBranch2() + { + var code = @" +using UnityEngine; + +class B {} + +class C : MonoBehaviour +{ + void Update() + { + [|MyMethod()|]; + //var result = GameObject.Find(""param""); + } + + void MyMethod() + { + if(true) + { + B b = new B(); + } + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.DoNotGCAllocInUpdate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void GCAllocValueTypeInUpdateRecursive() + { + var code = @" +using UnityEngine; + +struct S {} + +class C : MonoBehaviour +{ + void Update() + { + [|MyMethod()|]; + //var result = GameObject.Find(""param""); + } + + void MyMethod() + { + S s = new S(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.DoNotGCAllocInUpdate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Generics/DoNotUseEnumTypeParameterAnalyzerTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Generics/DoNotUseEnumTypeParameterAnalyzerTests.cs new file mode 100644 index 0000000..4a64414 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Generics/DoNotUseEnumTypeParameterAnalyzerTests.cs @@ -0,0 +1,132 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.Generics; + + +//using Microsoft.CodeAnalysis.Workspaces; + +namespace UnityEngineAnalyzer.Test.GCAlloc +{ + [TestFixture] + sealed class DoNotUseEnumTypeParameterAnalyzerTests : AnalyzerTestFixture + { + + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new DoNotUseEnumTypeParameterAnalyzer(); + + [Test] + public void UseEnumTypeParameter() + { + var code = @" + +class C +{ + public enum MyEnum { One, Two } + public Dictionary<[|MyEnum|], int> map; +} + +"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, null, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.DoNoUseEnumTypeParameter); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void UseEnumTypeParameter2() + { + var code = @" + +class C +{ + public enum MyEnum { One, Two } + public Dictionary> map; +} + +"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, null, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.DoNoUseEnumTypeParameter); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + + [Test] + public void UseEnumTypeParameter3() + { + var code = @" + +class C +{ + public enum MyEnum { One, Two } + public Dictionary>> map; +} + +"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, null, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.DoNoUseEnumTypeParameter); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void UseNoEnumTypeParameter() + { + var code = @" + +class C +{ + public enum MyEnum { One, Two } + public Dictionary<[|int|], int> map; +} + +"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, null, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.DoNoUseEnumTypeParameter); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Generics/EnumShouldManualSetMemberValueTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Generics/EnumShouldManualSetMemberValueTests.cs new file mode 100644 index 0000000..a057255 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Generics/EnumShouldManualSetMemberValueTests.cs @@ -0,0 +1,75 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.Generics; + + +//using Microsoft.CodeAnalysis.Workspaces; + +namespace UnityEngineAnalyzer.Test.GCAlloc +{ + [TestFixture] + sealed class EnumShouldManualSetMemberValueTests : AnalyzerTestFixture + { + + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new EnumShouldManualSetMemberValue(); + + [Test] + public void EnumWithoutManualMemberValue() + { + var code = @" + +enum E +{ + [|a|], + b = 2, +} + +"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, null, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.EnumShouldManualSetMemberValue); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + + [Test] + public void EnumWithManualMemberValue() + { + var code = @" + +enum E +{ + [|a|] = 1, + b = 2, +} +"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, null, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.EnumShouldManualSetMemberValue); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/IL2CPP/UnsealedDerivedClassAnalyzerTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/IL2CPP/UnsealedDerivedClassAnalyzerTests.cs new file mode 100644 index 0000000..f98832c --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/IL2CPP/UnsealedDerivedClassAnalyzerTests.cs @@ -0,0 +1,58 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.IL2CPP; + + +//using Microsoft.CodeAnalysis.Workspaces; + +namespace UnityEngineAnalyzer.Test.GCAlloc +{ + [TestFixture] + sealed class UnsealedDerivedClassAnalyzerTests : AnalyzerTestFixture + { + + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new UnsealedDerivedClassAnalyzer(); + + [Test] + public void UnsealedOverrideMethod() + { + var code = @" + +class C +{ + protected virtual void Method() + { + } +} + + +class D : C +{ + protected override void [|Method|]() + { + } +} + +"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, null, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.UnsealedDerivedClass); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/IL2CPP/UnsealedDerivedClassCodeFixTests.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/IL2CPP/UnsealedDerivedClassCodeFixTests.cs new file mode 100644 index 0000000..00f9b78 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/IL2CPP/UnsealedDerivedClassCodeFixTests.cs @@ -0,0 +1,63 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Text; +using NUnit.Core; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.IL2CPP; + +namespace UnityEngineAnalyzer.Test.IL2CPP +{ + [TestFixture] + sealed class UnsealedDerivedClassCodeFixTests:CodeFixTestFixture + { + protected override string LanguageName => LanguageNames.CSharp; + protected override CodeFixProvider CreateProvider()=>new UnsealedDerivedClassCodeFixer(); + + [Test] + public void AddSealedModifier() + { + const string rawStr = @" +class C +{ + protected virtual void Method() + { + } +} + + +class D : C +{ + protected override void [|Method|]() + { + } +} + +"; + + const string expectStr = @" +class C +{ + protected virtual void Method() + { + } +} + + +class D : C +{ + protected sealed override void Method() + { + } +} + +"; + + Document document; + TextSpan span; + TestHelpers.TryGetDocumentAndSpanFromMarkup(rawStr, LanguageName, MetadataReferenceHelper.UsingUnityEngine, out document, out span); + + TestCodeFix(document, span, expectStr, DiagnosticDescriptors.UnsealedDerivedClass); + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/DuplicatedDelegateDetectionTestCases.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/DuplicatedDelegateDetectionTestCases.cs new file mode 100644 index 0000000..eefa77f --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/DuplicatedDelegateDetectionTestCases.cs @@ -0,0 +1,190 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.Language; + +namespace UnityEngineAnalyzer.Test.Language +{ + [TestFixture] + class DuplicatedDelegateDetectionTestCases:AnalyzerTestFixture + { + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer()=>new DuplicatedDelegateDetection(); + + [Test] + public void DetectCustomedDelegateDeclaration() + { + var code = @" +class TestHost +{ + [|private delegate void TestDelegate(string argu1);|] +} +"; + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, + MetadataReferenceHelper.UsingUnityEngine, out document, out span)) + { + // HasDiagnostic(document, span, DiagnosticIDs.UseCommonDelegate); + HasDiagnostic(document, span, DiagnosticIDs.UseCommonDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void DetectUDTDelegateDeclaration() + { + var code = @" +class TestHost +{ + [|private delegate TestHost TestDelegate(TestHost argu1);|] +} +"; + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, + MetadataReferenceHelper.UsingUnityEngine, out document, out span)) + { + // HasDiagnostic(document, span, DiagnosticIDs.UseCommonDelegate); + HasDiagnostic(document, span, DiagnosticIDs.UseCommonDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void DetectUDTDelegateDeclarationWithOutKeyword() + { + var code = @" +class TestHost +{ + [|private delegate TestHost TestDelegate(TestHost argu1, out int arg2);|] +} +"; + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, + MetadataReferenceHelper.UsingUnityEngine, out document, out span)) + { + // HasDiagnostic(document, span, DiagnosticIDs.UseCommonDelegate); + HasDiagnostic(document, span, DiagnosticIDs.UseCommonDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void DetectDelegateAsParam() + { + var code = @" +class TestHost +{ + [|private delegate string TestDelegate(string argu);|] + + private void TestMethod([|TestDelegate argu|]) + { + } +} +"; + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, + MetadataReferenceHelper.UsingUnityEngine, out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.UseCommonDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void NoDetectDelegateAsParam() + { + var code = @" +using System; + +class TestHost +{ + private void TestMethod([|Action argu|]) + { + } +} +"; + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, + MetadataReferenceHelper.UsingUnityEngine, out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.UseCommonDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void DetectDelegateAsVariable() + { + var code = @" +using System; + +class TestHost +{ + [|private delegate void Miaow(string str);|] + + private [|Miaow|] aa; +} +"; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, + MetadataReferenceHelper.UsingUnityEngine, out var document, out var span)) + { + HasDiagnostic(document, span, DiagnosticIDs.UseCommonDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void NoDetectDelegateAsVariable() + { + var code = @" +using System; + +class TestHost +{ + [|private Action aa;|] +} +"; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, + MetadataReferenceHelper.UsingUnityEngine, out var document, out _)) + { + NoDiagnostic(document, DiagnosticIDs.UseCommonDelegate); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/LambdaLocalVaribleTestCases.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/LambdaLocalVaribleTestCases.cs new file mode 100644 index 0000000..463db4f --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/LambdaLocalVaribleTestCases.cs @@ -0,0 +1,92 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.Language; + +namespace UnityEngineAnalyzer.Test.Language +{ + [TestFixture] + class LambdaLocalVaribleTestCases:AnalyzerTestFixture + { + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer()=>new LambdaClosureAnalyzer(); + + [Test] + public void ExpressionWithConstructClosure() + { + var code = @" +using System; + +class TestHost +{ + private void QueryLambdaArgu(Action callback) + { + callback(); + } + + public void TestMethod() + { + int a; + QueryLambdaArgu(() => { + int b = 999; + int c = [|a|]; + [|a|].ToString(); + b.ToString(); + b.ToString(); + }); + } +}"; + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, + MetadataReferenceHelper.UsingUnityEngine, out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.LambdaUseLocalVariable); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void ExpressionWithoutConstructClosure() + { + var code = @" +using System; + +class TestHost +{ + private void QueryLambdaArgu(Action callback) + { + callback(); + } + + public void TestMethod() + { + int a; + QueryLambdaArgu([|() => { + int b = 999; + b.ToString(); + b.ToString(); + }|]); + } +}"; + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, + MetadataReferenceHelper.UsingUnityEngine, out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.LambdaUseLocalVariable); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/StructImplementAnalyzerTest.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/StructImplementAnalyzerTest.cs new file mode 100644 index 0000000..365921a --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/StructImplementAnalyzerTest.cs @@ -0,0 +1,101 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.Language; + + +//using Microsoft.CodeAnalysis.Workspaces; + +namespace UnityEngineAnalyzer.Test.Language +{ + [TestFixture] + sealed class StructImplementationAnalyzerTests : AnalyzerTestFixture + { + + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new StructImplementAnalyzer(); + + [Test] + public void StructDoNotImplementIEquatable() + { + var code = @" +using UnityEngine; + +struct [|S|] +{ +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.StructShouldImplementIEquatable); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + + [Test] + public void StructDoImplementIEquatable() + { + var code = @" +using UnityEngine; +using System; + +struct [|S|] : IEquatable +{ + public bool Equals(S other) { return false; } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.StructShouldImplementIEquatable); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void StructDoNotImplementCorrectIEquatable() + { + var code = @" +using UnityEngine; +using System; + + +struct [|S|] : IEquatable +{ + public bool Equals(int other) { return false; } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.StructShouldImplementIEquatable); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/StructOverrideEqualsGetHashCodeAnalyzerTest.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/StructOverrideEqualsGetHashCodeAnalyzerTest.cs new file mode 100644 index 0000000..034e50e --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/StructOverrideEqualsGetHashCodeAnalyzerTest.cs @@ -0,0 +1,72 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.Language; + + +//using Microsoft.CodeAnalysis.Workspaces; + +namespace UnityEngineAnalyzer.Test.Language +{ + [TestFixture] + sealed class StructOverrideGetHashCodeTests : AnalyzerTestFixture + { + + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new StructOverrideGetHashCodeAnalyzer(); + + + [Test] + public void StructDoNotOverrideAnything() + { + var code = @" +using UnityEngine; + +struct [|S|] +{ +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.StructShouldOverrideGetHashCode); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void StructOverridGetHashCode() + { + var code = @" +using UnityEngine; + +struct [|S|] +{ + public override int GetHashCode() { return 0; } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.StructShouldOverrideGetHashCode); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/StructOverrideEqualsObjectAnalyzerTest.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/StructOverrideEqualsObjectAnalyzerTest.cs new file mode 100644 index 0000000..118e55a --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/Language/StructOverrideEqualsObjectAnalyzerTest.cs @@ -0,0 +1,70 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.Language; + + +//using Microsoft.CodeAnalysis.Workspaces; + +namespace UnityEngineAnalyzer.Test.Language +{ + [TestFixture] + sealed class StructOverrideEqualsObjectAnalyzerTests : AnalyzerTestFixture + { + + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new StructOverrideEqualsObjectAnalyzer(); + + + [Test] + public void StructDoNotOverrideAnything() + { + var code = @" +using UnityEngine; + +struct [|S|] +{ +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.StructShouldOverrideEquals); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void StructOverrideEqualsObject() + { + var code = @" +using UnityEngine; + +struct [|S|] +{ + public override bool Equals(object obj) { return false; } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.StructShouldOverrideEquals); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/LogicError/InfiniteRecursiveCallTest.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/LogicError/InfiniteRecursiveCallTest.cs new file mode 100644 index 0000000..00e48fd --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/LogicError/InfiniteRecursiveCallTest.cs @@ -0,0 +1,396 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using NUnit.Framework; +using RoslynNUnitLight; +using UnityEngineAnalyzer.Language; +using UnityEngineAnalyzer.LogicError; + + +//using Microsoft.CodeAnalysis.Workspaces; + +namespace UnityEngineAnalyzer.Test.LogicError +{ + [TestFixture] + sealed class InfiniteRecursiveCallTest : AnalyzerTestFixture + { + + protected override string LanguageName => LanguageNames.CSharp; + protected override DiagnosticAnalyzer CreateAnalyzer() => new InfiniteRecursiveCallAnalyzer(); + + [Test] + public void InfiniteRecursiveCall1() + { + var code = @" +class A +{ + void M1() + { + [|M1()|]; + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.InfiniteRecursiveCall); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void InfiniteRecursiveCall2() + { + var code = @" +class A +{ + void M1() + { + [|M2()|]; + } + void M2() + { + M1(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.InfiniteRecursiveCall); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + + [Test] + public void InfiniteRecursiveCall3() + { + var code = @" +class A +{ + void M1() + { + [|M2()|]; + } + void M2() + { + M3(); + } + void M3() + { + M1(); + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.InfiniteRecursiveCall); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void InfiniteRecursiveCallDouble() + { + var code = @" +class A +{ + void M1() + { + [|M2()|]; + } + void M2() + { + M1(); + } + void M3() + { + [|M3()|]; + } + +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + HasDiagnostic(document, span, DiagnosticIDs.InfiniteRecursiveCall); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void InfiniteRecursiveCallWithBranch1() + { + var code = @" +class A +{ + private bool m_bool; + void M1() + { + if(m_bool) + { + [|M1()|]; + } + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.InfiniteRecursiveCall); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + + [Test] + public void InfiniteRecursiveCallWithBranch2() + { + var code = @" +class A +{ + private bool m_bool; + void M1() + { + switch(m_bool) + { + default: + [|M1()|]; + break; + } + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.InfiniteRecursiveCall); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void InfiniteRecursiveCallWithBranch3() + { + var code = @" +class A +{ + private bool m_bool; + void M1() + { + switch(m_bool) + { + default: + [|M1()|]; + break; + } + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.InfiniteRecursiveCall); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void InfiniteRecursiveCallWithBranch4() + { + var code = @" +class A +{ + private bool m_bool; + void M1() + { + int[] a = new int[3]; + foreach(var oneInt in a) + { + [|M1()|]; + } + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.InfiniteRecursiveCall); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void InfiniteRecursiveCallWithBranch5() + { + var code = @" +class A +{ + private bool m_bool; + void M1() + { + for(int i = 0; i < 3; ++i) + { + [|M1()|]; + } + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.InfiniteRecursiveCall); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void InfiniteRecursiveCallWithBranch6() + { + var code = @" +class A +{ + private bool m_bool; + void M1() + { + while(true) + { + [|M1()|]; + } + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.InfiniteRecursiveCall); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + [Test] + public void InfiniteRecursiveCallWithBranch7() + { + var code = @" +class A +{ + private bool m_bool; + void M1() + { + return; + [|M1()|]; + } +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.InfiniteRecursiveCall); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + + + [Test] + public void NotInfiniteRecursiveCall() + { + var code = @" +class A +{ + private bool m_bool; + void M1() + { + [|M2()|]; + } + + void M2() {} +}"; + + Document document; + TextSpan span; + + if (TestHelpers.TryGetDocumentAndSpanFromMarkup(code, LanguageName, MetadataReferenceHelper.UsingUnityEngine, + out document, out span)) + { + NoDiagnostic(document, DiagnosticIDs.InfiniteRecursiveCall); + } + else + { + Assert.Fail("Could not load unit test code"); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/MetadataReferenceHelper.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/MetadataReferenceHelper.cs index 7f4f5fc..35aafdf 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/MetadataReferenceHelper.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/MetadataReferenceHelper.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.CodeAnalysis; using System.Collections.Immutable; using System.IO; @@ -14,7 +14,7 @@ static class MetadataReferenceHelper private static MetadataReference GetUnityMetadataReference() { - var unityEnginePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), @"Unity\Editor\Data\Managed", "UnityEngine.dll"); + var unityEnginePath = Path.Combine(@"D:\Program Files", @"Unity2017.1.3p2\Editor\Data\Managed", "UnityEngine.dll"); return MetadataReference.CreateFromFile(unityEnginePath); } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj index c8363aa..cc10cc7 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer.Test/UnityEngineAnalyzer.Test.csproj @@ -21,7 +21,7 @@ bin\Debug\ DEBUG;TRACE prompt - 4 + 0 false @@ -34,6 +34,9 @@ false + + ..\..\packages\JetBrains.dotMemoryUnit.3.0.20171219.105559\lib\net35\dotMemory.Unit.dll + ..\..\packages\Microsoft.CodeAnalysis.Common.1.0.0\lib\net45\Microsoft.CodeAnalysis.dll True @@ -124,6 +127,9 @@ false + + ..\..\..\..\..\..\..\..\Program Files\Unity2017.1.3p2\Editor\Data\Managed\UnityEngine.dll + @@ -133,9 +139,23 @@ + + + + + + + + + + + + + + @@ -147,12 +167,6 @@ - - - {BFB2BB34-FED8-48CC-9B83-A6E38BA5666C} - UnityEngineAnalyzer - - @@ -160,7 +174,12 @@ - + + + {bfb2bb34-fed8-48cc-9b83-a6e38ba5666c} + UnityEngineAnalyzer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Use common generic delegates instead of customed delegate can decrease code size. + + + Use {0} replace this customed declaration delegate. + + + Use Action or Func Delegate replace {0} delegate. + + + Use Action or Func Delegate replace {0} delegate. + + + Use Action and Func delegates replace customed delegates. + + diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateAnalyzer.cs new file mode 100644 index 0000000..160988e --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateAnalyzer.cs @@ -0,0 +1,112 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; + +namespace UnityEngineAnalyzer.Delegates +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class ShouldCacheDelegateAnalyzer : DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.ShouldCacheDelegate); + + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeClassSyntax, SyntaxKind.ClassDeclaration); + } + + public static void AnalyzeClassSyntax(SyntaxNodeAnalysisContext context) + { + var monoBehaviourInfo = new MonoBehaviourInfo(context); + + var searched = new Dictionary(); + monoBehaviourInfo.ForEachUpdateMethod((updateMethod) => + { + AnalyzeAssignmentNode(context, updateMethod); + AnalyzeInvocationNode(context, updateMethod); + }); + } + + public static event Action e; + private static void AnalyzeAssignmentNode(SyntaxNodeAnalysisContext context, MethodDeclarationSyntax method) + { + e += ShouldCacheDelegateAnalyzer_e; + e -= ShouldCacheDelegateAnalyzer_e; + foreach (var oneAssign in method.DescendantNodes().OfType()) + { + AnalyzeAddRemoveNode(context, oneAssign); + } + } + + private static void ShouldCacheDelegateAnalyzer_e(int obj) + { + throw new NotImplementedException(); + } + + private static void AnalyzeAddRemoveNode(SyntaxNodeAnalysisContext context, ExpressionSyntax syntax) + { + foreach (var oneIdSyntax in syntax.ChildNodes().OfType()) + { + var oneIdSymbol = context.SemanticModel.GetSymbolInfo(oneIdSyntax); + if (oneIdSymbol.Symbol != null) + { + if (oneIdSymbol.Symbol is IMethodSymbol methodSymbol && !methodSymbol.IsStatic) + { + var diagnostic = Diagnostic.Create(DiagnosticDescriptors.ShouldCacheDelegate, + oneIdSyntax.GetLocation(), oneIdSyntax.ToString()); + context.ReportDiagnostic(diagnostic); + } + } + } + foreach (var oneAccessSyntax in syntax.ChildNodes().OfType()) + { + foreach (var oneIdSyntax in oneAccessSyntax.ChildNodes().OfType()) + { + var oneIdSymbol = context.SemanticModel.GetSymbolInfo(oneIdSyntax); + if (oneIdSymbol.Symbol != null) + { + if (oneIdSymbol.Symbol is IMethodSymbol methodSymbol && !methodSymbol.IsStatic) + { + var diagnostic = Diagnostic.Create(DiagnosticDescriptors.ShouldCacheDelegate, + oneIdSyntax.GetLocation(), oneIdSyntax.ToString()); + context.ReportDiagnostic(diagnostic); + } + } + } + } + } + + + private static void AnalyzeInvocationNode(SyntaxNodeAnalysisContext context, MethodDeclarationSyntax method) + { + foreach(var invoke in method.DescendantNodes().OfType()) + { + var argumentListSyntax = invoke.ChildNodes().OfType(); + foreach (var oneArgListSyntax in argumentListSyntax) + { + foreach (var oneArgSyntax in oneArgListSyntax.ChildNodes().OfType()) + { + foreach (var oneIdSyntax in oneArgSyntax.ChildNodes().OfType()) + { + var oneIdSymbol = context.SemanticModel.GetSymbolInfo(oneIdSyntax); + if (oneIdSymbol.Symbol != null) + { + if (oneIdSymbol.Symbol is IMethodSymbol methodSymbol && !methodSymbol.IsStatic) + { + var diagnostic = Diagnostic.Create(DiagnosticDescriptors.ShouldCacheDelegate, + oneIdSyntax.GetLocation(), oneIdSyntax.ToString()); + context.ReportDiagnostic(diagnostic); + } + } + } + } + } + } + + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateResource.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateResource.Designer.cs new file mode 100644 index 0000000..dc6d158 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateResource.Designer.cs @@ -0,0 +1,91 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace UnityEngineAnalyzer.Delegates { + using System; + using System.Reflection; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class ShouldCacheDelegateResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal ShouldCacheDelegateResource() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityEngineAnalyzer.Delegates.ShouldCacheDelegateResource", typeof(ShouldCacheDelegateResource).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 使用此强类型资源类,为所有资源查找 + /// 重写当前线程的 CurrentUICulture 属性。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 Should use variable to cache delegate for future access. 的本地化字符串。 + /// + internal static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + /// + /// 查找类似 Each time you access delegate "{0}" will cause gc allocation. You should use variable to cache delegate "{0}" for future access. 的本地化字符串。 + /// + internal static string MessageFormat { + get { + return ResourceManager.GetString("MessageFormat", resourceCulture); + } + } + + /// + /// 查找类似 Should cache delegate 的本地化字符串。 + /// + internal static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateResource.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateResource.resx new file mode 100644 index 0000000..eaf8ccc --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Delegates/ShouldCacheDelegateResource.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Should use variable to cache delegate for future access. + An optional longer localizable description of the diagnostic. + + + Each time you access delegate "{0}" will cause gc allocation. You should use variable to cache delegate "{0}" for future access. + The format-able message the diagnostic displays. + + + Should cache delegate + The title of the diagnostic. + + \ No newline at end of file diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticCategories.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticCategories.cs index 029cfc1..677e2cd 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticCategories.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticCategories.cs @@ -7,6 +7,7 @@ static class DiagnosticCategories public const string StringMethods = "String Methods"; public const string Miscellaneous = "Miscellaneous"; public const string Performance = "Performance"; + public const string LogicError = "Logic Error"; public const string AOT = "AOT"; } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs index 3e68c71..600739b 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticDescriptors.cs @@ -1,4 +1,4 @@ -using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis; using System; using System.Linq; using System.Resources; @@ -15,6 +15,12 @@ using UnityEngineAnalyzer.OnGUI; using UnityEngineAnalyzer.Physics; using UnityEngineAnalyzer.StringMethods; +using UnityEngineAnalyzer.Language; +using UnityEngineAnalyzer.GCAlloc; +using UnityEngineAnalyzer.Generics; +using UnityEngineAnalyzer.Delegates; +using UnityEngineAnalyzer.Lambda; +using UnityEngineAnalyzer.LogicError; namespace UnityEngineAnalyzer { @@ -37,6 +43,21 @@ public static class DiagnosticDescriptors public static readonly DiagnosticDescriptor DoNotUseStringPropertyNames; public static readonly DiagnosticDescriptor UseNonAllocMethods; public static readonly DiagnosticDescriptor CameraMainIsSlow; + public static readonly DiagnosticDescriptor DoNotGCAllocnInUpdate; + public static readonly DiagnosticDescriptor DoNotGCAllocnInUpdateRecursive; + public static readonly DiagnosticDescriptor DoNotBoxWhenInvoke; + public static readonly DiagnosticDescriptor StructShouldImplementIEquatable; + public static readonly DiagnosticDescriptor StructShouldOverrideEquals; + public static readonly DiagnosticDescriptor StructShouldOverrideGetHashCode; + public static readonly DiagnosticDescriptor DoNotUseEnumTypeParameter; + public static readonly DiagnosticDescriptor ShouldCacheDelegate; + public static readonly DiagnosticDescriptor InfiniteRecursiveCall; + public static readonly DiagnosticDescriptor InfiniteRecursiveCallRecursive; + public static readonly DiagnosticDescriptor LambdaClosure; + public static readonly DiagnosticDescriptor DuplicateDelegateDetection; + public static readonly DiagnosticDescriptor DuplicateDelegateParamDetection; + public static readonly DiagnosticDescriptor DuplicateDelegateVariableDetection; + public static readonly DiagnosticDescriptor EnumShouldManualSetMemberValue; static DiagnosticDescriptors() { @@ -49,18 +70,57 @@ static DiagnosticDescriptors() UseCompareTag = CreateDiagnosticDescriptor(DiagnosticIDs.UseCompareTag, DiagnosticCategories.GC, DiagnosticSeverity.Warning); UseNonAllocMethods = CreateDiagnosticDescriptor(DiagnosticIDs.PhysicsUseNonAllocMethods, DiagnosticCategories.GC, DiagnosticSeverity.Warning, UnityVersion.UNITY_5_3); CameraMainIsSlow = CreateDiagnosticDescriptor(DiagnosticIDs.CameraMainIsSlow, DiagnosticCategories.GC, DiagnosticSeverity.Warning); + DoNotGCAllocnInUpdate = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotGCAllocInUpdate, nameof(DoNotGCAllocInUpdateResources.MessageFormat), DiagnosticCategories.GC, DiagnosticSeverity.Warning); + DoNotGCAllocnInUpdateRecursive = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotGCAllocInUpdate, nameof(DoNotGCAllocInUpdateResources.MessageFormatRecursive), DiagnosticCategories.GC, DiagnosticSeverity.Warning); + DoNotBoxWhenInvoke = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotBoxWhenInvoke, DiagnosticCategories.GC, DiagnosticSeverity.Warning); + ShouldCacheDelegate = CreateDiagnosticDescriptor(DiagnosticIDs.ShouldCacheDelegate, DiagnosticCategories.GC, DiagnosticSeverity.Warning); //Performance - DoNotUseFindMethodsInUpdate = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseFindMethodsInUpdate, DiagnosticCategories.Performance, DiagnosticSeverity.Warning); - DoNotUseFindMethodsInUpdateRecursive = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseFindMethodsInUpdate, DiagnosticCategories.Performance, DiagnosticSeverity.Warning); + DoNotUseFindMethodsInUpdate = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseFindMethodsInUpdate, nameof(DoNotUseFindMethodsInUpdateResources.MessageFormat), DiagnosticCategories.Performance, DiagnosticSeverity.Warning); + DoNotUseFindMethodsInUpdateRecursive = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseFindMethodsInUpdate, nameof(DoNotUseFindMethodsInUpdateResources.MessageFormatRecursive), DiagnosticCategories.Performance, DiagnosticSeverity.Warning); DoNotUseForEachInUpdate = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseForEachInUpdate, DiagnosticCategories.Performance, DiagnosticSeverity.Warning, UnityVersion.UNITY_1_0, UnityVersion.UNITY_5_5); UnsealedDerivedClass = CreateDiagnosticDescriptor(DiagnosticIDs.UnsealedDerivedClass, DiagnosticCategories.Performance, DiagnosticSeverity.Warning); InvokeFunctionMissing = CreateDiagnosticDescriptor(DiagnosticIDs.InvokeFunctionMissing, DiagnosticCategories.Performance, DiagnosticSeverity.Warning); DoNotUseStateName = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseStateNameInAnimator, DiagnosticCategories.Performance, DiagnosticSeverity.Warning); DoNotUseStringPropertyNames = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseStringPropertyNamesInMaterial, DiagnosticCategories.Performance, DiagnosticSeverity.Warning); + LambdaClosure = CreateDiagnosticDescriptor( + DiagnosticIDs.LambdaUseLocalVariable, + nameof(LambdaAnalyzerResources.LambdaClosureAnalyzer), + DiagnosticCategories.Performance, + DiagnosticSeverity.Warning); + DuplicateDelegateDetection = CreateDiagnosticDescriptor( + DiagnosticIDs.UseCommonDelegate, + nameof(DuplicatedDelegateDetectionResource.FoundDuplicateDelegate), + DiagnosticCategories.Performance, + DiagnosticSeverity.Warning + ); + DuplicateDelegateParamDetection = CreateDiagnosticDescriptor( + DiagnosticIDs.UseCommonDelegate, + nameof(DuplicatedDelegateDetectionResource.FoundDuplicateDelegateAsParam), + DiagnosticCategories.Performance, + DiagnosticSeverity.Info + ); + DuplicateDelegateVariableDetection = CreateDiagnosticDescriptor( + DiagnosticIDs.UseCommonDelegate, + nameof(DuplicatedDelegateDetectionResource.FoundDuplicateDelegateVariable), + DiagnosticCategories.Performance, + DiagnosticSeverity.Info + ); + + //Logic Error + InfiniteRecursiveCall = CreateDiagnosticDescriptor(DiagnosticIDs.InfiniteRecursiveCall, nameof(InfiniteRecursiveCallResources.MessageFormat), DiagnosticCategories.LogicError, DiagnosticSeverity.Error); + InfiniteRecursiveCallRecursive = CreateDiagnosticDescriptor(DiagnosticIDs.InfiniteRecursiveCall, nameof(InfiniteRecursiveCallResources.MessageFormatRecursive), DiagnosticCategories.LogicError, DiagnosticSeverity.Error); + + + //Miscellaneous EmptyMonoBehaviourMethod = CreateDiagnosticDescriptor(DiagnosticIDs.EmptyMonoBehaviourMethod, DiagnosticCategories.Miscellaneous, DiagnosticSeverity.Warning); + StructShouldImplementIEquatable = CreateDiagnosticDescriptor(DiagnosticIDs.StructShouldImplementIEquatable, nameof(StructAnalyzerResources.ShouldImplmentIEquatable), DiagnosticCategories.Miscellaneous, DiagnosticSeverity.Warning); + StructShouldOverrideEquals = CreateDiagnosticDescriptor(DiagnosticIDs.StructShouldOverrideEquals, nameof(StructAnalyzerResources.ShouldOverrideEquals), DiagnosticCategories.Miscellaneous, DiagnosticSeverity.Warning); + StructShouldOverrideGetHashCode = CreateDiagnosticDescriptor(DiagnosticIDs.StructShouldOverrideGetHashCode, nameof(StructAnalyzerResources.ShouldOverrideGetHashCode), DiagnosticCategories.Miscellaneous, DiagnosticSeverity.Warning); + DoNotUseEnumTypeParameter = CreateDiagnosticDescriptor(DiagnosticIDs.DoNoUseEnumTypeParameter, DiagnosticCategories.Miscellaneous, DiagnosticSeverity.Warning); + EnumShouldManualSetMemberValue = CreateDiagnosticDescriptor(DiagnosticIDs.EnumShouldManualSetMemberValue, DiagnosticCategories.Miscellaneous, DiagnosticSeverity.Info); //** AOT ** DoNotUseRemoting = CreateDiagnosticDescriptor(DiagnosticIDs.DoNotUseRemoting, DiagnosticCategories.AOT, DiagnosticSeverity.Info); @@ -83,6 +143,21 @@ private static DiagnosticDescriptor CreateDiagnosticDescriptor(string id, str description: new LocalizableResourceString("Description", resourceManager, typeof(T))); } + private static DiagnosticDescriptor CreateDiagnosticDescriptor(string id, string messageFormat, + string category, DiagnosticSeverity severity, UnityVersion first = UnityVersion.UNITY_1_0, UnityVersion latest = UnityVersion.LATEST, bool isEnabledByDefault = true) + { + var resourceManager = new ResourceManager(typeof(T)); + + return new DiagnosticDescriptor( + id: id, + title: new LocalizableResourceString("Title", resourceManager, typeof(T)), + messageFormat: new LocalizableResourceString(messageFormat, resourceManager, typeof(T)), + category: category, + defaultSeverity: severity, + isEnabledByDefault: isEnabledByDefault, + customTags: CreateUnityVersionInfo(first, latest), + description: new LocalizableResourceString("Description", resourceManager, typeof(T))); + } private static string[] CreateUnityVersionInfo(UnityVersion start, UnityVersion end) { return new string[] { Enum.GetName(typeof(UnityVersion), start), Enum.GetName(typeof(UnityVersion), end) }; @@ -104,3 +179,4 @@ public static UnityVersionSpan GetVersion(DiagnosticDescriptor dc) } } } + diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs index 8bb4f66..9a1bafa 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/DiagnosticIDs.cs @@ -1,4 +1,4 @@ - + namespace UnityEngineAnalyzer { public static class DiagnosticIDs @@ -16,6 +16,22 @@ public static class DiagnosticIDs public const string DoNotUseStringPropertyNamesInMaterial = "UEA0011"; public const string CameraMainIsSlow = "UEA0012"; public const string PhysicsUseNonAllocMethods = "UEA0013"; + public const string DoNotGCAllocInUpdate = "UEA0014"; + + // language analysis + public const string StructShouldImplementIEquatable = "UCS0001"; + public const string StructShouldOverrideEquals = "UCS0002"; + public const string StructShouldOverrideGetHashCode = "UCS0003"; + public const string DoNotBoxWhenInvoke = "UCS0004"; + public const string DoNoUseEnumTypeParameter = "UCS0005"; + public const string ShouldCacheDelegate = "UCS0006"; + public const string LambdaUseLocalVariable = "UCS0007"; + public const string UseCommonDelegate = "UCS0008"; + public const string EnumShouldManualSetMemberValue = "UCS0009"; + + // logic error analysis + public const string InfiniteRecursiveCall = "ULE0001"; + //NOTES: These should probably be on their own analyzer - as they are not specific to Unity public const string DoNotUseRemoting = "AOT0001"; diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/EmptyMonoBehaviourMethods/EmptyMonoBehaviourMethodsAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/EmptyMonoBehaviourMethods/EmptyMonoBehaviourMethodsAnalyzer.cs index eb742f5..731a8a2 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/EmptyMonoBehaviourMethods/EmptyMonoBehaviourMethodsAnalyzer.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/EmptyMonoBehaviourMethods/EmptyMonoBehaviourMethodsAnalyzer.cs @@ -1,4 +1,4 @@ -using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; @@ -81,7 +81,7 @@ public override void Initialize(AnalysisContext context) //NOTE: It might be more officient to find classes and then determine if they are a MonoBehaviour rather than look at every method } - private static async void AnalyzeSymbol(SymbolAnalysisContext context) + private static void AnalyzeSymbol(SymbolAnalysisContext context) { // retrieve method symbol var methodSymbol = context.Symbol as IMethodSymbol; @@ -91,7 +91,7 @@ private static async void AnalyzeSymbol(SymbolAnalysisContext context) if (methodSymbol.DeclaringSyntaxReferences.Length != 1) { return; } // retrieve the method syntax from the method symbol - var methodSyntax = await methodSymbol.DeclaringSyntaxReferences[0].GetSyntaxAsync() as MethodDeclarationSyntax; + var methodSyntax = methodSymbol.DeclaringSyntaxReferences[0].GetSyntax() as MethodDeclarationSyntax; // from the method syntax, check if there is a body and if there are statements in it if (methodSyntax?.Body?.Statements.Any() ?? true) { return; } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/EmptyMonoBehaviourMethods/EmptyMonoBehaviourMethodsCodeFixer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/EmptyMonoBehaviourMethods/EmptyMonoBehaviourMethodsCodeFixer.cs new file mode 100644 index 0000000..f5c7900 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/EmptyMonoBehaviourMethods/EmptyMonoBehaviourMethodsCodeFixer.cs @@ -0,0 +1,54 @@ +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace UnityEngineAnalyzer.EmptyMonoBehaviourMethods +{ + [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(EmptyMonoBehaviourMethodsCodeFixer)), Shared] + public class EmptyMonoBehaviourMethodsCodeFixer : CodeFixProvider + { + private const string _title = "Remove Method"; + + public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(DiagnosticIDs.EmptyMonoBehaviourMethod); + + public override FixAllProvider GetFixAllProvider() + { + return WellKnownFixAllProviders.BatchFixer; + } + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + + var diagnostic = context.Diagnostics.First(); + var diagnosticSpan = diagnostic.Location.SourceSpan; + + var declaration = root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf() + .OfType().First(); + + context.RegisterCodeFix( + CodeAction.Create( + _title, + c => RemveMethod(context.Document, declaration, c), + _title + ), + diagnostic + ); + } + + private async Task RemveMethod(Document document, MethodDeclarationSyntax declaration, CancellationToken cancellationToken) + { + var removedParent = declaration.Parent.RemoveNode(declaration, SyntaxRemoveOptions.KeepNoTrivia); + var oldRoot = await document.GetSyntaxRootAsync(cancellationToken); + var newRoot = oldRoot.ReplaceNode(declaration.Parent, removedParent); + + return document.WithSyntaxRoot(newRoot); + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/FindMethodsInUpdate/DoNotUseFindMethodsInUpdateResources.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/FindMethodsInUpdate/DoNotUseFindMethodsInUpdateResources.Designer.cs index 7f9db7c..e18cf8c 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/FindMethodsInUpdate/DoNotUseFindMethodsInUpdateResources.Designer.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/FindMethodsInUpdate/DoNotUseFindMethodsInUpdateResources.Designer.cs @@ -1,10 +1,10 @@ //------------------------------------------------------------------------------ // -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 // -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 // //------------------------------------------------------------------------------ @@ -14,12 +14,12 @@ namespace UnityEngineAnalyzer.FindMethodsInUpdate { /// - /// A strongly-typed resource class, for looking up localized strings, etc. + /// 一个强类型的资源类,用于查找本地化的字符串等。 /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] @@ -34,7 +34,7 @@ internal DoNotUseFindMethodsInUpdateResources() { } /// - /// Returns the cached ResourceManager instance used by this class. + /// 返回此类使用的缓存的 ResourceManager 实例。 /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { @@ -48,8 +48,8 @@ internal DoNotUseFindMethodsInUpdateResources() { } /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. + /// 使用此强类型资源类,为所有资源查找 + /// 重写当前线程的 CurrentUICulture 属性。 /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { @@ -62,7 +62,7 @@ internal DoNotUseFindMethodsInUpdateResources() { } /// - /// Looks up a localized string similar to Using Find or GetComponent in Update methods can impact performance. Cache the result on Start or Awake methods. + /// 查找类似 Using Find or GetComponent in Update methods can impact performance. Cache the result on Start or Awake methods 的本地化字符串。 /// internal static string Description { get { @@ -71,7 +71,7 @@ internal static string Description { } /// - /// Looks up a localized string similar to The method {0} is called from {1}.{2} which could cause performance problems. Cache the result from {0} in Start or Awake instead.. + /// 查找类似 The method {0} is called from {1}.{2} which could cause performance problems. Cache the result from {0} in Start or Awake instead. 的本地化字符串。 /// internal static string MessageFormat { get { @@ -80,7 +80,7 @@ internal static string MessageFormat { } /// - /// Looks up a localized string similar to The method {0}.{1} calls {2} which eventually calls {3} which could impact performance. Cache the result from {3} in Start or Awake instead.. + /// 查找类似 The method {0}.{1} calls {2} which eventually calls {3} which could impact performance. Cache the result from {3}'s {2} in Start or Awake instead. 的本地化字符串。 /// internal static string MessageFormatRecursive { get { @@ -89,7 +89,7 @@ internal static string MessageFormatRecursive { } /// - /// Looks up a localized string similar to Cache the result of Find or GetComponent in Start or Awake.. + /// 查找类似 Cache the result of Find or GetComponent in Start or Awake. 的本地化字符串。 /// internal static string Title { get { diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/FindMethodsInUpdate/DoNotUseFindMethodsInUpdateResources.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/FindMethodsInUpdate/DoNotUseFindMethodsInUpdateResources.resx index f263459..980a5ed 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/FindMethodsInUpdate/DoNotUseFindMethodsInUpdateResources.resx +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/FindMethodsInUpdate/DoNotUseFindMethodsInUpdateResources.resx @@ -126,7 +126,7 @@ The format-able message the diagnostic displays. - The method {0}.{1} calls {2} which eventually calls {3} which could impact performance. Cache the result from {3} in Start or Awake instead. + The method {0}.{1} calls {2} which eventually calls {3} which could impact performance. Cache the result from {3}'s {2} in Start or Awake instead. Cache the result of Find or GetComponent in Start or Awake. diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/ForEachInUpdate/DoNotUseForEachInUpdate.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/ForEachInUpdate/DoNotUseForEachInUpdate.cs index 06c9321..9ac340d 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/ForEachInUpdate/DoNotUseForEachInUpdate.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/ForEachInUpdate/DoNotUseForEachInUpdate.cs @@ -23,6 +23,8 @@ public static void AnalyzeClassSyntax(SyntaxNodeAnalysisContext context) { var monoBehaviourInfo = new MonoBehaviourInfo(context); + + var searched = new Dictionary(); monoBehaviourInfo.ForEachUpdateMethod((updateMethod) => { diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNoGCAllocInUpdateAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNoGCAllocInUpdateAnalyzer.cs new file mode 100644 index 0000000..0c33c87 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNoGCAllocInUpdateAnalyzer.cs @@ -0,0 +1,204 @@ +using System; +using System.Collections.Generic; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; + +namespace UnityEngineAnalyzer.GCAlloc +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + /// DoNotGCAllocInUpdateAnalyzer currently is conservative, + /// which means, if the ObjectCreationExpressionSyntax is inside a branch (if/switch), + /// then this analyzer will NOT report diagnose. + public sealed class DoNotGCAllocInUpdateAnalyzer : DiagnosticAnalyzer + { + + private Dictionary _indirectCallers; + + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create( + DiagnosticDescriptors.DoNotGCAllocnInUpdate, + DiagnosticDescriptors.DoNotGCAllocnInUpdateRecursive); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeClassSyntax, SyntaxKind.ClassDeclaration); + } + + public void AnalyzeClassSyntax(SyntaxNodeAnalysisContext context) + { + var monoBehaviourInfo = new MonoBehaviourInfo(context); + + var searched = new Dictionary(); + _indirectCallers = new Dictionary(); + + monoBehaviourInfo.ForEachUpdateMethod((updateMethod) => + { + //Debug.WriteLine("Found an update call! " + updateMethod); + + var results = SearchForTargetExpression(context, updateMethod, searched, true); + + foreach (var oneResult in results) + { + if (!_indirectCallers.ContainsKey(oneResult)) + { + var diagnostic = Diagnostic.Create(DiagnosticDescriptors.DoNotGCAllocnInUpdate, + oneResult.GetLocation(), oneResult, monoBehaviourInfo.ClassName, updateMethod.Identifier); + context.ReportDiagnostic(diagnostic); + } + else + { + var endPoint = _indirectCallers[oneResult]; + + var diagnostic = Diagnostic.Create(DiagnosticDescriptors.DoNotGCAllocnInUpdateRecursive, + oneResult.GetLocation(), monoBehaviourInfo.ClassName, updateMethod.Identifier, oneResult, endPoint); + context.ReportDiagnostic(diagnostic); + } + + } + }); + } + + //TODO: Try to simplify this method - it's very hard to follow + private IEnumerable SearchForTargetExpression(SyntaxNodeAnalysisContext context, + MethodDeclarationSyntax method, IDictionary searchedSymbol, bool isRoot) + { + var targetExps = method.DescendantNodes().OfType(); + foreach (var oneTargetExp in targetExps) + { + /* + var oneTargetIdentifierExps = oneTargetExp.DescendantNodes().OfType(); + foreach(var oneTargetIdentifierExp in oneTargetIdentifierExps) + { + } + */ + + bool isContainedByBranch = false; + + SyntaxNode parent = oneTargetExp; + while (parent != method) + { + if (parent is IfStatementSyntax || parent is SwitchStatementSyntax) + { + isContainedByBranch = true; + break; + } + + parent = parent.Parent; + } + + if (isContainedByBranch) + { + continue; + } + + + SymbolInfo oneSymbolInfo; + if (!context.TryGetSymbolInfo(oneTargetExp, out oneSymbolInfo)) + { + continue; + } + + var targetSymbol = oneSymbolInfo.Symbol as IMethodSymbol; + if (targetSymbol != null) + { + if (searchedSymbol.ContainsKey(targetSymbol)) + { + if (searchedSymbol[targetSymbol]) + { + yield return (ExpressionSyntax)oneTargetExp; + } + } + else + { + if (targetSymbol.ReceiverType.IsReferenceType) + { + searchedSymbol.Add(targetSymbol, true); + yield return oneTargetExp; + } + } + } + } + + + var invocationExps = method.DescendantNodes().OfType(); + foreach (var oneInvocationExp in invocationExps) + { + SymbolInfo oneSymbolInfo; + if (!context.TryGetSymbolInfo(oneInvocationExp, out oneSymbolInfo)) + { + continue; + } + + + bool isContainedByBranch = false; + SyntaxNode parent = oneInvocationExp; + while (parent != method) + { + if (parent is IfStatementSyntax || parent is SwitchStatementSyntax) + { + isContainedByBranch = true; + break; + } + + parent = parent.Parent; + } + if (isContainedByBranch) + { + continue; + } + + var methodSymbol = oneSymbolInfo.Symbol as IMethodSymbol; + + if (methodSymbol != null) + { + if (searchedSymbol.ContainsKey(methodSymbol)) + { + if (searchedSymbol[methodSymbol]) + { + yield return oneInvocationExp; + } + } + else + { + var methodDeclarations = methodSymbol.DeclaringSyntaxReferences; + searchedSymbol.Add(methodSymbol, false); //let's assume there won't be any calls + + foreach (var methodDeclaration in methodDeclarations) + { + var theMethodSyntax = methodDeclaration.GetSyntax() as MethodDeclarationSyntax; + + if (theMethodSyntax != null) + { + var childResults = SearchForTargetExpression(context, theMethodSyntax, searchedSymbol, false); + + if (childResults != null && childResults.Any()) + { + searchedSymbol[methodSymbol] = true; //update the searched dictionary with new info + + if (isRoot) + { + _indirectCallers.Add(oneInvocationExp, childResults.First()); + } + + yield return oneInvocationExp; + break; + } + } + } + } + } + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeAnalyzer.cs new file mode 100644 index 0000000..8f85033 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeAnalyzer.cs @@ -0,0 +1,89 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using System.Collections.Immutable; + +namespace UnityEngineAnalyzer.GCAlloc +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class DoNotBoxWhenInvokeAnalyzer : DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.DoNotBoxWhenInvoke); + + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.InvocationExpression); + } + + private static void AnalyzeNode(SyntaxNodeAnalysisContext context) + { + var invocation = context.Node as InvocationExpressionSyntax; + + if (invocation?.ArgumentList == null || invocation.ArgumentList.Arguments.Count == 0) + { + return; + } + + var symbolInfo = context.SemanticModel.GetSymbolInfo(invocation); + var methodSymbol = symbolInfo.Symbol as IMethodSymbol; + + bool isConditional = false; + foreach(var oneAttribute in methodSymbol.GetAttributes()) + { + if(oneAttribute.AttributeClass.Name == "ConditionalAttribute" && oneAttribute.AttributeClass.ContainingNamespace.Name == "Diagnostics") + { + isConditional = true; + break; + } + + } + + if(isConditional) + { + return; + } + + if(methodSymbol.Parameters == null || methodSymbol.Parameters.Length == 0) + { + return; + } + + var lastParameter = methodSymbol.Parameters[methodSymbol.Parameters.Length - 1]; + if (lastParameter.IsParams) + { + if (lastParameter.Type is IArrayTypeSymbol arrSymbol && arrSymbol.ElementType.IsValueType) + return; + } + + for(int i = 0; i < methodSymbol.Parameters.Length && i < invocation.ArgumentList.Arguments.Count; ++i) + { + var oneArgSyntax = invocation.ArgumentList.Arguments[i]; + if(oneArgSyntax == null) + { + continue; + } + var oneArgType = context.SemanticModel.GetTypeInfo(oneArgSyntax.Expression); + if(oneArgType.Type == null) + { + continue; + } + + var oneParamType = methodSymbol.Parameters[i]; + + if(oneParamType.Type == null) + { + return; + } + + if(oneArgType.Type.IsValueType && oneParamType.Type.IsReferenceType) + { + var diagnostic = Diagnostic.Create(DiagnosticDescriptors.DoNotBoxWhenInvoke, oneArgSyntax.Expression.GetLocation(), + methodSymbol.ContainingType.Name, methodSymbol.Name, oneParamType.Name, oneParamType.Type.ToString(), + oneArgSyntax.Expression.ToString(), oneArgType.Type.ToString()); + context.ReportDiagnostic(diagnostic); + } + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeResource.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeResource.Designer.cs new file mode 100644 index 0000000..98a9267 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeResource.Designer.cs @@ -0,0 +1,91 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace UnityEngineAnalyzer.GCAlloc { + using System; + using System.Reflection; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class DoNotBoxWhenInvokeResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal DoNotBoxWhenInvokeResource() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityEngineAnalyzer.GCAlloc.DoNotBoxWhenInvokeResource", typeof(DoNotBoxWhenInvokeResource).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 使用此强类型资源类,为所有资源查找 + /// 重写当前线程的 CurrentUICulture 属性。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 Avoid boxing when invoke 的本地化字符串。 + /// + internal static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + /// + /// 查找类似 Method {0}.{1}'s parameter "{2}"'s type "{3}" is reference type, but the argument "{4}"'s type "{5}" is value type, which causes boxing and gc allocation. 的本地化字符串。 + /// + internal static string MessageFormat { + get { + return ResourceManager.GetString("MessageFormat", resourceCulture); + } + } + + /// + /// 查找类似 Avoid boxing when invoke 的本地化字符串。 + /// + internal static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeResource.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeResource.resx new file mode 100644 index 0000000..5514b0a --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotBoxWhenInvokeResource.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Avoid boxing when invoke + An optional longer localizable description of the diagnostic. + + + Method {0}.{1}'s parameter "{2}"'s type "{3}" is reference type, but the argument "{4}"'s type "{5}" is value type, which causes boxing and gc allocation. + The format-able message the diagnostic displays. + + + Avoid boxing when invoke + The title of the diagnostic. + + \ No newline at end of file diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotGCAllocInUpdateResources.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotGCAllocInUpdateResources.Designer.cs new file mode 100644 index 0000000..531a9c4 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotGCAllocInUpdateResources.Designer.cs @@ -0,0 +1,100 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace UnityEngineAnalyzer.GCAlloc { + using System; + using System.Reflection; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class DoNotGCAllocInUpdateResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal DoNotGCAllocInUpdateResources() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityEngineAnalyzer.GCAlloc.DoNotGCAllocInUpdateResources", typeof(DoNotGCAllocInUpdateResources).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 使用此强类型资源类,为所有资源查找 + /// 重写当前线程的 CurrentUICulture 属性。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 GC allocation in Update methods can impact performance. Use pool or data-member to cache your data. 的本地化字符串。 + /// + internal static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + /// + /// 查找类似 {0} makes GC allocation from {1}.{2} which could cause performance problems. Use pool or data-member to cache your data. 的本地化字符串。 + /// + internal static string MessageFormat { + get { + return ResourceManager.GetString("MessageFormat", resourceCulture); + } + } + + /// + /// 查找类似 The method {0}.{1} calls {2} which eventually calls {3} whose GC allocation could impact performance. Use pool or data-member to cache your data from {3} 的本地化字符串。 + /// + internal static string MessageFormatRecursive { + get { + return ResourceManager.GetString("MessageFormatRecursive", resourceCulture); + } + } + + /// + /// 查找类似 Avoid GC allocation in Update methods 的本地化字符串。 + /// + internal static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotGCAllocInUpdateResources.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotGCAllocInUpdateResources.resx new file mode 100644 index 0000000..a12743f --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/GCAlloc/DoNotGCAllocInUpdateResources.resx @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + GC allocation in Update methods can impact performance. Use pool or data-member to cache your data. + An optional longer localizable description of the diagnostic. + + + {0} makes GC allocation from {1}.{2} which could cause performance problems. Use pool or data-member to cache your data. + The format-able message the diagnostic displays. + + + The method {0}.{1} calls {2} which eventually calls {3} whose GC allocation could impact performance. Use pool or data-member to cache your data from {3} + + + Avoid GC allocation in Update methods + The title of the diagnostic. + + \ No newline at end of file diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/DoNotUseEnumTypeParameterAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/DoNotUseEnumTypeParameterAnalyzer.cs new file mode 100644 index 0000000..0423e07 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/DoNotUseEnumTypeParameterAnalyzer.cs @@ -0,0 +1,56 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using System.Collections.Immutable; +using System.Linq; + +namespace UnityEngineAnalyzer.Generics +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class DoNotUseEnumTypeParameterAnalyzer : DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.DoNotUseEnumTypeParameter); + + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.VariableDeclaration); + } + + private static void AnalyzeNode(SyntaxNodeAnalysisContext context) + { + var targetSyntax = context.Node as VariableDeclarationSyntax; + if (targetSyntax == null) + { + return; + } + + System.Collections.Generic.HashSet checkedIdSyntax = new System.Collections.Generic.HashSet(); + + var typeArgumentLists = targetSyntax.DescendantNodes().OfType(); + foreach(var oneTypeArgSyntax in typeArgumentLists) + { + foreach(var oneIdSyntax in oneTypeArgSyntax.DescendantNodes().OfType()) + { + if(!checkedIdSyntax.Contains(oneIdSyntax)) + { + checkedIdSyntax.Add(oneIdSyntax); + + var oneIdSymbolInfo = context.SemanticModel.GetSymbolInfo(oneIdSyntax); + var oneIdSymbolraw = oneIdSymbolInfo.Symbol as INamedTypeSymbol; + if (oneIdSymbolraw is INamedTypeSymbol oneIdSymbol && + oneIdSymbol.BaseType != null && + oneIdSymbol.BaseType.Name == "Enum" && + oneIdSymbol.BaseType.ContainingNamespace.Name == "System") + { + var diagnostic = Diagnostic.Create(DiagnosticDescriptors.DoNotUseEnumTypeParameter, + oneIdSyntax.GetLocation(), oneIdSyntax.ToString()); + context.ReportDiagnostic(diagnostic); + } + } + } + } + + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/DoNotUseEnumTypeParameterResource.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/DoNotUseEnumTypeParameterResource.Designer.cs new file mode 100644 index 0000000..0b1b275 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/DoNotUseEnumTypeParameterResource.Designer.cs @@ -0,0 +1,91 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace UnityEngineAnalyzer.Generics { + using System; + using System.Reflection; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class DoNotUseEnumTypeParameterResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal DoNotUseEnumTypeParameterResource() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityEngineAnalyzer.Generics.DoNotUseEnumTypeParameterResource", typeof(DoNotUseEnumTypeParameterResource).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 使用此强类型资源类,为所有资源查找 + /// 重写当前线程的 CurrentUICulture 属性。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 Prefer to use integer type (short or int) as TypeParameter, and convert it between Enum type. This can reduce code size, and you don't need to pass additional IEqualityComparer for your Enum for your container. 的本地化字符串。 + /// + internal static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + /// + /// 查找类似 Change {0} to integer type (short or int) as TypeParameter 的本地化字符串。 + /// + internal static string MessageFormat { + get { + return ResourceManager.GetString("MessageFormat", resourceCulture); + } + } + + /// + /// 查找类似 Use integer type instead of Enum as TypeParameter 的本地化字符串。 + /// + internal static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/DoNotUseEnumTypeParameterResource.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/DoNotUseEnumTypeParameterResource.resx new file mode 100644 index 0000000..869e6a7 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/DoNotUseEnumTypeParameterResource.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Prefer to use integer type (short or int) as TypeParameter, and convert it between Enum type. This can reduce code size, and you don't need to pass additional IEqualityComparer for your Enum for your container. + An optional longer localizable description of the diagnostic. + + + Change {0} to integer type (short or int) as TypeParameter + The format-able message the diagnostic displays. + + + Use integer type instead of Enum as TypeParameter + The title of the diagnostic. + + \ No newline at end of file diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/EnumShouldManualSetMemberValue.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/EnumShouldManualSetMemberValue.cs new file mode 100644 index 0000000..66560eb --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/EnumShouldManualSetMemberValue.cs @@ -0,0 +1,48 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using System.Collections.Immutable; +using System.Linq; + +namespace UnityEngineAnalyzer.Generics +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class EnumShouldManualSetMemberValue : DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.EnumShouldManualSetMemberValue); + + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.EnumMemberDeclaration); + } + + + private static void AnalyzeNode(SyntaxNodeAnalysisContext context) + { + var targetSyntax = context.Node as EnumMemberDeclarationSyntax; + if (targetSyntax == null) + { + return; + } + + bool hasEqualClause = false; + foreach(var oneSyntax in targetSyntax.DescendantNodes().OfType()) + { + if(oneSyntax != null) + { + hasEqualClause = true; + break; + } + } + + if(!hasEqualClause) + { + var diagnostic = Diagnostic.Create(DiagnosticDescriptors.EnumShouldManualSetMemberValue, + targetSyntax.GetLocation(), targetSyntax.ToString()); + context.ReportDiagnostic(diagnostic); + } + + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/EnumShouldManualSetMemberValueResource.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/EnumShouldManualSetMemberValueResource.Designer.cs new file mode 100644 index 0000000..6e1ba57 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/EnumShouldManualSetMemberValueResource.Designer.cs @@ -0,0 +1,91 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace UnityEngineAnalyzer.Generics { + using System; + using System.Reflection; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class EnumShouldManualSetMemberValueResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal EnumShouldManualSetMemberValueResource() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityEngineAnalyzer.Generics.EnumShouldManualSetMemberValueResource", typeof(EnumShouldManualSetMemberValueResource).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 使用此强类型资源类,为所有资源查找 + /// 重写当前线程的 CurrentUICulture 属性。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 If this enum is used for prefab or excel, you must manually set enum member value. 的本地化字符串。 + /// + internal static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + /// + /// 查找类似 enum member "{0}" should be manually set value 的本地化字符串。 + /// + internal static string MessageFormat { + get { + return ResourceManager.GetString("MessageFormat", resourceCulture); + } + } + + /// + /// 查找类似 Enum member value required 的本地化字符串。 + /// + internal static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/EnumShouldManualSetMemberValueResource.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/EnumShouldManualSetMemberValueResource.resx new file mode 100644 index 0000000..837e742 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Generics/EnumShouldManualSetMemberValueResource.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + If this enum is used for prefab or excel, you must manually set enum member value. + An optional longer localizable description of the diagnostic. + + + enum member "{0}" should be manually set value + The format-able message the diagnostic displays. + + + Enum member value required + The title of the diagnostic. + + \ No newline at end of file diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/IL2CPP/UnsealedDerivedClassAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/IL2CPP/UnsealedDerivedClassAnalyzer.cs index 0d5501f..4dd4ad2 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/IL2CPP/UnsealedDerivedClassAnalyzer.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/IL2CPP/UnsealedDerivedClassAnalyzer.cs @@ -28,7 +28,7 @@ private void AnalyzeClassSyntax(SyntaxNodeAnalysisContext context) if (method.IsOverriden() && !method.IsSealed()) { - var diagnostic = Diagnostic.Create(SupportedDiagnostics.First(), method.GetLocation(), + var diagnostic = Diagnostic.Create(SupportedDiagnostics.First(), method.Identifier.GetLocation(), method.Identifier.ToString(), classDeclaration.Identifier.ToString()); context.ReportDiagnostic(diagnostic); diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/IL2CPP/UnsealedDerivedClassCodeFixer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/IL2CPP/UnsealedDerivedClassCodeFixer.cs new file mode 100644 index 0000000..32b8144 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/IL2CPP/UnsealedDerivedClassCodeFixer.cs @@ -0,0 +1,65 @@ +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace UnityEngineAnalyzer.IL2CPP +{ + [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(UnsealedDerivedClassCodeFixer)), Shared] + public class UnsealedDerivedClassCodeFixer:CodeFixProvider + { + private const string _title = "Add sealed Modifier"; + + public override ImmutableArray FixableDiagnosticIds =>ImmutableArray.Create(DiagnosticIDs.UnsealedDerivedClass); + + public override FixAllProvider GetFixAllProvider() + { + return WellKnownFixAllProviders.BatchFixer; + } + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + + var diagnostic = context.Diagnostics.First(); + var diagnosticSpan = diagnostic.Location.SourceSpan; + + var declarations = root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf() + .OfType().First(); + + context.RegisterCodeFix( + CodeAction.Create( + _title, + c=>AddModifier(context.Document, declarations, c), + _title + ), + diagnostic + ); + } + + private async Task AddModifier(Document contextDocument, MethodDeclarationSyntax declaration, CancellationToken cancellationToken) + { + var firstToken = declaration.GetFirstToken(); + var leadingTrivia = firstToken.LeadingTrivia; + //var trimmedDeclaration = + // declaration.ReplaceToken(firstToken, firstToken.WithLeadingTrivia(SyntaxTriviaList.Empty)); + + var sealedToken = SyntaxFactory.Token(leadingTrivia, SyntaxKind.SealedKeyword, + SyntaxFactory.TriviaList(SyntaxFactory.ElasticMarker)); + + var newModifiers = declaration.Modifiers.Insert(declaration.Modifiers.Count >= 1 ? 1 : 0, sealedToken); + var newDecolation = declaration.WithModifiers(newModifiers); + + var oldRoot = await contextDocument.GetSyntaxRootAsync(cancellationToken); + var newRoot = oldRoot.ReplaceNode(declaration, newDecolation); + + return contextDocument.WithSyntaxRoot(newRoot); + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/IgnoringWithCommit.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/IgnoringWithCommit.cs new file mode 100644 index 0000000..c169817 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/IgnoringWithCommit.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; + +namespace UnityEngineAnalyzer +{ + internal static class IgnoringWithCommit + { + private readonly static Regex IgnoreFormatRegex = new Regex(@"//\s*Ignore CA:\s*(?[0-9a-zA-Z]+)(,\s*(?[0-9a-zA-Z]+))"); + + //public static bool ShouldIgnore(SyntaxNode node, string checkId) + //{ + + //} + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Lambda/LambdaAnalyzerResources.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Lambda/LambdaAnalyzerResources.Designer.cs new file mode 100644 index 0000000..14f300b --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Lambda/LambdaAnalyzerResources.Designer.cs @@ -0,0 +1,91 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace UnityEngineAnalyzer.Lambda { + using System; + using System.Reflection; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class LambdaAnalyzerResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal LambdaAnalyzerResources() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityEngineAnalyzer.Lambda.LambdaAnalyzerResources", typeof(LambdaAnalyzerResources).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 使用此强类型资源类,为所有资源查找 + /// 重写当前线程的 CurrentUICulture 属性。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 A reference of variables in outter scope of a lambda expression will make a gerenation of a closure. It's better to avoid this in order to decrease code size, memory and gc cost. 的本地化字符串。 + /// + internal static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + /// + /// 查找类似 A outter variable is found in a lambda expression, which may cause a closure object allocated. 的本地化字符串。 + /// + internal static string LambdaClosureAnalyzer { + get { + return ResourceManager.GetString("LambdaClosureAnalyzer", resourceCulture); + } + } + + /// + /// 查找类似 Check local variable in a lambda expression. 的本地化字符串。 + /// + internal static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Lambda/LambdaAnalyzerResources.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/Lambda/LambdaAnalyzerResources.resx new file mode 100644 index 0000000..363907a --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Lambda/LambdaAnalyzerResources.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + A reference of variables in outter scope of a lambda expression will make a gerenation of a closure. It's better to avoid this in order to decrease code size, memory and gc cost. + + + A outter variable is found in a lambda expression, which may cause a closure object allocated. + + + Check local variable in a lambda expression. + + diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Lambda/LambdaClosureAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Lambda/LambdaClosureAnalyzer.cs new file mode 100644 index 0000000..7a7fd2e --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Lambda/LambdaClosureAnalyzer.cs @@ -0,0 +1,40 @@ +using System.Collections.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace UnityEngineAnalyzer.Language +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class LambdaClosureAnalyzer : DiagnosticAnalyzer + { + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalysisLambda, SyntaxKind.SimpleLambdaExpression, SyntaxKind.ParenthesizedLambdaExpression); + } + + public override ImmutableArray SupportedDiagnostics => + ImmutableArray.Create(DiagnosticDescriptors.LambdaClosure); + + private void AnalysisLambda(SyntaxNodeAnalysisContext context) + { + var lambdaNode = (LambdaExpressionSyntax) context.Node; + var des = context.SemanticModel.GetSymbolInfo(lambdaNode).Symbol; + + var childrenNodes = + from node in lambdaNode.DescendantNodes().OfType() + let symbol = context.SemanticModel.GetSymbolInfo(node).Symbol + where symbol.Kind == SymbolKind.Local && symbol.ContainingSymbol != des + select node; + + foreach (var node in childrenNodes) + { + var diagnostic = + Diagnostic.Create(SupportedDiagnostics.First(), node.GetLocation(), node.Identifier.Text); + context.ReportDiagnostic(diagnostic); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructAnalyzerResources.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructAnalyzerResources.Designer.cs new file mode 100644 index 0000000..9d728bb --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructAnalyzerResources.Designer.cs @@ -0,0 +1,109 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace UnityEngineAnalyzer.Language { + using System; + using System.Reflection; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class StructAnalyzerResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal StructAnalyzerResources() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityEngineAnalyzer.Language.StructAnalyzerResources", typeof(StructAnalyzerResources).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 使用此强类型资源类,为所有资源查找 + /// 重写当前线程的 CurrentUICulture 属性。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 Inappropriate Struct implementation can impact performance 的本地化字符串。 + /// + internal static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + /// + /// 查找类似 Struct {0} should implement IEquatable to avoid boxing in case Equals(object o) is called. 的本地化字符串。 + /// + internal static string ShouldImplmentIEquatable { + get { + return ResourceManager.GetString("ShouldImplmentIEquatable", resourceCulture); + } + } + + /// + /// 查找类似 Struct {0} should override Equals(object o) because its default implementation may use reflection and is not efficient 的本地化字符串。 + /// + internal static string ShouldOverrideEquals { + get { + return ResourceManager.GetString("ShouldOverrideEquals", resourceCulture); + } + } + + /// + /// 查找类似 Struct {0} should override GetHashCode() because its default implementation may use reflection and is not efficient 的本地化字符串。 + /// + internal static string ShouldOverrideGetHashCode { + get { + return ResourceManager.GetString("ShouldOverrideGetHashCode", resourceCulture); + } + } + + /// + /// 查找类似 Inappropriate Struct implementation 的本地化字符串。 + /// + internal static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructAnalyzerResources.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructAnalyzerResources.resx new file mode 100644 index 0000000..4dace20 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructAnalyzerResources.resx @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Inappropriate Struct implementation can impact performance + An optional longer localizable description of the diagnostic. + + + Struct {0} should implement IEquatable to avoid boxing in case Equals(object o) is called. + The format-able message the diagnostic displays. + + + Struct {0} should override Equals(object o) because its default implementation may use reflection and is not efficient + + + Struct {0} should override GetHashCode() because its default implementation may use reflection and is not efficient + + + Inappropriate Struct implementation + The title of the diagnostic. + + \ No newline at end of file diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructImplementationAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructImplementationAnalyzer.cs new file mode 100644 index 0000000..32e5a04 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructImplementationAnalyzer.cs @@ -0,0 +1,79 @@ +using System.Collections.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace UnityEngineAnalyzer.Language +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class StructImplementAnalyzer : DiagnosticAnalyzer + { + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeClassSyntax, SyntaxKind.StructDeclaration); + } + + private void AnalyzeClassSyntax(SyntaxNodeAnalysisContext context) + { + CheckStructShouldImplementIEquatable(context); + } + + + private void CheckStructShouldImplementIEquatable(SyntaxNodeAnalysisContext context) + { + var declSyntax = (StructDeclarationSyntax)context.Node; + var declSymbol = context.SemanticModel.GetDeclaredSymbol(declSyntax) as INamedTypeSymbol; + + bool foundIEquatableOfStruct = false; + + if (declSyntax.BaseList != null) + { + + var baseList = declSyntax.BaseList.Types; + var baseTypesSyntax = baseList.OfType(); + + + foreach (var oneBaseTypeSyntax in baseTypesSyntax) + { + var baseTypeName = oneBaseTypeSyntax.ChildNodes(); + foreach (var oneName in baseTypeName) + { + if (oneName is GenericNameSyntax) + { + var genericSymbolInfo = context.SemanticModel.GetSymbolInfo(oneName); + var genericSymbol = genericSymbolInfo.Symbol as INamedTypeSymbol; + if (genericSymbol != null && genericSymbol.ContainingNamespace.Name == "System" && + genericSymbol.Name == "IEquatable") + { + foreach (var oneParameterType in genericSymbol.TypeArguments) + { + if (oneParameterType == declSymbol) + { + foundIEquatableOfStruct = true; + } + } + } + } + } + } + } + + if (!foundIEquatableOfStruct) + { + + var diagnostic = Diagnostic.Create(SupportedDiagnostics.First(), declSyntax.Identifier.GetLocation(), + declSyntax.Identifier.ToString()); + + context.ReportDiagnostic(diagnostic); + } + } + + + public override ImmutableArray SupportedDiagnostics => + ImmutableArray.Create( + DiagnosticDescriptors.StructShouldImplementIEquatable + ); + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructOverrideEqualsObjectAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructOverrideEqualsObjectAnalyzer.cs new file mode 100644 index 0000000..c164e45 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructOverrideEqualsObjectAnalyzer.cs @@ -0,0 +1,68 @@ +using System.Collections.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace UnityEngineAnalyzer.Language +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class StructOverrideEqualsObjectAnalyzer : DiagnosticAnalyzer + { + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeClassSyntax, SyntaxKind.StructDeclaration); + } + + private void AnalyzeClassSyntax(SyntaxNodeAnalysisContext context) + { + CheckStructShouldOverride(context); + } + + + private void CheckStructShouldOverride(SyntaxNodeAnalysisContext context) + { + var declSyntax = (StructDeclarationSyntax)context.Node; + + bool foundOverrideEquals = false; + + foreach (var oneChild in declSyntax.ChildNodes()) + { + if(oneChild is MethodDeclarationSyntax) + { + var oneMethodSymbol = context.SemanticModel.GetDeclaredSymbol(oneChild) as IMethodSymbol; + if(oneMethodSymbol.IsOverride) + { + if(oneMethodSymbol.Parameters != null && oneMethodSymbol.Parameters.Length == 1 && + oneMethodSymbol.Name == "Equals") + { + var oneParamType = oneMethodSymbol.Parameters[0].Type; + if (oneParamType.ContainingNamespace.Name == "System" && oneParamType.Name.ToLower() == "object") + { + foundOverrideEquals = true; + break; + } + } + + } + } + } + + + if(!foundOverrideEquals) + { + var diagnostic = Diagnostic.Create(SupportedDiagnostics[0], declSyntax.Identifier.GetLocation(), + declSyntax.Identifier.ToString()); + + context.ReportDiagnostic(diagnostic); + } + } + + + public override ImmutableArray SupportedDiagnostics => + ImmutableArray.Create( + DiagnosticDescriptors.StructShouldOverrideEquals + ); + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructOverrideGetHashCodeAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructOverrideGetHashCodeAnalyzer.cs new file mode 100644 index 0000000..ad7e4df --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/Language/StructOverrideGetHashCodeAnalyzer.cs @@ -0,0 +1,63 @@ +using System.Collections.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace UnityEngineAnalyzer.Language +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class StructOverrideGetHashCodeAnalyzer : DiagnosticAnalyzer + { + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeClassSyntax, SyntaxKind.StructDeclaration); + } + + private void AnalyzeClassSyntax(SyntaxNodeAnalysisContext context) + { + CheckStructShouldOverride(context); + } + + + private void CheckStructShouldOverride(SyntaxNodeAnalysisContext context) + { + var declSyntax = (StructDeclarationSyntax)context.Node; + + bool foundOverridGetHashCode = false; + + foreach (var oneChild in declSyntax.ChildNodes()) + { + if(oneChild is MethodDeclarationSyntax) + { + var oneMethodSymbol = context.SemanticModel.GetDeclaredSymbol(oneChild) as IMethodSymbol; + if(oneMethodSymbol.IsOverride) + { + if ((oneMethodSymbol.Parameters == null || oneMethodSymbol.Parameters.Length == 0) && + oneMethodSymbol.Name == "GetHashCode") + { + foundOverridGetHashCode = true; + break; + } + } + } + } + + + if (!foundOverridGetHashCode) + { + var diagnostic = Diagnostic.Create(SupportedDiagnostics[0], declSyntax.Identifier.GetLocation(), + declSyntax.Identifier.ToString()); + + context.ReportDiagnostic(diagnostic); + } + } + + + public override ImmutableArray SupportedDiagnostics => + ImmutableArray.Create( + DiagnosticDescriptors.StructShouldOverrideGetHashCode + ); + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/LogicError/InfiniteRecursiveCallAnalyzer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/LogicError/InfiniteRecursiveCallAnalyzer.cs new file mode 100644 index 0000000..66bcba8 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/LogicError/InfiniteRecursiveCallAnalyzer.cs @@ -0,0 +1,234 @@ +using System; +using System.Collections.Generic; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; + +namespace UnityEngineAnalyzer.LogicError +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + /// InfiniteRecursiveCallAnalyzer currently is conservative, + /// which means, if the recursive call is inside a branch (if/switch), + /// then this analyzer will NOT report diagnose. + public sealed class InfiniteRecursiveCallAnalyzer : DiagnosticAnalyzer + { + + private Dictionary _directCallers; + private Dictionary _indirectCallers; + + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create( + DiagnosticDescriptors.InfiniteRecursiveCall, + DiagnosticDescriptors.InfiniteRecursiveCallRecursive); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeSymbol, SyntaxKind.ClassDeclaration); + } + + private void AnalyzeSymbol(SyntaxNodeAnalysisContext context) + { + var searched = new Dictionary(); + _directCallers = new Dictionary(); + _indirectCallers = new Dictionary(); + + List reported = new List(); + + foreach (var oneMethodDeclaration in context.Node.ChildNodes().OfType()) + { + var methodSymbol = context.SemanticModel.GetDeclaredSymbol(oneMethodDeclaration) as IMethodSymbol; + + + var results = SearchForTargetExpression(context, methodSymbol, oneMethodDeclaration, searched, true); + + foreach (var oneResult in results) + { + if (!reported.Contains((oneResult))) + { + if (_directCallers.ContainsKey(oneResult)) + { + var diagnostic = Diagnostic.Create(DiagnosticDescriptors.InfiniteRecursiveCall, + oneResult.GetLocation(), methodSymbol.ContainingType.Name, oneMethodDeclaration.Identifier); + context.ReportDiagnostic(diagnostic); + reported.Add(oneResult); + + } + else if (_indirectCallers.ContainsKey(oneResult)) + { + var endPoint = _indirectCallers[oneResult]; + + var diagnostic = Diagnostic.Create(DiagnosticDescriptors.InfiniteRecursiveCallRecursive, + oneResult.GetLocation(), methodSymbol.ContainingType.Name, oneMethodDeclaration.Identifier, oneResult); + context.ReportDiagnostic(diagnostic); + reported.Add(oneResult); + } + } + + } + } + + } + + + //TODO: Try to simplify this currMethodSyntax - it's very hard to follow + private IEnumerable SearchForTargetExpression(SyntaxNodeAnalysisContext context, + IMethodSymbol checkMethodSymbol, + MethodDeclarationSyntax currMethodSyntax, IDictionary searchedSymbol, bool isRoot) + { + foreach (var oneTargetExp in currMethodSyntax.DescendantNodes().OfType()) + { + /* + var oneTargetIdentifierExps = oneTargetExp.DescendantNodes().OfType(); + foreach(var oneTargetIdentifierExp in oneTargetIdentifierExps) + { + } + */ + + bool isContainedByBranch = false; + + SyntaxNode parent = oneTargetExp; + while (parent != currMethodSyntax) + { + if (parent is IfStatementSyntax || parent is SwitchStatementSyntax || parent is ForStatementSyntax || + parent is ForEachStatementSyntax || parent is WhileStatementSyntax) + { + isContainedByBranch = true; + break; + } + + parent = parent.Parent; + } + + if (isContainedByBranch) + { + continue; + } + + bool hasPrecedingReturn = false; + + foreach (var oneReturnSyntax in currMethodSyntax.DescendantNodes().OfType()) + { + if (oneReturnSyntax.SpanStart < oneTargetExp.SpanStart) + { + hasPrecedingReturn = true; + break; + } + } + if (hasPrecedingReturn) + { + continue; + } + + SymbolInfo oneSymbolInfo; + if (!context.TryGetSymbolInfo(oneTargetExp, out oneSymbolInfo)) + { + continue; + } + + var targetSymbol = oneSymbolInfo.Symbol as IMethodSymbol; + if (targetSymbol != null) + { + if (searchedSymbol.ContainsKey(targetSymbol)) + { + if (searchedSymbol[targetSymbol]) + { + yield return (ExpressionSyntax)oneTargetExp; + } + } + else + { + if (targetSymbol == checkMethodSymbol) + { + searchedSymbol.Add(targetSymbol, true); + if (isRoot) + { + _directCallers.Add(oneTargetExp, oneTargetExp); + } + yield return oneTargetExp; + } + } + } + } + + + var invocationExps = currMethodSyntax.DescendantNodes().OfType(); + foreach (var oneInvocationExp in invocationExps) + { + SymbolInfo oneSymbolInfo; + if (!context.TryGetSymbolInfo(oneInvocationExp, out oneSymbolInfo)) + { + continue; + } + + + bool isContainedByBranch = false; + SyntaxNode parent = oneInvocationExp; + while (parent != currMethodSyntax) + { + if (parent is IfStatementSyntax || parent is SwitchStatementSyntax) + { + isContainedByBranch = true; + break; + } + + parent = parent.Parent; + } + if (isContainedByBranch) + { + continue; + } + + var methodSymbol = oneSymbolInfo.Symbol as IMethodSymbol; + + if (methodSymbol != null) + { + if (searchedSymbol.ContainsKey(methodSymbol)) + { + if (searchedSymbol[methodSymbol]) + { + yield return oneInvocationExp; + } + } + else + { + var methodDeclarations = methodSymbol.DeclaringSyntaxReferences; + searchedSymbol.Add(methodSymbol, false); //let's assume there won't be any calls + + foreach (var methodDeclaration in methodDeclarations) + { + var theMethodSyntax = methodDeclaration.GetSyntax() as MethodDeclarationSyntax; + + if (theMethodSyntax != null) + { + var childResults = SearchForTargetExpression(context, checkMethodSymbol, theMethodSyntax, searchedSymbol, false); + + if (childResults != null && childResults.Any()) + { + searchedSymbol[methodSymbol] = true; //update the searched dictionary with new info + + if (isRoot) + { + _indirectCallers.Add(oneInvocationExp, childResults.First()); + } + + yield return oneInvocationExp; + break; + } + } + } + } + } + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/LogicError/InfiniteRecursiveCallResources.Designer.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/LogicError/InfiniteRecursiveCallResources.Designer.cs new file mode 100644 index 0000000..0da4656 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/LogicError/InfiniteRecursiveCallResources.Designer.cs @@ -0,0 +1,100 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace UnityEngineAnalyzer.LogicError { + using System; + using System.Reflection; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class InfiniteRecursiveCallResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal InfiniteRecursiveCallResources() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityEngineAnalyzer.LogicError.InfiniteRecursiveCallResources", typeof(InfiniteRecursiveCallResources).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 使用此强类型资源类,为所有资源查找 + /// 重写当前线程的 CurrentUICulture 属性。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 Infinite recursive loop detected 的本地化字符串。 + /// + internal static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + /// + /// 查找类似 The method {0}.{1}() calls {1}() again. This is an infinite recursive call. 的本地化字符串。 + /// + internal static string MessageFormat { + get { + return ResourceManager.GetString("MessageFormat", resourceCulture); + } + } + + /// + /// 查找类似 The method {0}.{1}() calls {2} which eventually calls {1}() again. This is an infinite recursive call. 的本地化字符串。 + /// + internal static string MessageFormatRecursive { + get { + return ResourceManager.GetString("MessageFormatRecursive", resourceCulture); + } + } + + /// + /// 查找类似 Infinite recursive loop 的本地化字符串。 + /// + internal static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + } +} diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/LogicError/InfiniteRecursiveCallResources.resx b/UnityEngineAnalyzer/UnityEngineAnalyzer/LogicError/InfiniteRecursiveCallResources.resx new file mode 100644 index 0000000..299c102 --- /dev/null +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/LogicError/InfiniteRecursiveCallResources.resx @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Infinite recursive loop detected + An optional longer localizable description of the diagnostic. + + + The method {0}.{1}() calls {1}() again. This is an infinite recursive call. + + + The method {0}.{1}() calls {2} which eventually calls {1}() again. This is an infinite recursive call. + + + Infinite recursive loop + The title of the diagnostic. + + \ No newline at end of file diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/MonoBehaviourInfo.cs b/UnityEngineAnalyzer/UnityEngineAnalyzer/MonoBehaviourInfo.cs index 7f5b981..9d7acce 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/MonoBehaviourInfo.cs +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/MonoBehaviourInfo.cs @@ -19,6 +19,17 @@ class MonoBehaviourInfo "FixedUpdate", "LateUpdate"); + private static readonly ImmutableHashSet MoreUpdateMethodNames = ImmutableHashSet.Create( + + "MoreFixedUpdate", + "MoreSlowFixedUpdate", + "MoreUpdate", + "MoreUpdate2", + "MoreUpdate3", + "MoreLateUpdate", + "MoreLateUpdate2" + ); + public MonoBehaviourInfo(SyntaxNodeAnalysisContext analysisContext) { _classDeclaration = analysisContext.Node as ClassDeclarationSyntax; @@ -34,10 +45,10 @@ public MonoBehaviourInfo(SyntaxNodeAnalysisContext analysisContext) public void ForEachUpdateMethod(Action callback) { + var methods = _classDeclaration.Members.OfType(); + if (this.IsMonoBehaviour()) { - var methods = _classDeclaration.Members.OfType(); - foreach (var method in methods) { if (UpdateMethodNames.Contains(method.Identifier.ValueText)) @@ -46,6 +57,17 @@ public void ForEachUpdateMethod(Action callback) } } } + + if (this.IsMoreLoopBehaviour()) + { + foreach (var method in methods) + { + if (MoreUpdateMethodNames.Contains(method.Identifier.ValueText)) + { + callback(method); + } + } + } } public bool IsMonoBehaviour() @@ -70,5 +92,32 @@ private static bool IsMonoBehavior(INamedTypeSymbol classDeclaration) return IsMonoBehavior(baseClass); //determine if the BaseClass extends mono behavior } + + + + public bool IsMoreLoopBehaviour() + { + return IsMonoBehavior(_classSymbol); + } + + private static bool IsMoreLoopBehaviour(INamedTypeSymbol classDeclaration) + { + if (classDeclaration.BaseType == null) + { + return false; + } + + var allInterfaces = classDeclaration.AllInterfaces; + foreach (var oneInterface in allInterfaces) + { + if (oneInterface.Name.Equals("IMoreLoopBehaviour")) + { + return true; + } + } + + return false; + + } } } diff --git a/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj b/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj index 4295573..2c7a6bf 100644 --- a/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj +++ b/UnityEngineAnalyzer/UnityEngineAnalyzer/UnityEngineAnalyzer.csproj @@ -74,10 +74,22 @@ True DoNotUseCoroutinesResources.resx + + True + True + DuplicatedDelegateDetectionResource.resx + + + + True + True + ShouldCacheDelegateResource.resx + + True True @@ -95,12 +107,59 @@ DoNotUseForEachInUpdateResources.resx + + + True + True + DoNotGCAllocInUpdateResources.resx + + + + True + True + DoNotBoxWhenInvokeResource.resx + + + + + True + True + EnumShouldManualSetMemberValueResource.resx + + + True + True + DoNotUseEnumTypeParameterResource.resx + + + True True UnsealedDerivedClassResources.resx + + + + True + True + LambdaAnalyzerResources.resx + + + + + + True + True + StructAnalyzerResources.resx + + + True + True + InfiniteRecursiveCallResources.resx + + True @@ -161,6 +220,15 @@ ResXFileCodeGenerator UseCompareTagResources.Designer.cs + + ResXFileCodeGenerator + DuplicatedDelegateDetectionResource.Designer.cs + Designer + + + ResXFileCodeGenerator + ShouldCacheDelegateResource.Designer.cs + ResXFileCodeGenerator EmptyMonoBehaviourMethodsResources.Designer.cs @@ -177,10 +245,39 @@ ResXFileCodeGenerator DoNotUseCoroutinesResources.Designer.cs + + ResXFileCodeGenerator + DoNotGCAllocInUpdateResources.Designer.cs + + + ResXFileCodeGenerator + DoNotBoxWhenInvokeResource.Designer.cs + + + ResXFileCodeGenerator + EnumShouldManualSetMemberValueResource.Designer.cs + + + ResXFileCodeGenerator + DoNotUseEnumTypeParameterResource.Designer.cs + ResXFileCodeGenerator UnsealedDerivedClassResources.Designer.cs + + ResXFileCodeGenerator + LambdaAnalyzerResources.Designer.cs + + + ResXFileCodeGenerator + StructAnalyzerResources.Designer.cs + Designer + + + ResXFileCodeGenerator + InfiniteRecursiveCallResources.Designer.cs + ResXFileCodeGenerator DoNotUseStringPropertyNamesResource.Designer.cs @@ -265,8 +362,12 @@ ..\..\packages\System.Reflection.Metadata.1.0.21\lib\portable-net45+win8\System.Reflection.Metadata.dll False + + ..\..\..\..\..\..\..\..\Program Files\Unity2017.1.3p2\Editor\Data\Managed\UnityEngine.dll + + diff --git a/readme.md b/readme.md index 175809c..5fae71d 100644 --- a/readme.md +++ b/readme.md @@ -1,9 +1,38 @@ UnityEngineAnalyzer =================== + +![](https://raw.githubusercontent.com/donaldwuid/UnityEngineAnalyzer/master/Documents/usage.png) + UnityEngineAnalyzer is a set of Roslyn analyzers that aim to detect common problems in Unity3D C# code. Unity3D makes it easy for us to make cross platform games, but there are hidden rules about performance and AOT, which might only come with experience, testing or reading the forums. It is hoped that such problems can be caught before compilation. +Auto Add UnityEngineAnalyzer to your Unity project +--------------------- +Each time your refresh your unity project, the UnityEngineAnalyzer may be removed from the solution. +To auto add it to your solution, you can: +1. copy Editor/AutoAddUnityEngineAnalyzer.cs to your unity project Editor folder; +1. modify `public const string UnityEngineAnalyzerPath = "Tools\\VisualStudio\\UnityEngineAnalyzer\\UnityEngineAnalyzer.dll";` to your actual UnityEngineAnalyzer.dll path + +Implemented Analyzer +--------------------- +- Prefered to sealed the inherited class; 应对子类进行sealed +- struct should implement IEquatable, should override GetHashCode(); struct应实现IEquatable、应override GetHashCode() +- should cache delegate to avoid gc; 应cache delegate以防止产生gc +- avoid new object in Update() to avoid gc; 避免在Update()函数里new对象产生gc +- avoid boxing; 调用函数时应避免box产生gc +- should not use enum for generic; 泛型避免使用enum +- should use hash for Animator interfaces; Animator接口避免使用String,应使用Hash +- should use hash for Material interfaces; Material接口避免使用String,应使用Hash +- avoid calling Camera.main in Update-like message function; 避免Update()函数里调用Camera.main +- avoid calling GameObject.Find() in Update-like message function; 避免在Update()函数里GameObject.Find()之类的函数 +- should use CompareTag() but not string comparision; 应使用ComareTag(),而不是字符串比较 +- avoid using coroutine; 避免使用协程 +- avoid empty Update-like message function; 避免Mono空消息函数 +- avoid SendMessage(); 避免SendMessage +- detect some infinite recursive call; 错误无限递归检查 + + Comand Line Interface ---------------------