From f39da383a17b368e92fd243cf155b27ba42671f3 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 13 Apr 2022 20:24:14 -0400 Subject: enable nullable annotations in SMAPI where no logic changes are needed (#837) --- src/SMAPI/Framework/ModHelpers/ContentHelper.cs | 9 +++---- src/SMAPI/Framework/ModHelpers/DataHelper.cs | 28 ++++++++++++---------- .../Framework/ModHelpers/GameContentHelper.cs | 8 ++++--- src/SMAPI/Framework/ModHelpers/ModContentHelper.cs | 6 ++--- .../Framework/ModHelpers/ModRegistryHelper.cs | 13 +++++----- .../Framework/ModHelpers/MultiplayerHelper.cs | 8 +++---- src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs | 26 ++++++++++---------- 7 files changed, 50 insertions(+), 48 deletions(-) (limited to 'src/SMAPI/Framework/ModHelpers') diff --git a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs index 12ef0439..b610b395 100644 --- a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -114,6 +112,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// public T Load(string key, ContentSource source = ContentSource.ModFolder) + where T : notnull { IAssetName assetName = this.ContentCore.ParseAssetName(key, allowLocales: source == ContentSource.GameContent); @@ -140,7 +139,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// [Pure] - public string NormalizeAssetName(string assetName) + public string NormalizeAssetName(string? assetName) { return this.ModContentManager.AssertAndNormalizeAssetName(assetName); } @@ -171,6 +170,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// public bool InvalidateCache() + where T : notnull { this.Monitor.Log($"Requested cache invalidation for all assets of type {typeof(T)}. This is an expensive operation and should be avoided if possible."); return this.ContentCore.InvalidateCache((_, _, type) => typeof(T).IsAssignableFrom(type)).Any(); @@ -184,7 +184,8 @@ namespace StardewModdingAPI.Framework.ModHelpers } /// - public IAssetData GetPatchHelper(T data, string assetName = null) + public IAssetData GetPatchHelper(T data, string? assetName = null) + where T : notnull { if (data == null) throw new ArgumentNullException(nameof(data), "Can't get a patch helper for a null value."); diff --git a/src/SMAPI/Framework/ModHelpers/DataHelper.cs b/src/SMAPI/Framework/ModHelpers/DataHelper.cs index 86a34ee8..92b3b398 100644 --- a/src/SMAPI/Framework/ModHelpers/DataHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/DataHelper.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using System.IO; @@ -42,19 +40,21 @@ namespace StardewModdingAPI.Framework.ModHelpers ** JSON file ****/ /// - public TModel ReadJsonFile(string path) where TModel : class + public TModel? ReadJsonFile(string path) + where TModel : class { if (!PathUtilities.IsSafeRelativePath(path)) throw new InvalidOperationException($"You must call {nameof(IModHelper.Data)}.{nameof(this.ReadJsonFile)} with a relative path."); path = Path.Combine(this.ModFolderPath, PathUtilities.NormalizePath(path)); - return this.JsonHelper.ReadJsonFileIfExists(path, out TModel data) + return this.JsonHelper.ReadJsonFileIfExists(path, out TModel? data) ? data : null; } /// - public void WriteJsonFile(string path, TModel data) where TModel : class + public void WriteJsonFile(string path, TModel? data) + where TModel : class { if (!PathUtilities.IsSafeRelativePath(path)) throw new InvalidOperationException($"You must call {nameof(IMod.Helper)}.{nameof(IModHelper.Data)}.{nameof(this.WriteJsonFile)} with a relative path (without directory climbing)."); @@ -71,7 +71,8 @@ namespace StardewModdingAPI.Framework.ModHelpers ** Save file ****/ /// - public TModel ReadSaveData(string key) where TModel : class + public TModel? ReadSaveData(string key) + where TModel : class { if (Context.LoadStage == LoadStage.None) throw new InvalidOperationException($"Can't use {nameof(IMod.Helper)}.{nameof(IModHelper.Data)}.{nameof(this.ReadSaveData)} when a save file isn't loaded."); @@ -82,14 +83,15 @@ namespace StardewModdingAPI.Framework.ModHelpers string internalKey = this.GetSaveFileKey(key); foreach (IDictionary dataField in this.GetDataFields(Context.LoadStage)) { - if (dataField.TryGetValue(internalKey, out string value)) + if (dataField.TryGetValue(internalKey, out string? value)) return this.JsonHelper.Deserialize(value); } return null; } /// - public void WriteSaveData(string key, TModel model) where TModel : class + public void WriteSaveData(string key, TModel? model) + where TModel : class { if (Context.LoadStage == LoadStage.None) throw new InvalidOperationException($"Can't use {nameof(IMod.Helper)}.{nameof(IModHelper.Data)}.{nameof(this.WriteSaveData)} when a save file isn't loaded."); @@ -97,7 +99,7 @@ namespace StardewModdingAPI.Framework.ModHelpers throw new InvalidOperationException($"Can't use {nameof(IMod.Helper)}.{nameof(IModHelper.Data)}.{nameof(this.WriteSaveData)} when connected to a remote host. (Save files are stored on the main player's computer.)"); string internalKey = this.GetSaveFileKey(key); - string data = model != null + string? data = model != null ? this.JsonHelper.Serialize(model, Formatting.None) : null; @@ -114,16 +116,18 @@ namespace StardewModdingAPI.Framework.ModHelpers ** Global app data ****/ /// - public TModel ReadGlobalData(string key) where TModel : class + public TModel? ReadGlobalData(string key) + where TModel : class { string path = this.GetGlobalDataPath(key); - return this.JsonHelper.ReadJsonFileIfExists(path, out TModel data) + return this.JsonHelper.ReadJsonFileIfExists(path, out TModel? data) ? data : null; } /// - public void WriteGlobalData(string key, TModel data) where TModel : class + public void WriteGlobalData(string key, TModel? data) + where TModel : class { string path = this.GetGlobalDataPath(key); if (data != null) diff --git a/src/SMAPI/Framework/ModHelpers/GameContentHelper.cs b/src/SMAPI/Framework/ModHelpers/GameContentHelper.cs index 6d0c2f5f..4c1cde02 100644 --- a/src/SMAPI/Framework/ModHelpers/GameContentHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/GameContentHelper.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Linq; using StardewModdingAPI.Framework.Content; @@ -71,6 +69,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// public T Load(string key) + where T : notnull { IAssetName assetName = this.ContentCore.ParseAssetName(key, allowLocales: true); return this.Load(assetName); @@ -78,6 +77,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// public T Load(IAssetName assetName) + where T : notnull { try { @@ -105,6 +105,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// public bool InvalidateCache() + where T : notnull { this.Monitor.Log($"Requested cache invalidation for all assets of type {typeof(T)}. This is an expensive operation and should be avoided if possible."); return this.ContentCore.InvalidateCache((_, _, type) => typeof(T).IsAssignableFrom(type)).Any(); @@ -118,7 +119,8 @@ namespace StardewModdingAPI.Framework.ModHelpers } /// - public IAssetData GetPatchHelper(T data, string assetName = null) + public IAssetData GetPatchHelper(T data, string? assetName = null) + where T : notnull { if (data == null) throw new ArgumentNullException(nameof(data), "Can't get a patch helper for a null value."); diff --git a/src/SMAPI/Framework/ModHelpers/ModContentHelper.cs b/src/SMAPI/Framework/ModHelpers/ModContentHelper.cs index b149ed82..fc47f269 100644 --- a/src/SMAPI/Framework/ModHelpers/ModContentHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ModContentHelper.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using Microsoft.Xna.Framework.Content; using StardewModdingAPI.Framework.Content; @@ -57,6 +55,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// public T Load(string relativePath) + where T : notnull { relativePath = this.RelativePathCache.GetAssetName(relativePath); @@ -80,7 +79,8 @@ namespace StardewModdingAPI.Framework.ModHelpers } /// - public IAssetData GetPatchHelper(T data, string relativePath = null) + public IAssetData GetPatchHelper(T data, string? relativePath = null) + where T : notnull { if (data == null) throw new ArgumentNullException(nameof(data), "Can't get a patch helper for a null value."); diff --git a/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs b/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs index e277e6fa..84899610 100644 --- a/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs @@ -1,5 +1,3 @@ -#nullable disable - using System.Collections.Generic; using StardewModdingAPI.Framework.Reflection; @@ -47,7 +45,7 @@ namespace StardewModdingAPI.Framework.ModHelpers } /// - public IModInfo Get(string uniqueID) + public IModInfo? Get(string uniqueID) { return this.Registry.Get(uniqueID); } @@ -59,7 +57,7 @@ namespace StardewModdingAPI.Framework.ModHelpers } /// - public object GetApi(string uniqueID) + public object? GetApi(string uniqueID) { // validate ready if (!this.Registry.AreAllModsInitialized) @@ -69,17 +67,18 @@ namespace StardewModdingAPI.Framework.ModHelpers } // get raw API - IModMetadata mod = this.Registry.Get(uniqueID); + IModMetadata? mod = this.Registry.Get(uniqueID); if (mod?.Api != null && this.AccessedModApis.Add(mod.Manifest.UniqueID)) this.Monitor.Log($"Accessed mod-provided API for {mod.DisplayName}."); return mod?.Api; } /// - public TInterface GetApi(string uniqueID) where TInterface : class + public TInterface? GetApi(string uniqueID) + where TInterface : class { // get raw API - object api = this.GetApi(uniqueID); + object? api = this.GetApi(uniqueID); if (api == null) return null; diff --git a/src/SMAPI/Framework/ModHelpers/MultiplayerHelper.cs b/src/SMAPI/Framework/ModHelpers/MultiplayerHelper.cs index 96b074e2..a419327e 100644 --- a/src/SMAPI/Framework/ModHelpers/MultiplayerHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/MultiplayerHelper.cs @@ -1,5 +1,3 @@ -#nullable disable - using System.Collections.Generic; using StardewModdingAPI.Framework.Networking; using StardewValley; @@ -41,9 +39,9 @@ namespace StardewModdingAPI.Framework.ModHelpers } /// - public IMultiplayerPeer GetConnectedPlayer(long id) + public IMultiplayerPeer? GetConnectedPlayer(long id) { - return this.Multiplayer.Peers.TryGetValue(id, out MultiplayerPeer peer) + return this.Multiplayer.Peers.TryGetValue(id, out MultiplayerPeer? peer) ? peer : null; } @@ -55,7 +53,7 @@ namespace StardewModdingAPI.Framework.ModHelpers } /// - public void SendMessage(TMessage message, string messageType, string[] modIDs = null, long[] playerIDs = null) + public void SendMessage(TMessage message, string messageType, string[]? modIDs = null, long[]? playerIDs = null) { this.Multiplayer.BroadcastModMessage( message: message, diff --git a/src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs b/src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs index 24cbd01c..af018c87 100644 --- a/src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Reflection; using StardewModdingAPI.Framework.Reflection; @@ -39,7 +37,7 @@ namespace StardewModdingAPI.Framework.ModHelpers { return this.AssertAccessAllowed( this.Reflector.GetField(obj, name, required) - ); + )!; } /// @@ -47,7 +45,7 @@ namespace StardewModdingAPI.Framework.ModHelpers { return this.AssertAccessAllowed( this.Reflector.GetField(type, name, required) - ); + )!; } /// @@ -55,7 +53,7 @@ namespace StardewModdingAPI.Framework.ModHelpers { return this.AssertAccessAllowed( this.Reflector.GetProperty(obj, name, required) - ); + )!; } /// @@ -63,7 +61,7 @@ namespace StardewModdingAPI.Framework.ModHelpers { return this.AssertAccessAllowed( this.Reflector.GetProperty(type, name, required) - ); + )!; } /// @@ -71,7 +69,7 @@ namespace StardewModdingAPI.Framework.ModHelpers { return this.AssertAccessAllowed( this.Reflector.GetMethod(obj, name, required) - ); + )!; } /// @@ -79,7 +77,7 @@ namespace StardewModdingAPI.Framework.ModHelpers { return this.AssertAccessAllowed( this.Reflector.GetMethod(type, name, required) - ); + )!; } @@ -90,7 +88,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// The field value type. /// The field being accessed. /// Returns the same field instance for convenience. - private IReflectedField AssertAccessAllowed(IReflectedField field) + private IReflectedField? AssertAccessAllowed(IReflectedField? field) { this.AssertAccessAllowed(field?.FieldInfo); return field; @@ -100,7 +98,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// The property value type. /// The property being accessed. /// Returns the same property instance for convenience. - private IReflectedProperty AssertAccessAllowed(IReflectedProperty property) + private IReflectedProperty? AssertAccessAllowed(IReflectedProperty? property) { this.AssertAccessAllowed(property?.PropertyInfo.GetMethod?.GetBaseDefinition()); this.AssertAccessAllowed(property?.PropertyInfo.SetMethod?.GetBaseDefinition()); @@ -110,7 +108,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// Assert that mods can use the reflection helper to access the given member. /// The method being accessed. /// Returns the same method instance for convenience. - private IReflectedMethod AssertAccessAllowed(IReflectedMethod method) + private IReflectedMethod? AssertAccessAllowed(IReflectedMethod? method) { this.AssertAccessAllowed(method?.MethodInfo.GetBaseDefinition()); return method; @@ -118,18 +116,18 @@ namespace StardewModdingAPI.Framework.ModHelpers /// Assert that mods can use the reflection helper to access the given member. /// The member being accessed. - private void AssertAccessAllowed(MemberInfo member) + private void AssertAccessAllowed(MemberInfo? member) { if (member == null) return; // get type which defines the member - Type declaringType = member.DeclaringType; + Type? declaringType = member.DeclaringType; if (declaringType == null) throw new InvalidOperationException($"Can't validate access to {member.MemberType} {member.Name} because it has no declaring type."); // should never happen // validate access - string rootNamespace = typeof(Program).Namespace; + string? rootNamespace = typeof(Program).Namespace; if (declaringType.Namespace == rootNamespace || declaringType.Namespace?.StartsWith(rootNamespace + ".") == true) throw new InvalidOperationException($"SMAPI blocked access by {this.ModName} to its internals through the reflection API. Accessing the SMAPI internals is strongly discouraged since they're subject to change, which means the mod can break without warning. (Detected access to {declaringType.FullName}.{member.Name}.)"); } -- cgit