track full mod & stack metadata in queued deprecation warnings

This commit is contained in:
Jesse Plamondon-Willard 2022-04-14 23:11:41 -04:00
parent 1a3befa93e
commit fd136d34c5
No known key found for this signature in database
GPG Key ID: CF8B1456B3E29F49
9 changed files with 41 additions and 44 deletions

View File

@ -83,7 +83,7 @@ namespace StardewModdingAPI
get
{
SCore.DeprecationManager.Warn(
source: SCore.DeprecationManager.GetSourceNameFromStack(),
source: SCore.DeprecationManager.GetModFromStack(),
nounPhrase: $"{nameof(Constants)}.{nameof(Constants.ExecutionPath)}",
version: "3.14.0",
severity: DeprecationLevel.Notice

View File

@ -32,7 +32,7 @@ namespace StardewModdingAPI.Framework.Content
get
{
SCore.DeprecationManager.Warn(
source: SCore.DeprecationManager.GetSourceNameFromStack(),
source: SCore.DeprecationManager.GetModFromStack(),
nounPhrase: $"{nameof(IAssetInfo)}.{nameof(IAssetInfo.AssetName)}",
version: "3.14.0",
severity: DeprecationLevel.Notice
@ -68,7 +68,7 @@ namespace StardewModdingAPI.Framework.Content
public bool AssetNameEquals(string path)
{
SCore.DeprecationManager.Warn(
source: SCore.DeprecationManager.GetSourceNameFromStack(),
source: SCore.DeprecationManager.GetModFromStack(),
nounPhrase: $"{nameof(IAssetInfo)}.{nameof(IAssetInfo.AssetNameEquals)}",
version: "3.14.0",
severity: DeprecationLevel.Notice

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace StardewModdingAPI.Framework
@ -35,34 +36,33 @@ namespace StardewModdingAPI.Framework
this.ModRegistry = modRegistry;
}
/// <summary>Get the source name for a mod from its unique ID.</summary>
public string? GetSourceNameFromStack()
/// <summary>Get a mod for the closest assembly registered as a source of deprecation warnings.</summary>
/// <returns>Returns the source name, or <c>null</c> if no registered assemblies were found.</returns>
public IModMetadata? GetModFromStack()
{
return this.ModRegistry.GetFromStack()?.DisplayName;
return this.ModRegistry.GetFromStack();
}
/// <summary>Get the source name for a mod from its unique ID.</summary>
/// <summary>Get a mod from its unique ID.</summary>
/// <param name="modId">The mod's unique ID.</param>
public string? GetSourceName(string modId)
public IModMetadata? GetMod(string modId)
{
return this.ModRegistry.Get(modId)?.DisplayName;
return this.ModRegistry.Get(modId);
}
/// <summary>Log a deprecation warning.</summary>
/// <param name="source">The friendly mod name which used the deprecated code.</param>
/// <param name="source">The mod which used the deprecated code, if known.</param>
/// <param name="nounPhrase">A noun phrase describing what is deprecated.</param>
/// <param name="version">The SMAPI version which deprecated it.</param>
/// <param name="severity">How deprecated the code is.</param>
public void Warn(string? source, string nounPhrase, string version, DeprecationLevel severity)
public void Warn(IModMetadata? source, string nounPhrase, string version, DeprecationLevel severity)
{
source ??= this.GetSourceNameFromStack() ?? "<unknown>";
// ignore if already warned
if (!this.MarkWarned(source, nounPhrase, version))
return;
// queue warning
this.QueuedWarnings.Add(new DeprecationWarning(source, nounPhrase, version, severity, Environment.StackTrace));
this.QueuedWarnings.Add(new DeprecationWarning(source, nounPhrase, version, severity, new StackTrace(skipFrames: 1)));
}
/// <summary>A placeholder method used to track deprecated code for which a separate warning will be shown.</summary>
@ -104,7 +104,7 @@ namespace StardewModdingAPI.Framework
else
{
this.Monitor.Log(message, level);
this.Monitor.Log(warning.StackTrace, LogLevel.Debug);
this.Monitor.Log(warning.StackTrace.ToString(), LogLevel.Debug);
}
}
@ -116,16 +116,13 @@ namespace StardewModdingAPI.Framework
** Private methods
*********/
/// <summary>Mark a deprecation warning as already logged.</summary>
/// <param name="source">The friendly name of the assembly which used the deprecated code.</param>
/// <param name="source">The mod which used the deprecated code.</param>
/// <param name="nounPhrase">A noun phrase describing what is deprecated (e.g. "the Extensions.AsInt32 method").</param>
/// <param name="version">The SMAPI version which deprecated it.</param>
/// <returns>Returns whether the deprecation was successfully marked as warned. Returns <c>false</c> if it was already marked.</returns>
private bool MarkWarned(string source, string nounPhrase, string version)
private bool MarkWarned(IModMetadata? source, string nounPhrase, string version)
{
if (string.IsNullOrWhiteSpace(source))
throw new InvalidOperationException("The deprecation source cannot be empty.");
string key = $"{source}::{nounPhrase}::{version}";
string key = $"{source?.DisplayName ?? "<unknown>"}::{nounPhrase}::{version}";
if (this.LoggedDeprecations.Contains(key))
return false;
this.LoggedDeprecations.Add(key);

View File

@ -1,3 +1,5 @@
using System.Diagnostics;
namespace StardewModdingAPI.Framework
{
/// <summary>A deprecation warning for a mod.</summary>
@ -6,8 +8,11 @@ namespace StardewModdingAPI.Framework
/*********
** Accessors
*********/
/// <summary>The affected mod's display name.</summary>
public string ModName { get; }
/// <summary>The affected mod.</summary>
public IModMetadata? Mod { get; }
/// <summary>Get the display name for the affected mod.</summary>
public string ModName => this.Mod?.DisplayName ?? "<unknown mod>";
/// <summary>A noun phrase describing what is deprecated.</summary>
public string NounPhrase { get; }
@ -19,21 +24,21 @@ namespace StardewModdingAPI.Framework
public DeprecationLevel Level { get; }
/// <summary>The stack trace when the deprecation warning was raised.</summary>
public string StackTrace { get; }
public StackTrace StackTrace { get; }
/*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
/// <param name="modName">The affected mod's display name.</param>
/// <param name="mod">The affected mod.</param>
/// <param name="nounPhrase">A noun phrase describing what is deprecated.</param>
/// <param name="version">The SMAPI version which deprecated it.</param>
/// <param name="level">The deprecation level for the affected code.</param>
/// <param name="stackTrace">The stack trace when the deprecation warning was raised.</param>
public DeprecationWarning(string modName, string nounPhrase, string version, DeprecationLevel level, string stackTrace)
public DeprecationWarning(IModMetadata? mod, string nounPhrase, string version, DeprecationLevel level, StackTrace stackTrace)
{
this.ModName = modName;
this.Mod = mod;
this.NounPhrase = nounPhrase;
this.Version = version;
this.Level = level;

View File

@ -36,7 +36,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
public bool Trigger(string name, string[] arguments)
{
SCore.DeprecationManager.Warn(
source: SCore.DeprecationManager.GetSourceName(this.ModID),
source: SCore.DeprecationManager.GetMod(this.ModID),
nounPhrase: $"{nameof(IModHelper)}.{nameof(IModHelper.ConsoleCommands)}.{nameof(ICommandHelper.Trigger)}",
version: "3.8.1",
severity: DeprecationLevel.Notice

View File

@ -29,9 +29,6 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <summary>A content manager for this mod which manages files from the mod's folder.</summary>
private readonly ModContentManager ModContentManager;
/// <summary>The friendly mod name for use in errors.</summary>
private readonly string ModName;
/// <summary>Encapsulates monitoring and logging.</summary>
private readonly IMonitor Monitor;
@ -60,7 +57,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
get
{
SCore.DeprecationManager.Warn(
source: this.ModName,
source: this.Mod,
nounPhrase: $"{nameof(IContentHelper)}.{nameof(IContentHelper.AssetLoaders)}",
version: "3.14.0",
severity: DeprecationLevel.Notice
@ -76,7 +73,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
get
{
SCore.DeprecationManager.Warn(
source: this.ModName,
source: this.Mod,
nounPhrase: $"{nameof(IContentHelper)}.{nameof(IContentHelper.AssetEditors)}",
version: "3.14.0",
severity: DeprecationLevel.Notice
@ -94,18 +91,16 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <param name="contentCore">SMAPI's core content logic.</param>
/// <param name="modFolderPath">The absolute path to the mod folder.</param>
/// <param name="mod">The mod using this instance.</param>
/// <param name="modName">The friendly mod name for use in errors.</param>
/// <param name="monitor">Encapsulates monitoring and logging.</param>
/// <param name="reflection">Simplifies access to private code.</param>
public ContentHelper(ContentCoordinator contentCore, string modFolderPath, IModMetadata mod, string modName, IMonitor monitor, Reflector reflection)
public ContentHelper(ContentCoordinator contentCore, string modFolderPath, IModMetadata mod, IMonitor monitor, Reflector reflection)
: base(mod)
{
string managedAssetPrefix = contentCore.GetManagedAssetPrefix(mod.Manifest.UniqueID);
this.ContentCore = contentCore;
this.GameContentManager = contentCore.CreateGameContentManager(managedAssetPrefix + ".content");
this.ModContentManager = contentCore.CreateModContentManager(managedAssetPrefix, modName, modFolderPath, this.GameContentManager);
this.ModName = modName;
this.ModContentManager = contentCore.CreateModContentManager(managedAssetPrefix, this.Mod.DisplayName, modFolderPath, this.GameContentManager);
this.Monitor = monitor;
this.Reflection = reflection;
}
@ -128,12 +123,12 @@ namespace StardewModdingAPI.Framework.ModHelpers
return this.ModContentManager.LoadExact<T>(assetName, useCache: false);
default:
throw new SContentLoadException($"{this.ModName} failed loading content asset '{key}' from {source}: unknown content source '{source}'.");
throw new SContentLoadException($"{this.Mod.DisplayName} failed loading content asset '{key}' from {source}: unknown content source '{source}'.");
}
}
catch (Exception ex) when (ex is not SContentLoadException)
{
throw new SContentLoadException($"{this.ModName} failed loading content asset '{key}' from {source}.", ex);
throw new SContentLoadException($"{this.Mod.DisplayName} failed loading content asset '{key}' from {source}.", ex);
}
}

View File

@ -32,7 +32,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
get
{
SCore.DeprecationManager.Warn(
source: SCore.DeprecationManager.GetSourceName(this.ModID),
source: SCore.DeprecationManager.GetMod(this.ModID),
nounPhrase: $"{nameof(IModHelper)}.{nameof(IModHelper.Content)}",
version: "3.14.0",
severity: DeprecationLevel.Notice

View File

@ -1598,7 +1598,7 @@ namespace StardewModdingAPI.Framework
if (metadata.Mod is IAssetEditor editor)
{
SCore.DeprecationManager.Warn(
source: metadata.DisplayName,
source: metadata,
nounPhrase: $"{nameof(IAssetEditor)}",
version: "3.14.0",
severity: DeprecationLevel.Notice
@ -1610,7 +1610,7 @@ namespace StardewModdingAPI.Framework
if (metadata.Mod is IAssetLoader loader)
{
SCore.DeprecationManager.Warn(
source: metadata.DisplayName,
source: metadata,
nounPhrase: $"{nameof(IAssetLoader)}",
version: "3.14.0",
severity: DeprecationLevel.Notice
@ -1846,7 +1846,7 @@ namespace StardewModdingAPI.Framework
ICommandHelper commandHelper = new CommandHelper(mod, this.CommandManager);
CaseInsensitivePathCache relativePathCache = this.ContentCore.GetCaseInsensitivePathCache(mod.DirectoryPath);
#pragma warning disable CS0612 // deprecated code
ContentHelper contentHelper = new(contentCore, mod.DirectoryPath, mod, mod.DisplayName, monitor, this.Reflection);
ContentHelper contentHelper = new(contentCore, mod.DirectoryPath, mod, monitor, this.Reflection);
#pragma warning restore CS0612
GameContentHelper gameContentHelper = new(contentCore, mod, mod.DisplayName, monitor, this.Reflection);
IModContentHelper modContentHelper = new ModContentHelper(contentCore, mod.DirectoryPath, mod, mod.DisplayName, gameContentHelper.GetUnderlyingContentManager(), relativePathCache, this.Reflection);

View File

@ -96,7 +96,7 @@ namespace StardewModdingAPI.Utilities
if (!nullExpected)
{
SCore.DeprecationManager.Warn(
SCore.DeprecationManager.GetSourceNameFromStack(),
SCore.DeprecationManager.GetModFromStack(),
$"calling the {nameof(PerScreen<T>)} constructor with null",
"3.14.0",
DeprecationLevel.Notice