diff --git a/.github/instructions/lc0092-naming-pattern.instructions.md b/.github/instructions/lc0092-naming-pattern.instructions.md index e84e3dd..dd4a65a 100644 --- a/.github/instructions/lc0092-naming-pattern.instructions.md +++ b/.github/instructions/lc0092-naming-pattern.instructions.md @@ -180,6 +180,7 @@ Invalid user-supplied patterns fail gracefully: `CompilePattern` catches `Argume **HasDiagnostic (9 cases):** ProcedureLowerCaseStart, VariableLowerCaseStart, VariableWithSpecialChars, ParameterLowerCaseStart, ReturnValueLowerCaseStart, ObjectLowerCaseStart, FieldWithSpecialChars, ActionLowerCaseStart, ControlLowerCaseStart. **NoDiagnostic (18 cases):** ProcedurePascalCase, VariablePascalCase, FieldWithLettersAndDigits, ObsoleteProcedure, TriggerMethod, InterfaceImplementingMethod, EventSubscriberPascalCase, EventSubscriberPlatformParams, EventSubscriberUserParams, ApiPageControlCamelCase, ActionAcceleratorKey, EnumValueBlankSpace, EnumValueLowerCaseStart, SingleLetterVariable, SingleLetterParameter, UnderscorePrefix, XRecVariable, XRecParameter, ParameterPascalCase. **HasDiagnosticWithCustomSettings (1 case):** EnumValueLowerCaseStartCustomSettings. +**SchemaParity (1 case):** NamingTargetEnumMatchesSchemaPropertyNames. ## Phase 2 roadmap (not yet implemented) diff --git a/.github/instructions/settings-schema.instructions.md b/.github/instructions/settings-schema.instructions.md index 2c490e6..676ffcc 100644 --- a/.github/instructions/settings-schema.instructions.md +++ b/.github/instructions/settings-schema.instructions.md @@ -1,5 +1,5 @@ --- -applyTo: 'src/ALCops.Common/Settings/ALCopsSettings.cs' +applyTo: 'src/**/{ALCopsSettings,alcops.schema,NamingPattern}*' --- # Settings Schema Synchronization @@ -31,6 +31,14 @@ When you: | Add a new `NamingTarget` enum value | Add to the `propertyNames.enum` array in `NamingPatterns` | | Add/modify fields in `NamingPattern.cs` | Update the `NamingPattern` definition in `$defs` | +## Required parity guard test + +For NamingPattern schema parity, the PR must include or update a test that validates `NamingPattern.NamingTarget` enum values against `NamingPatterns.propertyNames.enum` in `alcops.schema.json`. + +Minimum expectation: +- Run `NamingTargetEnumMatchesSchemaPropertyNames` in `src/ALCops.LinterCop.Test/Rules/NamingPattern/NamingPatternSettings.cs`. +- The test must fail when either side adds/removes a target without updating the other. + ## Description format Each property description should follow this pattern: diff --git a/src/ALCops.Common/Settings/alcops.schema.json b/src/ALCops.Common/Settings/alcops.schema.json index c2d56c2..660a023 100644 --- a/src/ALCops.Common/Settings/alcops.schema.json +++ b/src/ALCops.Common/Settings/alcops.schema.json @@ -51,7 +51,10 @@ "EventSubscriber", "EventDeclaration", "Variable", + "LocalVariable", + "GlobalVariable", "Parameter", + "VarParameter", "ReturnValue", "Object", "Field", @@ -65,9 +68,10 @@ } }, "UseSequentialGuidScope": { - "type": "string", - "enum": ["AllGuidFields"], - "description": "Controls the scope of the sequential GUID check. When set to 'AllGuidFields', all CreateGuid() calls are flagged, not just those in primary key fields. Used by PC0029." + "type": ["string", "null"], + "enum": ["AllGuidFields", null], + "default": null, + "description": "Controls the scope of the sequential GUID check. When set to 'AllGuidFields', all CreateGuid() calls are flagged, not just those in primary key fields. Used by PC0029. Default: null" }, "ToolTipAllowedPunctuations": { "type": "array", diff --git a/src/ALCops.LinterCop.Test/Rules/NamingPattern/NamingPatternSettings.cs b/src/ALCops.LinterCop.Test/Rules/NamingPattern/NamingPatternSettings.cs index 89479c0..ac74e37 100644 --- a/src/ALCops.LinterCop.Test/Rules/NamingPattern/NamingPatternSettings.cs +++ b/src/ALCops.LinterCop.Test/Rules/NamingPattern/NamingPatternSettings.cs @@ -1,4 +1,5 @@ using RoslynTestKit; +using System.Text.Json; using NamingPatternTarget = ALCops.LinterCop.Analyzers.NamingPattern.NamingTarget; using NamingPatternConfig = ALCops.LinterCop.Analyzers.NamingPattern.NamingPatternConfig; using NamingPatternSetting = ALCops.Common.Settings.NamingPattern; @@ -67,6 +68,68 @@ public async Task InheritanceFallsBackToVariableWhenOnlyVariableHasOverride( Assert.That(actual, Is.EqualTo(expected)); } + [Test] + public void NamingTargetEnumMatchesSchemaPropertyNames() + { + string repoRoot = FindRepositoryRoot(); + string schemaPath = Path.Combine(repoRoot, "src", "ALCops.Common", "Settings", "alcops.schema.json"); + + Assert.That(File.Exists(schemaPath), Is.True, $"Schema file not found: {schemaPath}"); + + using JsonDocument document = JsonDocument.Parse(File.ReadAllText(schemaPath)); + + JsonElement enumValues = document.RootElement + .GetProperty("properties") + .GetProperty("NamingPatterns") + .GetProperty("propertyNames") + .GetProperty("enum"); + + var schemaTargets = enumValues + .EnumerateArray() + .Where(item => item.ValueKind == JsonValueKind.String) + .Select(item => item.GetString()) + .OfType() + .ToHashSet(StringComparer.Ordinal); + + var namingTargets = Enum.GetNames(typeof(NamingPatternTarget)) + .ToHashSet(StringComparer.Ordinal); + + var missingInSchema = namingTargets.Except(schemaTargets).OrderBy(name => name).ToArray(); + var extraInSchema = schemaTargets.Except(namingTargets).OrderBy(name => name).ToArray(); + + Assert.Multiple(() => + { + Assert.That( + missingInSchema, + Is.Empty, + $"NamingTarget enum values missing in schema: {string.Join(", ", missingInSchema)}"); + + Assert.That( + extraInSchema, + Is.Empty, + $"Schema contains unknown NamingPatterns targets: {string.Join(", ", extraInSchema)}"); + }); + } + + private static string FindRepositoryRoot() + { + var current = new DirectoryInfo(Environment.CurrentDirectory); + + while (current is not null) + { + if (File.Exists(Path.Combine(current.FullName, "ALCops.sln"))) + { + return current.FullName; + } + + current = current.Parent; + } + + Assert.Fail("Could not locate repository root (ALCops.sln)."); + + return string.Empty; + } + private static string AllowPatternFor(string targetName) => $"{targetName}_Allow"; private static string DisallowPatternFor(string targetName) => $"{targetName}_Disallow";