Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 0 additions & 98 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -1,99 +1 @@
root = true

[*.cs]
# Treat ALL diagnostics as errors — compiler, analyzers, IDE, style
dotnet_analyzer_diagnostic.severity = error

# ──────────────────────────────────────────────────────────────────────────────
# Naming rules (dotnet/runtime style)
#
# Mirrors ANcpLua.NET.Sdk's NamingConvention.editorconfig. This repo doesn't
# consume the SDK, so the rules are inlined here under [*.cs] (no is_global —
# IDE picks them up via filesystem walk, no MSBuild registration needed).
# Source of truth: ANcpLua.NET.Sdk/src/Config/NamingConvention.editorconfig.
# ──────────────────────────────────────────────────────────────────────────────

dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case

# constant fields using PascalCase
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
dotnet_naming_symbols.constant_fields.applicable_kinds = field
dotnet_naming_symbols.constant_fields.required_modifiers = const
dotnet_naming_style.pascal_case_style.capitalization = pascal_case

# private/internal static fields (incl. readonly) use s_ prefix.
# Matches dotnet/runtime: private/internal static readonly is NOT PascalCase —
# the const rule above keeps const fields PascalCase; the fallback rule at the
# bottom keeps public/protected static (readonly) fields PascalCase.
dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion
dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields
dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style
dotnet_naming_symbols.static_fields.applicable_kinds = field
dotnet_naming_symbols.static_fields.required_modifiers = static
dotnet_naming_symbols.static_fields.applicable_accessibilities = private, internal, private_protected
dotnet_naming_style.static_prefix_style.required_prefix = s_
dotnet_naming_style.static_prefix_style.capitalization = camel_case

# private/internal instance fields use _camelCase.
# The static rule above is more specific (kinds + modifier + accessibility)
# and wins for static fields.
dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion
dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields
dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style
dotnet_naming_symbols.private_internal_fields.applicable_kinds = field
dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal
dotnet_naming_style.camel_case_underscore_style.required_prefix = _
dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case

# name all constant variables using PascalCase
dotnet_naming_rule.constant_variables_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.constant_variables_should_be_pascal_case.symbols = constant_variables
dotnet_naming_rule.constant_variables_should_be_pascal_case.style = pascal_case_style
dotnet_naming_symbols.constant_variables.applicable_kinds = local
dotnet_naming_symbols.constant_variables.required_modifiers = const

# Locals and parameters are camelCase
dotnet_naming_rule.locals_should_be_camel_case.severity = suggestion
dotnet_naming_rule.locals_should_be_camel_case.symbols = locals_and_parameters
dotnet_naming_rule.locals_should_be_camel_case.style = camel_case_style

dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local
dotnet_naming_style.camel_case_style.capitalization = camel_case

# Local functions are PascalCase
dotnet_naming_rule.local_functions_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.local_functions_should_be_pascal_case.symbols = local_functions
dotnet_naming_rule.local_functions_should_be_pascal_case.style = non_private_static_field_style

dotnet_naming_symbols.local_functions.applicable_kinds = local_function
dotnet_naming_style.local_function_style.capitalization = pascal_case

# Type Parameters
dotnet_naming_style.type_parameter_style.capitalization = pascal_case
dotnet_naming_style.type_parameter_style.required_prefix = T

dotnet_naming_rule.type_parameter_naming.symbols = type_parameter_symbol
dotnet_naming_rule.type_parameter_naming.style = type_parameter_style
dotnet_naming_rule.type_parameter_naming.severity = warning
dotnet_naming_symbols.type_parameter_symbol.applicable_kinds = type_parameter
dotnet_naming_symbols.type_parameter_symbol.applicable_accessibilities = *

# Interface
dotnet_naming_style.interface_style.capitalization = pascal_case
dotnet_naming_style.interface_style.required_prefix = I

dotnet_naming_rule.interface_should_be_begins_with_i.severity = warning
dotnet_naming_rule.interface_should_be_begins_with_i.style = interface_style
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface_symbols

dotnet_naming_symbols.interface_symbols.applicable_kinds = interface
dotnet_naming_symbols.interface_symbols.applicable_accessibilities = *

# By default, name items with PascalCase
dotnet_naming_rule.members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.members_should_be_pascal_case.symbols = all_members
dotnet_naming_rule.members_should_be_pascal_case.style = non_private_static_field_style

dotnet_naming_symbols.all_members.applicable_kinds = *
24 changes: 21 additions & 3 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project>
<!-- SDK-owned properties - centralized here, NOT in individual csproj files -->
<PropertyGroup>
<!-- Centralize all build output to artifacts/ -->
<!-- Centralize build output to artifacts/ -->
<ArtifactsPath>$(MSBuildThisFileDirectory)artifacts/</ArtifactsPath>
<BaseOutputPath>$(ArtifactsPath)bin/$(MSBuildProjectName)/</BaseOutputPath>
<BaseIntermediateOutputPath>$(ArtifactsPath)obj/$(MSBuildProjectName)/</BaseIntermediateOutputPath>
Expand All @@ -13,9 +14,16 @@

