mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-04 16:07:21 +00:00
Moved the build to the .NET SDK v10.0.403 (#1156)
This commit is contained in:
1 parent
c0a75f39ac
commit
0375318b1b
10 files changed
+143
-153
No files matched your search
@@ -793,7 +793,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Read the toolchain versions from the metadata
|
- name: Read the toolchain versions from the metadata
|
||||||
run: |
|
run: |
|
||||||
# The .NET SDK version. The format is '10.0.114 (commit 0427b9abbe)',
|
# The .NET SDK version. The format is '10.0.403 (commit 5a2162bea6)',
|
||||||
# so we extract the version number alone:
|
# so we extract the version number alone:
|
||||||
dotnet_sdk_version=$(sed -n '4p' metadata.txt | sed 's/[^0-9.]*\([0-9.]*\).*/\1/')
|
dotnet_sdk_version=$(sed -n '4p' metadata.txt | sed 's/[^0-9.]*\([0-9.]*\).*/\1/')
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -28,7 +28,7 @@ Key structure:
|
|||||||
## Important Development Notes
|
## Important Development Notes
|
||||||
|
|
||||||
- **Naming conventions** - Constants, enum members, and `static readonly` fields use `UPPER_SNAKE_CASE` such as `MY_CONSTANT`.
|
- **Naming conventions** - Constants, enum members, and `static readonly` fields use `UPPER_SNAKE_CASE` such as `MY_CONSTANT`.
|
||||||
- **Extension members** - Declare extension methods in an `extension` block of their static class, one block per receiver, never with a `this` parameter: MWAIS0016 refuses the classic form. The `<param>` of the receiver, and of a generic block its `<typeparam>`, belongs on the block, not on its members. The one exception is a method with a `params` parameter, which stays classic until the SDK in `metadata.txt` carries the fix for dotnet/roslyn#80024: for a member of an extension block, the compiler warns about every argument passed one by one to a `params` parameter (CS8620), see `JsRuntimeExtensions`.
|
- **Extension members** - Declare extension methods in an `extension` block of their static class, one block per receiver, never with a `this` parameter: MWAIS0016 refuses the classic form. The `<param>` of the receiver, and of a generic block its `<typeparam>`, belongs on the block, not on its members.
|
||||||
- **Message Bus** - Singleton event bus for cross-component communication inside the .NET app
|
- **Message Bus** - Singleton event bus for cross-component communication inside the .NET app
|
||||||
- **Prompt injection reports** - The user hears once per action about what the filter removed, as soon as that action ends. Content which belongs to an action carries its scope from `PromptInjectionGuardService.BeginAction` down to where it is read or filtered; whatever is filtered without a scope is reported right away. Pass the scope as a parameter, never through a field or an `AsyncLocal`: the service serves the whole app, and the message bus delivers to other receivers in the execution context of whoever sends.
|
- **Prompt injection reports** - The user hears once per action about what the filter removed, as soon as that action ends. Content which belongs to an action carries its scope from `PromptInjectionGuardService.BeginAction` down to where it is read or filtered; whatever is filtered without a scope is reported right away. Pass the scope as a parameter, never through a field or an `AsyncLocal`: the service serves the whole app, and the message bus delivers to other receivers in the execution context of whoever sends.
|
||||||
- **Paths in stored data** - A stored path to a file which AI Studio keeps below the data directory, such as a transcript or a tokenizer copy, is relative to that directory, so that a backup or a copy of the data directory works on another computer, under another user name, or on another OS. Mark such a property with `[JsonConverter(typeof(DataDirectoryRelativePathConverter))]`, for a positional record parameter with `[property: ...]`; in memory, the path stays absolute. Better still, derive the path from IDs and store none, as `VisualBriefingStore` does. Paths to files of the user stay absolute. Chats stored before v26.10.1 are migrated by a compatibility shim, see `documentation/compatibility-shims/2026-10-relative-data-paths.md`.
|
- **Paths in stored data** - A stored path to a file which AI Studio keeps below the data directory, such as a transcript or a tokenizer copy, is relative to that directory, so that a backup or a copy of the data directory works on another computer, under another user name, or on another OS. Mark such a property with `[JsonConverter(typeof(DataDirectoryRelativePathConverter))]`, for a positional record parameter with `[property: ...]`; in memory, the path stays absolute. Better still, derive the path from IDs and store none, as `VisualBriefingStore` does. Paths to files of the user stay absolute. Chats stored before v26.10.1 are migrated by a compatibility shim, see `documentation/compatibility-shims/2026-10-relative-data-paths.md`.
|
||||||
|
|||||||
@@ -3,16 +3,11 @@ using AIStudio.Tools.Services;
|
|||||||
|
|
||||||
namespace AIStudio.Tools;
|
namespace AIStudio.Tools;
|
||||||
|
|
||||||
//
|
|
||||||
// The methods with a params parameter stay classic extension methods instead of moving into the
|
|
||||||
// extension block: for a member of an extension block, the compiler of the .NET 10.0.1xx SDK warns
|
|
||||||
// about every argument passed one by one to a params parameter (CS8620, dotnet/roslyn#80024). Move
|
|
||||||
// them once the SDK in metadata.txt carries the fix; MWAIS0016 makes the same exception until then.
|
|
||||||
//
|
|
||||||
public static class JsRuntimeExtensions
|
public static class JsRuntimeExtensions
|
||||||
{
|
{
|
||||||
private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger(nameof(JsRuntimeExtensions));
|
private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger(nameof(JsRuntimeExtensions));
|
||||||
|
|
||||||
|
/// <param name="jsRuntime">The JS runtime to call.</param>
|
||||||
extension(IJSRuntime jsRuntime)
|
extension(IJSRuntime jsRuntime)
|
||||||
{
|
{
|
||||||
public async Task GenerateAndShowDiff(string text1, string text2)
|
public async Task GenerateAndShowDiff(string text1, string text2)
|
||||||
@@ -24,115 +19,115 @@ public static class JsRuntimeExtensions
|
|||||||
{
|
{
|
||||||
await jsRuntime.InvokeVoidAsync("clearDiv", divId);
|
await jsRuntime.InvokeVoidAsync("clearDiv", divId);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calls a JavaScript function which returns nothing, and tolerates a circuit which is already gone.
|
/// Calls a JavaScript function which returns nothing, and tolerates a circuit which is already gone.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Blazor cannot issue JS interop calls once the browser connection of a circuit is gone. That happens
|
/// Blazor cannot issue JS interop calls once the browser connection of a circuit is gone. That happens
|
||||||
/// during every reload and while a component gets disposed, so the failure is expected rather than
|
/// during every reload and while a component gets disposed, so the failure is expected rather than
|
||||||
/// exceptional. Discarding such a call is not an option, though: the discarded task keeps the fault
|
/// exceptional. Discarding such a call is not an option, though: the discarded task keeps the fault
|
||||||
/// until the finalizer reports it as an unobserved task exception, without any hint at its origin.
|
/// until the finalizer reports it as an unobserved task exception, without any hint at its origin.
|
||||||
/// This method is the one place which knows how to await such a call and what to do with its failure.
|
/// This method is the one place which knows how to await such a call and what to do with its failure.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="jsRuntime">The JS runtime to call.</param>
|
/// <param name="identifier">The name of the JavaScript function.</param>
|
||||||
/// <param name="identifier">The name of the JavaScript function.</param>
|
/// <param name="args">The arguments for the JavaScript function.</param>
|
||||||
/// <param name="args">The arguments for the JavaScript function.</param>
|
/// <returns>True when the browser ran the function. Callers which remember what they told the browser
|
||||||
/// <returns>True when the browser ran the function. Callers which remember what they told the browser
|
/// must check this: a call which never arrived leaves the browser in its previous state.</returns>
|
||||||
/// must check this: a call which never arrived leaves the browser in its previous state.</returns>
|
public async ValueTask<bool> TryInvokeVoidAsync(string identifier, params object?[]? args)
|
||||||
public static async ValueTask<bool> TryInvokeVoidAsync(this IJSRuntime jsRuntime, string identifier, params object?[]? args)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
await jsRuntime.InvokeVoidAsync(identifier, args);
|
try
|
||||||
return true;
|
{
|
||||||
|
await jsRuntime.InvokeVoidAsync(identifier, args);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
LogInvocationFailure(exception, identifier);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calls a JavaScript function which returns nothing, unless the circuit is known to be disconnected.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Prefer this over the variant without a circuit state wherever the caller knows its circuit. While a
|
||||||
|
/// browser connection is gone, every single call would otherwise throw, which is needless work for
|
||||||
|
/// something we already know cannot succeed — a component of a disconnected circuit which keeps
|
||||||
|
/// rendering would produce one such exception per render.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="circuitState">The circuit of the caller.</param>
|
||||||
|
/// <param name="identifier">The name of the JavaScript function.</param>
|
||||||
|
/// <param name="args">The arguments for the JavaScript function.</param>
|
||||||
|
/// <returns>True when the browser ran the function, false when it was skipped or failed.</returns>
|
||||||
|
public async ValueTask<bool> TryInvokeVoidAsync(CircuitStateService circuitState, string identifier, params object?[]? args)
|
||||||
{
|
{
|
||||||
LogInvocationFailure(exception, identifier);
|
if (!circuitState.IsConnected)
|
||||||
return false;
|
{
|
||||||
|
LOGGER.LogDebug("The JS call '{Identifier}' was skipped because the browser connection of the circuit '{CircuitId}' is down.", identifier, circuitState.CircuitId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await jsRuntime.TryInvokeVoidAsync(identifier, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calls a JavaScript function which returns a value, unless the circuit is known to be disconnected.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The two parts of the result answer two different questions, and callers must keep them apart.
|
||||||
|
/// Whether the browser ran the function at all comes first: a call which never arrived says nothing
|
||||||
|
/// about the page, so nobody may act on an answer they did not get. What the function returned is the
|
||||||
|
/// second question, and there a null is a legitimate answer -- it means the browser looked and found
|
||||||
|
/// nothing.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="circuitState">The circuit of the caller.</param>
|
||||||
|
/// <param name="identifier">The name of the JavaScript function.</param>
|
||||||
|
/// <param name="args">The arguments for the JavaScript function.</param>
|
||||||
|
/// <returns>Whether the browser ran the function, and what it returned.</returns>
|
||||||
|
public async ValueTask<(bool WasInvoked, TValue? Value)> TryInvokeAsync<TValue>(CircuitStateService circuitState, string identifier, params object?[]? args)
|
||||||
|
{
|
||||||
|
if (!circuitState.IsConnected)
|
||||||
|
{
|
||||||
|
LOGGER.LogDebug("The JS call '{Identifier}' was skipped because the browser connection of the circuit '{CircuitId}' is down.", identifier, circuitState.CircuitId);
|
||||||
|
return (false, default);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return (true, await jsRuntime.InvokeAsync<TValue>(identifier, args));
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
LogInvocationFailure(exception, identifier);
|
||||||
|
return (false, default);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Calls a JavaScript function which returns nothing, unless the circuit is known to be disconnected.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Prefer this over the variant without a circuit state wherever the caller knows its circuit. While a
|
|
||||||
/// browser connection is gone, every single call would otherwise throw, which is needless work for
|
|
||||||
/// something we already know cannot succeed — a component of a disconnected circuit which keeps
|
|
||||||
/// rendering would produce one such exception per render.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="jsRuntime">The JS runtime to call.</param>
|
|
||||||
/// <param name="circuitState">The circuit of the caller.</param>
|
|
||||||
/// <param name="identifier">The name of the JavaScript function.</param>
|
|
||||||
/// <param name="args">The arguments for the JavaScript function.</param>
|
|
||||||
/// <returns>True when the browser ran the function, false when it was skipped or failed.</returns>
|
|
||||||
public static async ValueTask<bool> TryInvokeVoidAsync(this IJSRuntime jsRuntime, CircuitStateService circuitState, string identifier, params object?[]? args)
|
|
||||||
{
|
|
||||||
if (!circuitState.IsConnected)
|
|
||||||
{
|
|
||||||
LOGGER.LogDebug("The JS call '{Identifier}' was skipped because the browser connection of the circuit '{CircuitId}' is down.", identifier, circuitState.CircuitId);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return await jsRuntime.TryInvokeVoidAsync(identifier, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Calls a JavaScript function which returns a value, unless the circuit is known to be disconnected.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// The two parts of the result answer two different questions, and callers must keep them apart.
|
|
||||||
/// Whether the browser ran the function at all comes first: a call which never arrived says nothing
|
|
||||||
/// about the page, so nobody may act on an answer they did not get. What the function returned is the
|
|
||||||
/// second question, and there a null is a legitimate answer -- it means the browser looked and found
|
|
||||||
/// nothing.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="jsRuntime">The JS runtime to call.</param>
|
|
||||||
/// <param name="circuitState">The circuit of the caller.</param>
|
|
||||||
/// <param name="identifier">The name of the JavaScript function.</param>
|
|
||||||
/// <param name="args">The arguments for the JavaScript function.</param>
|
|
||||||
/// <returns>Whether the browser ran the function, and what it returned.</returns>
|
|
||||||
public static async ValueTask<(bool WasInvoked, TValue? Value)> TryInvokeAsync<TValue>(this IJSRuntime jsRuntime, CircuitStateService circuitState, string identifier, params object?[]? args)
|
|
||||||
{
|
|
||||||
if (!circuitState.IsConnected)
|
|
||||||
{
|
|
||||||
LOGGER.LogDebug("The JS call '{Identifier}' was skipped because the browser connection of the circuit '{CircuitId}' is down.", identifier, circuitState.CircuitId);
|
|
||||||
return (false, default);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return (true, await jsRuntime.InvokeAsync<TValue>(identifier, args));
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
LogInvocationFailure(exception, identifier);
|
|
||||||
return (false, default);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Calls a function of a JavaScript module which returns nothing, and tolerates a circuit which is
|
|
||||||
/// already gone. See the remarks on the JS runtime variant of this method.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="module">The JavaScript module to call.</param>
|
/// <param name="module">The JavaScript module to call.</param>
|
||||||
/// <param name="identifier">The name of the function inside the module.</param>
|
extension(IJSObjectReference module)
|
||||||
/// <param name="args">The arguments for the function.</param>
|
|
||||||
/// <returns>True when the browser ran the function, false when it failed.</returns>
|
|
||||||
public static async ValueTask<bool> TryInvokeVoidAsync(this IJSObjectReference module, string identifier, params object?[]? args)
|
|
||||||
{
|
{
|
||||||
try
|
/// <summary>
|
||||||
|
/// Calls a function of a JavaScript module which returns nothing, and tolerates a circuit which is
|
||||||
|
/// already gone. See the remarks on the JS runtime variant of this method.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="identifier">The name of the function inside the module.</param>
|
||||||
|
/// <param name="args">The arguments for the function.</param>
|
||||||
|
/// <returns>True when the browser ran the function, false when it failed.</returns>
|
||||||
|
public async ValueTask<bool> TryInvokeVoidAsync(string identifier, params object?[]? args)
|
||||||
{
|
{
|
||||||
await module.InvokeVoidAsync(identifier, args);
|
try
|
||||||
return true;
|
{
|
||||||
}
|
await module.InvokeVoidAsync(identifier, args);
|
||||||
catch (Exception exception)
|
return true;
|
||||||
{
|
}
|
||||||
LogInvocationFailure(exception, identifier);
|
catch (Exception exception)
|
||||||
return false;
|
{
|
||||||
|
LogInvocationFailure(exception, identifier);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+22
-25
@@ -49,32 +49,29 @@ internal static class MailToolArguments
|
|||||||
|
|
||||||
private static readonly string[] SPECIAL_FOLDER_VALUES = [SPECIAL_FOLDER_SENT, SPECIAL_FOLDER_DRAFTS];
|
private static readonly string[] SPECIAL_FOLDER_VALUES = [SPECIAL_FOLDER_SENT, SPECIAL_FOLDER_DRAFTS];
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds the conditions to the arguments a mail tool describes.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="builder">The schema of the tool.</param>
|
/// <param name="builder">The schema of the tool.</param>
|
||||||
/// <param name="toolAction">What the tool does with the mails, e.g., "search", completing "the mailboxes to ...".</param>
|
extension(ToolParameterSchemaBuilder builder)
|
||||||
/// <param name="mailboxIds">The ids of the mailboxes the tool offers, or none while no mailboxes are known.</param>
|
{
|
||||||
/// <returns>The schema, for further arguments.</returns>
|
/// <summary>
|
||||||
/// <remarks>
|
/// Adds the conditions to the arguments a mail tool describes.
|
||||||
/// A classic extension method instead of a member of an extension block: for those, the compiler of
|
/// </summary>
|
||||||
/// the .NET 10.0.1xx SDK warns about every mailbox id passed one by one to the params parameter
|
/// <param name="toolAction">What the tool does with the mails, e.g., "search", completing "the mailboxes to ...".</param>
|
||||||
/// (CS8620, dotnet/roslyn#80024). Move it once the SDK in metadata.txt carries the fix; MWAIS0016 makes
|
/// <param name="mailboxIds">The ids of the mailboxes the tool offers, or none while no mailboxes are known.</param>
|
||||||
/// the same exception until then.
|
/// <returns>The schema, for further arguments.</returns>
|
||||||
/// </remarks>
|
public ToolParameterSchemaBuilder AddMailConditions(string toolAction, params string[] mailboxIds) => builder
|
||||||
public static ToolParameterSchemaBuilder AddMailConditions(this ToolParameterSchemaBuilder builder, string toolAction, params string[] mailboxIds) => builder
|
.OptionalStringArray(MAILBOX_IDS_ARGUMENT, $"Optional IDs of the mailboxes to {toolAction}, out of those listed in the description of this tool. Leave it out to {toolAction} all of them.", mailboxIds)
|
||||||
.OptionalStringArray(MAILBOX_IDS_ARGUMENT, $"Optional IDs of the mailboxes to {toolAction}, out of those listed in the description of this tool. Leave it out to {toolAction} all of them.", mailboxIds)
|
.OptionalString(FROM_ARGUMENT, $"Optional part of the address or the name of the sender, such as 'alice@example.org', 'example.org', or 'Alice'. At most {MAX_ADDRESS_CHARACTERS} characters.")
|
||||||
.OptionalString(FROM_ARGUMENT, $"Optional part of the address or the name of the sender, such as 'alice@example.org', 'example.org', or 'Alice'. At most {MAX_ADDRESS_CHARACTERS} characters.")
|
.OptionalString(TO_ARGUMENT, $"Optional part of the address or the name of a recipient in To, Cc, or Bcc. At most {MAX_ADDRESS_CHARACTERS} characters.")
|
||||||
.OptionalString(TO_ARGUMENT, $"Optional part of the address or the name of a recipient in To, Cc, or Bcc. At most {MAX_ADDRESS_CHARACTERS} characters.")
|
.OptionalString(AFTER_ARGUMENT, "Optional: only mails received at this point in time or later. A date such as 2026-09-01 stands for the beginning of that day in the time zone of the user. A date with a time of day such as 2026-09-01T14:30 is read in that time zone as well, unless it ends with an offset such as +02:00 or with Z.")
|
||||||
.OptionalString(AFTER_ARGUMENT, "Optional: only mails received at this point in time or later. A date such as 2026-09-01 stands for the beginning of that day in the time zone of the user. A date with a time of day such as 2026-09-01T14:30 is read in that time zone as well, unless it ends with an offset such as +02:00 or with Z.")
|
.OptionalString(BEFORE_ARGUMENT, "Optional: only mails received before this point in time, in the same forms as the argument after. A date stands for the beginning of that day, so before 2026-09-30 leaves that day out.")
|
||||||
.OptionalString(BEFORE_ARGUMENT, "Optional: only mails received before this point in time, in the same forms as the argument after. A date stands for the beginning of that day, so before 2026-09-30 leaves that day out.")
|
.OptionalBoolean(IS_UNREAD_ARGUMENT, "Optional: true for unread mails only, false for read ones only.")
|
||||||
.OptionalBoolean(IS_UNREAD_ARGUMENT, "Optional: true for unread mails only, false for read ones only.")
|
.OptionalBoolean(IS_FLAGGED_ARGUMENT, "Optional: true for flagged mails only, false for unflagged ones only.")
|
||||||
.OptionalBoolean(IS_FLAGGED_ARGUMENT, "Optional: true for flagged mails only, false for unflagged ones only.")
|
.OptionalBoolean(IS_ENCRYPTED_ARGUMENT, "Optional: true for encrypted mails only, false for unencrypted ones only. AI Studio cannot read the content of encrypted mails, only their header.")
|
||||||
.OptionalBoolean(IS_ENCRYPTED_ARGUMENT, "Optional: true for encrypted mails only, false for unencrypted ones only. AI Studio cannot read the content of encrypted mails, only their header.")
|
.OptionalEnum(IMPORTANCE_ARGUMENT, "Optional importance the sender marked the mails with. Mails without such a mark count as normal.", IMPORTANCE_VALUES)
|
||||||
.OptionalEnum(IMPORTANCE_ARGUMENT, "Optional importance the sender marked the mails with. Mails without such a mark count as normal.", IMPORTANCE_VALUES)
|
.OptionalBoolean(HAS_ATTACHMENTS_ARGUMENT, "Optional: true for mails with attachments only, false for mails without any.")
|
||||||
.OptionalBoolean(HAS_ATTACHMENTS_ARGUMENT, "Optional: true for mails with attachments only, false for mails without any.")
|
.OptionalString(FOLDER_ARGUMENT, $"Optional full path of the folder the mails lie in, exactly as results show it, such as 'INBOX' or 'Archive/2026'. Subfolders are not included. For the sent mails or the drafts, use {SPECIAL_FOLDER_ARGUMENT} instead.")
|
||||||
.OptionalString(FOLDER_ARGUMENT, $"Optional full path of the folder the mails lie in, exactly as results show it, such as 'INBOX' or 'Archive/2026'. Subfolders are not included. For the sent mails or the drafts, use {SPECIAL_FOLDER_ARGUMENT} instead.")
|
.OptionalEnum(SPECIAL_FOLDER_ARGUMENT, $"Optional: only the mails the user sent, or only the drafts of the user, however the server names these folders. Cannot be combined with {FOLDER_ARGUMENT}.", SPECIAL_FOLDER_VALUES);
|
||||||
.OptionalEnum(SPECIAL_FOLDER_ARGUMENT, $"Optional: only the mails the user sent, or only the drafts of the user, however the server names these folders. Cannot be combined with {FOLDER_ARGUMENT}.", SPECIAL_FOLDER_VALUES);
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The value of the importance argument which stands for the given importance.
|
/// The value of the importance argument which stands for the given importance.
|
||||||
|
|||||||
@@ -20,10 +20,12 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<!--
|
<!--
|
||||||
Never above the Roslyn of the .NET SDK we build with (see metadata.txt): the 10.0.1xx SDKs ship
|
Never above the Roslyn of the oldest .NET SDK which may build AI Studio: the 10.0.1xx SDKs ship
|
||||||
Roslyn 5.0. Built against a newer one, the compiler skips these analyzers with CS9057. Raise
|
Roslyn 5.0. Built against a newer one, the compiler skips these analyzers with CS9057. The
|
||||||
both only together with the SDK feature band, with SourceGeneratedMappings, and with the Tests
|
pipeline builds with the SDK in metadata.txt, a 10.0.4xx with Roslyn 5.9, but the .NET packages
|
||||||
project.
|
of Linux distributions bring a 10.0.1xx, and so may the computer of a contributor. Raise both
|
||||||
|
only together with SourceGeneratedMappings and the Tests project, and only once a 10.0.1xx SDK
|
||||||
|
no longer has to build AI Studio.
|
||||||
-->
|
-->
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="5.0.0" />
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="5.0.0" />
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="5.0.0" />
|
||||||
|
|||||||
+2
-8
@@ -13,10 +13,7 @@ namespace SourceCodeRules.StyleAnalyzers;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// AI Studio declares its extension members in extension blocks, one per receiver, so that all members of a
|
/// AI Studio declares its extension members in extension blocks, one per receiver, so that all members of a
|
||||||
/// receiver read alike and extension properties can join them. A method with a params parameter stays a
|
/// receiver read alike and extension properties can join them.
|
||||||
/// classic one for now: for a member of an extension block, the compiler of the .NET 10.0.1xx SDK warns about
|
|
||||||
/// every argument passed one by one to a params parameter (CS8620, dotnet/roslyn#80024). Drop that exception
|
|
||||||
/// once the SDK in metadata.txt carries the fix.
|
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
#pragma warning disable RS1038
|
#pragma warning disable RS1038
|
||||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||||
@@ -29,7 +26,7 @@ public sealed class ClassicExtensionMethodAnalyzer : DiagnosticAnalyzer
|
|||||||
|
|
||||||
private const string MESSAGE_FORMAT = "Declare '{0}' as a member of an extension block for its receiver instead of with a this parameter";
|
private const string MESSAGE_FORMAT = "Declare '{0}' as a member of an extension block for its receiver instead of with a this parameter";
|
||||||
|
|
||||||
private const string DESCRIPTION = "AI Studio declares its extension members in extension blocks, one per receiver. Only a method with a params parameter stays a classic extension method, until the SDK fixes dotnet/roslyn#80024.";
|
private const string DESCRIPTION = "AI Studio declares its extension members in extension blocks, one per receiver.";
|
||||||
|
|
||||||
private const string CATEGORY = "Style";
|
private const string CATEGORY = "Style";
|
||||||
|
|
||||||
@@ -55,9 +52,6 @@ public sealed class ClassicExtensionMethodAnalyzer : DiagnosticAnalyzer
|
|||||||
if (thisKeyword.IsKind(SyntaxKind.None))
|
if (thisKeyword.IsKind(SyntaxKind.None))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (parameters.Any(parameter => parameter.Modifiers.Any(SyntaxKind.ParamsKeyword)))
|
|
||||||
return;
|
|
||||||
|
|
||||||
context.ReportDiagnostic(Diagnostic.Create(RULE, thisKeyword.GetLocation(), method.Identifier.Text));
|
context.ReportDiagnostic(Diagnostic.Create(RULE, thisKeyword.GetLocation(), method.Identifier.Text));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21,10 +21,13 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<!--
|
<!--
|
||||||
Never above the Roslyn of the .NET SDK we build with (see metadata.txt): the 10.0.1xx SDKs ship
|
Never above the Roslyn of the oldest .NET SDK which may build AI Studio: the 10.0.1xx SDKs ship
|
||||||
Roslyn 5.0. Built against a newer one, the compiler skips this generator with CS9057, and the
|
Roslyn 5.0. Built against a newer one, the compiler skips this generator with CS9057, and the
|
||||||
generated namespaces such as AIStudio.Tools.PluginSystem.Assistants.Icons go missing. Raise it
|
generated namespaces such as AIStudio.Tools.PluginSystem.Assistants.Icons go missing. The
|
||||||
only together with the SDK feature band, with SourceCodeRules, and with the Tests project.
|
pipeline builds with the SDK in metadata.txt, a 10.0.4xx with Roslyn 5.9, but the .NET packages
|
||||||
|
of Linux distributions bring a 10.0.1xx, and so may the computer of a contributor. Raise it only
|
||||||
|
together with SourceCodeRules and the Tests project, and only once a 10.0.1xx SDK no longer has
|
||||||
|
to build AI Studio.
|
||||||
-->
|
-->
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="5.0.0" />
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="5.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -11,9 +11,7 @@ namespace AIStudio.Tests.Analyzers;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// AI Studio declares all its extension members in extension blocks, one per receiver. MWAIS0016 refuses the
|
/// AI Studio declares all its extension members in extension blocks, one per receiver. MWAIS0016 refuses the
|
||||||
/// classic form while compiling, except for a method with a params parameter: for a member of an extension
|
/// classic form while compiling.
|
||||||
/// block, the compiler of the .NET 10.0.1xx SDK warns about every argument passed one by one to a params
|
|
||||||
/// parameter (CS8620, dotnet/roslyn#80024).
|
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public sealed class ClassicExtensionMethodAnalyzerTests
|
public sealed class ClassicExtensionMethodAnalyzerTests
|
||||||
@@ -24,6 +22,7 @@ public sealed class ClassicExtensionMethodAnalyzerTests
|
|||||||
[TestCase("public static T Echo<T>(this T value) where T : class => value;", "Echo", TestName = "A generic extension method")]
|
[TestCase("public static T Echo<T>(this T value) where T : class => value;", "Echo", TestName = "A generic extension method")]
|
||||||
[TestCase("public static void Bump(this ref int value) => value++;", "Bump", TestName = "An extension method with a ref receiver")]
|
[TestCase("public static void Bump(this ref int value) => value++;", "Bump", TestName = "An extension method with a ref receiver")]
|
||||||
[TestCase("public static System.Threading.Tasks.Task<string> LabelAsync(this Meeting meeting) => System.Threading.Tasks.Task.FromResult(meeting.Name);", "LabelAsync", TestName = "An asynchronous extension method")]
|
[TestCase("public static System.Threading.Tasks.Task<string> LabelAsync(this Meeting meeting) => System.Threading.Tasks.Task.FromResult(meeting.Name);", "LabelAsync", TestName = "An asynchronous extension method")]
|
||||||
|
[TestCase("public static string Label(this Meeting meeting, params string[] prefixes) => string.Concat(prefixes) + meeting.Name;", "Label", TestName = "An extension method with a params parameter")]
|
||||||
public async Task AClassicExtensionMethodIsRefused(string member, string name)
|
public async Task AClassicExtensionMethodIsRefused(string member, string name)
|
||||||
{
|
{
|
||||||
var reported = await AnalyzeAsync(member);
|
var reported = await AnalyzeAsync(member);
|
||||||
@@ -39,7 +38,6 @@ public sealed class ClassicExtensionMethodAnalyzerTests
|
|||||||
[TestCase("extension(Meeting meeting) { public string Label() => meeting.Name; }", TestName = "A member of an extension block")]
|
[TestCase("extension(Meeting meeting) { public string Label() => meeting.Name; }", TestName = "A member of an extension block")]
|
||||||
[TestCase("extension<T>(T value) where T : class { public T Echo() => value; }", TestName = "A member of a generic extension block")]
|
[TestCase("extension<T>(T value) where T : class { public T Echo() => value; }", TestName = "A member of a generic extension block")]
|
||||||
[TestCase("public static string Label(Meeting meeting) => meeting.Name;", TestName = "A static method without a this parameter")]
|
[TestCase("public static string Label(Meeting meeting) => meeting.Name;", TestName = "A static method without a this parameter")]
|
||||||
[TestCase("public static string Label(this Meeting meeting, params string[] prefixes) => string.Concat(prefixes) + meeting.Name;", TestName = "An extension method with a params parameter")]
|
|
||||||
public async Task AnythingElseIsLeftAlone(string member)
|
public async Task AnythingElseIsLeftAlone(string member)
|
||||||
{
|
{
|
||||||
var reported = await AnalyzeAsync(member);
|
var reported = await AnalyzeAsync(member);
|
||||||
|
|||||||
@@ -14,7 +14,8 @@
|
|||||||
<!--
|
<!--
|
||||||
Held at the version the two analyzer projects build against, so that a test compiling a
|
Held at the version the two analyzer projects build against, so that a test compiling a
|
||||||
snippet uses the same Roslyn the analyzers themselves were written for. Raise all three
|
snippet uses the same Roslyn the analyzers themselves were written for. Raise all three
|
||||||
together, and never above the Roslyn of the .NET SDK in metadata.txt (5.0 for 10.0.1xx).
|
together, and never above the Roslyn of the oldest .NET SDK which may build AI Studio (5.0 for
|
||||||
|
10.0.1xx), even though the SDK in metadata.txt ships a newer one; SourceCodeRules explains why.
|
||||||
-->
|
-->
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="5.0.0" />
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.11.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.11.0" />
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
26.10.1
|
26.10.1
|
||||||
2026-10-29 07:39:07 UTC
|
2026-10-29 07:39:07 UTC
|
||||||
259
|
259
|
||||||
10.0.114 (commit 0427b9abbe)
|
10.0.403 (commit 5a2162bea6)
|
||||||
10.0.14 (commit 0427b9abbe)
|
10.0.14 (commit 0427b9abbe)
|
||||||
1.99.1 (commit 58cf8f9c3)
|
1.99.1 (commit 58cf8f9c3)
|
||||||
9.12.0
|
9.12.0
|
||||||
|
|||||||
Reference in new issue
Block a user