mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-08-24 20:52:11 +00:00
Exempt the settings manager from the provider access rule
This commit is contained in:
parent
f0527f3852
commit
494e48ddd3
@ -1,4 +1,3 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
@ -434,7 +433,6 @@ public sealed class SettingsManager
|
|||||||
return localeTag[..separatorIndex];
|
return localeTag[..separatorIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
[SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed")]
|
|
||||||
public Provider GetPreselectedProvider(Tools.Components component, string? currentProviderId = null, bool usePreselectionBeforeCurrentProvider = false)
|
public Provider GetPreselectedProvider(Tools.Components component, string? currentProviderId = null, bool usePreselectionBeforeCurrentProvider = false)
|
||||||
{
|
{
|
||||||
var minimumLevel = this.GetMinimumConfidenceLevel(component);
|
var minimumLevel = this.GetMinimumConfidenceLevel(component);
|
||||||
@ -486,7 +484,6 @@ public sealed class SettingsManager
|
|||||||
return this.ConfigurationData.Providers.FirstOrDefault(x => x.Id == this.ConfigurationData.App.PreselectedProvider && x.UsedLLMProvider.GetConfidence(this).Level >= minimumLevel) ?? Provider.NONE;
|
return this.ConfigurationData.Providers.FirstOrDefault(x => x.Id == this.ConfigurationData.App.PreselectedProvider && x.UsedLLMProvider.GetConfidence(this).Level >= minimumLevel) ?? Provider.NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
[SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed")]
|
|
||||||
public Provider GetChatProviderForLoadedChat(string? chatProviderId = null)
|
public Provider GetChatProviderForLoadedChat(string? chatProviderId = null)
|
||||||
{
|
{
|
||||||
var minimumLevel = this.GetMinimumConfidenceLevel(Tools.Components.CHAT);
|
var minimumLevel = this.GetMinimumConfidenceLevel(Tools.Components.CHAT);
|
||||||
@ -548,7 +545,6 @@ public sealed class SettingsManager
|
|||||||
/// </para>
|
/// </para>
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <returns>All configured providers, unfiltered.</returns>
|
/// <returns>All configured providers, unfiltered.</returns>
|
||||||
[SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed")]
|
|
||||||
public IReadOnlyList<Provider> GetAllProviders() => this.ConfigurationData.Providers;
|
public IReadOnlyList<Provider> GetAllProviders() => this.ConfigurationData.Providers;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -563,7 +559,6 @@ public sealed class SettingsManager
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="providerId">The id of the provider to look up.</param>
|
/// <param name="providerId">The id of the provider to look up.</param>
|
||||||
/// <returns>The provider, or <see cref="Provider.NONE"/> when no provider with that id exists.</returns>
|
/// <returns>The provider, or <see cref="Provider.NONE"/> when no provider with that id exists.</returns>
|
||||||
[SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed")]
|
|
||||||
public Provider GetProviderById(string? providerId)
|
public Provider GetProviderById(string? providerId)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(providerId))
|
if (string.IsNullOrWhiteSpace(providerId))
|
||||||
@ -611,7 +606,6 @@ public sealed class SettingsManager
|
|||||||
/// <param name="component">The component for which the providers get filtered.</param>
|
/// <param name="component">The component for which the providers get filtered.</param>
|
||||||
/// <param name="explicitMinimum">An explicit minimum level, which is applied when it is higher than the component's minimum.</param>
|
/// <param name="explicitMinimum">An explicit minimum level, which is applied when it is higher than the component's minimum.</param>
|
||||||
/// <returns>All providers the component may use.</returns>
|
/// <returns>All providers the component may use.</returns>
|
||||||
[SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed")]
|
|
||||||
public IEnumerable<Provider> GetConfidentProviders(Tools.Components component, ConfidenceLevel explicitMinimum = ConfidenceLevel.UNKNOWN)
|
public IEnumerable<Provider> GetConfidentProviders(Tools.Components component, ConfidenceLevel explicitMinimum = ConfidenceLevel.UNKNOWN)
|
||||||
{
|
{
|
||||||
var minimumLevel = this.GetEffectiveMinimumConfidenceLevel(component, explicitMinimum);
|
var minimumLevel = this.GetEffectiveMinimumConfidenceLevel(component, explicitMinimum);
|
||||||
|
|||||||
@ -17,11 +17,16 @@ public sealed class ProviderAccessAnalyzer : DiagnosticAnalyzer
|
|||||||
|
|
||||||
private static readonly string TITLE = "Direct access to `Providers` is not allowed";
|
private static readonly string TITLE = "Direct access to `Providers` is not allowed";
|
||||||
|
|
||||||
private static readonly string MESSAGE_FORMAT = "Direct access to `SettingsManager.ConfigurationData.Providers` is not allowed. Instead, use APIs like `SettingsManager.GetPreselectedProvider`, etc.";
|
private static readonly string MESSAGE_FORMAT = "Direct access to `SettingsManager.ConfigurationData.Providers` is not allowed. Instead, use APIs like `SettingsManager.GetAllProviders`, `GetProviderById`, `GetConfidentProviders`, `GetPreselectedProvider`, or `GetChatProviderForLoadedChat`.";
|
||||||
|
|
||||||
private static readonly string DESCRIPTION = MESSAGE_FORMAT;
|
private static readonly string DESCRIPTION = MESSAGE_FORMAT;
|
||||||
|
|
||||||
private const string CATEGORY = "Usage";
|
private const string CATEGORY = "Usage";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The one type which owns the provider list and is therefore allowed to access it directly.
|
||||||
|
/// </summary>
|
||||||
|
private const string OWNING_TYPE = "AIStudio.Settings.SettingsManager";
|
||||||
|
|
||||||
private static readonly DiagnosticDescriptor RULE = new(DIAGNOSTIC_ID, TITLE, MESSAGE_FORMAT, CATEGORY, DiagnosticSeverity.Error, isEnabledByDefault: true, description: DESCRIPTION);
|
private static readonly DiagnosticDescriptor RULE = new(DIAGNOSTIC_ID, TITLE, MESSAGE_FORMAT, CATEGORY, DiagnosticSeverity.Error, isEnabledByDefault: true, description: DESCRIPTION);
|
||||||
|
|
||||||
@ -42,8 +47,17 @@ public sealed class ProviderAccessAnalyzer : DiagnosticAnalyzer
|
|||||||
if (memberAccess.Name.Identifier.Text != "Providers")
|
if (memberAccess.Name.Identifier.Text != "Providers")
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
//
|
||||||
|
// The settings manager owns the provider list: it implements the very APIs which all other
|
||||||
|
// code is meant to use, so it must access `Providers` directly. Exempting it here keeps
|
||||||
|
// those implementations free of suppression attributes, which would otherwise read as if
|
||||||
|
// suppressing this rule was a normal thing to do:
|
||||||
|
//
|
||||||
|
if (IsOwningType(context.ContainingSymbol))
|
||||||
|
return;
|
||||||
|
|
||||||
// Get the full path of the member access:
|
// Get the full path of the member access:
|
||||||
var fullPath = this.GetFullMemberAccessPath(memberAccess);
|
var fullPath = GetFullMemberAccessPath(memberAccess);
|
||||||
|
|
||||||
// Check for the forbidden pattern:
|
// Check for the forbidden pattern:
|
||||||
if (fullPath.EndsWith("ConfigurationData.Providers"))
|
if (fullPath.EndsWith("ConfigurationData.Providers"))
|
||||||
@ -53,7 +67,30 @@ public sealed class ProviderAccessAnalyzer : DiagnosticAnalyzer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetFullMemberAccessPath(ExpressionSyntax expression)
|
/// <summary>
|
||||||
|
/// Checks whether the analyzed node sits inside the type which owns the provider list.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The containing symbol is the member the node belongs to, e.g. a method or a property. We walk
|
||||||
|
/// the chain of containing types so that nested types of the owning type are covered as well.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="containingSymbol">The symbol containing the analyzed node, which may be null.</param>
|
||||||
|
/// <returns>True, when the node belongs to the owning type.</returns>
|
||||||
|
private static bool IsOwningType(ISymbol? containingSymbol)
|
||||||
|
{
|
||||||
|
var containingType = containingSymbol as INamedTypeSymbol ?? containingSymbol?.ContainingType;
|
||||||
|
while (containingType != null)
|
||||||
|
{
|
||||||
|
if (containingType.ToDisplayString() == OWNING_TYPE)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
containingType = containingType.ContainingType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetFullMemberAccessPath(ExpressionSyntax expression)
|
||||||
{
|
{
|
||||||
var parts = new List<string>();
|
var parts = new List<string>();
|
||||||
while (expression is MemberAccessExpressionSyntax memberAccess)
|
while (expression is MemberAccessExpressionSyntax memberAccess)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user