<!-- Deterministic builds for reproducibility -->
<Deterministic>true</Deterministic>
<LangVersion Condition="'$(LangVersion)' == ''">latest</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings Condition="'$(ImplicitUsings)' == ''">enable</ImplicitUsings>
<EnableNETAnalyzers Condition="'$(EnableNETAnalyzers)' == ''">true</EnableNETAnalyzers>
<AnalysisLevel Condition="'$(AnalysisLevel)' == ''">latest-all</AnalysisLevel>

<MSBuildTreatWarningsAsErrors Condition="'$(MSBuildTreatWarningsAsErrors)' == '' AND '$(ContinuousIntegrationBuild)' == 'true'">true</MSBuildTreatWarningsAsErrors>
<TreatWarningsAsErrors Condition="'$(TreatWarningsAsErrors)' == '' AND '$(ContinuousIntegrationBuild)' == 'true'">true</TreatWarningsAsErrors>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Set CI warning promotion before it is referenced

This condition depends on $(ContinuousIntegrationBuild), but that property is only assigned later in the same file (<PropertyGroup Condition="'$(CI)' == 'true'">). MSBuild evaluates properties in order, so in CI environments that rely on this file to set ContinuousIntegrationBuild, the condition here evaluates false and warnings are not elevated to errors. Set ContinuousIntegrationBuild earlier or key this condition directly off $(CI)/$(GITHUB_ACTIONS).

Useful? React with 👍 / 👎.

<EnforceCodeStyleInBuild Condition="'$(EnforceCodeStyleInBuild)' == ''">true</EnforceCodeStyleInBuild>

<!-- Treat all warnings, suggestions, and hints as errors -->
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<NoWarn>$(NoWarn);NU1604</NoWarn>
<NoWarn Condition="'$(TargetFramework)' == 'netstandard2.0'">$(NoWarn);NU1701</NoWarn>

Expand All @@ -26,6 +34,16 @@
<PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl>
</PropertyGroup>

<!-- Pull in the canonical ANcpLua.NET.Sdk analyzer/style conventions without importing the SDK package -->
<PropertyGroup>
<EnableEditorConfigDogfooding Condition="'$(EnableEditorConfigDogfooding)' == ''">true</EnableEditorConfigDogfooding>
</PropertyGroup>

<ItemGroup Condition="'$(EnableEditorConfigDogfooding)' == 'true'">
<EditorConfigFiles Include="$(MSBuildThisFileDirectory)../ANcpLua.NET.Sdk/src/Config/*.editorconfig"
Exclude="$(MSBuildThisFileDirectory)../ANcpLua.NET.Sdk/src/Config/ANcpLua.NET.Sdk*.editorconfig" />
Comment on lines +43 to +44

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add fallback when importing external analyzer configs

This import points to ../ANcpLua.NET.Sdk/..., but this repository’s CI checkout only pulls the current repo (see .github/workflows/nuget-publish.yml), so that sibling path is absent in normal builds. Because this commit also removes the in-repo .editorconfig rules, the glob resolves to no analyzer config files and the intended naming/style diagnostics silently stop being enforced. Please gate this include behind an Exists(...) check and keep a local fallback config so analyzer conventions still run when the sibling repo is not present.

Useful? React with 👍 / 👎.

</ItemGroup>

<!-- CI-specific settings -->
<PropertyGroup Condition="'$(CI)' == 'true'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
Expand Down
22 changes: 0 additions & 22 deletions src/ANcpLua.Roslyn.Utilities.Testing/AI/BitNetCollection.cs

This file was deleted.

85 changes: 0 additions & 85 deletions src/ANcpLua.Roslyn.Utilities.Testing/AI/BitNetFixture.cs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ public static async Task<FullPath> Get(NetSdkVersion version)
if (Path.GetExtension(file.Name) is ".zip")
{
using var ms = new MemoryStream(bytes);
var zip = new ZipArchive(ms);
using var zip = new ZipArchive(ms);
await zip.ExtractToDirectoryAsync(tempFolder, true).ConfigureAwait(false);
}
else
Expand Down Expand Up @@ -212,4 +212,4 @@ public static void ClearCache()
{
s_values.Clear();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ public abstract class IntegrationTestBase<TProgram>

/// <summary>Creates the WebApplicationFactory once per test class.</summary>
[OneTimeSetUp]
[System.Diagnostics.CodeAnalysis.SuppressMessage(
"Reliability",
"CA2000:Dispose objects before losing scope",
Justification = "The factory lifetime is stored on the test fixture and disposed by OneTimeTearDownAsync.")]
public virtual void OneTimeSetUp()
{
Factory = new WebApplicationFactory<TProgram>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ public abstract class KestrelTestBase<TProgram>

/// <summary>Creates a fresh Kestrel server per test.</summary>
[SetUp]
[System.Diagnostics.CodeAnalysis.SuppressMessage(
"Reliability",
"CA2000:Dispose objects before losing scope",
Justification = "The factory lifetime is stored in _factory and disposed by TearDownAsync.")]
public virtual void SetUp()
{
_factory = new WebApplicationFactory<TProgram>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ public abstract class IntegrationTestBase<TProgram>

/// <summary>Creates the factory, HTTP client, and log collector per test.</summary>
[Before(HookType.Test)]
[System.Diagnostics.CodeAnalysis.SuppressMessage(
"Reliability",
"CA2000:Dispose objects before losing scope",
Justification = "The factory lifetime is stored in _factory and disposed by TearDown.")]
public virtual Task SetUp()
{
_factory = new WebApplicationFactory<TProgram>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ public abstract class KestrelTestBase<TProgram>

/// <summary>Starts a fresh Kestrel server per test.</summary>
[Before(HookType.Test)]
[System.Diagnostics.CodeAnalysis.SuppressMessage(
"Reliability",
"CA2000:Dispose objects before losing scope",
Justification = "The factory lifetime is stored in _factory and disposed by TearDown.")]
public virtual Task SetUp()
{
_factory = new WebApplicationFactory<TProgram>()
Expand Down
2 changes: 2 additions & 0 deletions src/ANcpLua.Roslyn.Utilities/Encoding/Base64Url.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ namespace ANcpLua.Roslyn.Utilities;
#endif
static class Base64Url
{
#if NET5_0_OR_GREATER
private const int StackallocByteThreshold = 192;
#endif

/// <summary>
/// Encodes raw bytes as URL-safe Base64 without <c>=</c> padding.
Expand Down
4 changes: 4 additions & 0 deletions src/ANcpLua.Roslyn.Utilities/Ids/ShortId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ public static string NewHex(int hexChars)
/// tail is cryptographically strong. Suitable for tokens that appear in URLs or tenant ids.
/// </para>
/// </remarks>
[System.Diagnostics.CodeAnalysis.SuppressMessage(
"Globalization",
"CA1308:Normalize strings to uppercase",
Justification = "This helper's public contract returns lowercase hexadecimal identifiers.")]
public static string NewPrefixedSortable(string prefix, int randomHexChars = 24)
{
if (string.IsNullOrWhiteSpace(prefix))
Expand Down
10 changes: 4 additions & 6 deletions src/ANcpLua.Roslyn.Utilities/OperationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,14 +176,12 @@ public static bool IsInExpressionTree(this IOperation operation, INamedTypeSymbo
// Each of the four shapes that carry a type contributes one switch arm.
private static bool HasExpressionType(IOperation op, ISymbol expressionSymbol)
{
var type = op switch
return op switch
{
IArgumentOperation { Parameter.Type: { } pt } => pt,
IConversionOperation { Type: { } ct } => ct,
_ => null
IArgumentOperation { Parameter.Type: { } pt } => IsConstructedFromExpressionType(pt, expressionSymbol),
IConversionOperation { Type: { } ct } => IsConstructedFromExpressionType(ct, expressionSymbol),
_ => false
};

return type is not null && IsConstructedFromExpressionType(type, expressionSymbol);
}

/// <summary>
Expand Down
6 changes: 3 additions & 3 deletions src/ANcpLua.Roslyn.Utilities/ReflectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,13 @@ public static MethodInfo GetMethodFromGenericDefinition(this Type specializedTyp
#if NET6_0_OR_GREATER
return (MethodInfo)specializedType.GetMemberWithSameMetadataDefinitionAs(genericMethodDefinition);
#else
const BindingFlags all = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static |
const BindingFlags All = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static |
BindingFlags.Instance;
Comment on lines +120 to 121

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 MEDIUM RISK

Add BindingFlags.FlattenHierarchy to ensure static methods from base classes are included in the search.

Suggested change
const BindingFlags All = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static |
BindingFlags.Instance;
const BindingFlags All = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static |
BindingFlags.Instance | BindingFlags.FlattenHierarchy;

foreach (var m in specializedType.GetMethods(all))
foreach (var m in specializedType.GetMethods(All))
if (m.MetadataToken == genericMethodDefinition.MetadataToken)
return m;

throw new MissingMethodException(specializedType.FullName, genericMethodDefinition.Name);
#endif
}
}
}
4 changes: 4 additions & 0 deletions src/ANcpLua.Roslyn.Utilities/StringExtensions.Hashing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ public static string ToShortHash(this string input)
/// parallel branches. Cyclomatic complexity per overload stays at 1–2.
/// </para>
/// </remarks>
[System.Diagnostics.CodeAnalysis.SuppressMessage(
"Globalization",
"CA1308:Normalize strings to uppercase",
Justification = "The lowercase option intentionally formats hexadecimal output for callers that require it.")]
public static string ToShortHash(this string input, int hexChars, bool lowercase = false)
{
if (hexChars is < 1 or > 64)
Expand Down
4 changes: 4 additions & 0 deletions src/ANcpLua.Roslyn.Utilities/StringExtensions.Identifiers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ public static string ToPropertyName(this string input)
/// </para>
/// </remarks>
/// <seealso cref="ToPropertyName" />
[System.Diagnostics.CodeAnalysis.SuppressMessage(
"Globalization",
"CA1308:Normalize strings to uppercase",
Justification = "Parameter names and C# keyword escaping intentionally produce lowercase or camelCase names.")]
public static string ToParameterName(this string input)
{
if (input is null)
Expand Down
Loading
Loading