< Summary

Information
Class: NexusLabs.Needlr.Generators.DiagnosticDescriptors
Assembly: NexusLabs.Needlr.Generators
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.Generators/DiagnosticDescriptors.cs
Line coverage
100%
Covered lines: 514
Uncovered lines: 0
Coverable lines: 514
Total lines: 831
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%

File(s)

/home/runner/work/needlr/needlr/src/NexusLabs.Needlr.Generators/DiagnosticDescriptors.cs

#LineLine coverage
 1using Microsoft.CodeAnalysis;
 2
 3namespace NexusLabs.Needlr.Generators;
 4
 5/// <summary>
 6/// Contains diagnostic descriptors for the Needlr source generator.
 7/// </summary>
 8internal static class DiagnosticDescriptors
 9{
 10    private const string Category = "NexusLabs.Needlr.Generators";
 11    private const string HelpLinkBase = "https://github.com/nexus-labs/needlr/blob/main/docs/analyzers/";
 12
 13    /// <summary>
 14    /// NDLRGEN001: Internal type in referenced assembly cannot be registered.
 15    /// </summary>
 16    /// <remarks>
 17    /// This error is emitted when a type in a referenced assembly:
 18    /// - Matches the namespace filter
 19    /// - Would be registerable (injectable or plugin) if it were accessible
 20    /// - Is internal (not public) and thus inaccessible from the generated code
 21    ///
 22    /// To fix this error, add [GenerateTypeRegistry] to the referenced assembly
 23    /// so that it generates its own type registry that can access its internal types.
 24    /// </remarks>
 125    public static readonly DiagnosticDescriptor InaccessibleInternalType = new(
 126        id: "NDLRGEN001",
 127        title: "Internal type in referenced assembly cannot be registered",
 128        messageFormat: "Type '{0}' in assembly '{1}' is internal and cannot be registered. Add [GenerateTypeRegistry] at
 129        category: Category,
 130        defaultSeverity: DiagnosticSeverity.Error,
 131        isEnabledByDefault: true,
 132        description: "Internal types in referenced assemblies cannot be accessed by the generated code. To include inter
 133        helpLinkUri: HelpLinkBase + "NDLRGEN001.md");
 34
 35    /// <summary>
 36    /// NDLRGEN002: Referenced assembly has internal plugin types but no [GenerateTypeRegistry] attribute.
 37    /// </summary>
 38    /// <remarks>
 39    /// This error is emitted when a referenced assembly:
 40    /// - Contains internal types that implement plugin interfaces (e.g., IServiceCollectionPlugin)
 41    /// - Does not have a [GenerateTypeRegistry] attribute
 42    ///
 43    /// Without the attribute, the internal plugin types will not be registered and will
 44    /// silently fail to load at runtime.
 45    /// </remarks>
 146    public static readonly DiagnosticDescriptor MissingGenerateTypeRegistryAttribute = new(
 147        id: "NDLRGEN002",
 148        title: "Referenced assembly has internal plugin types but no type registry",
 149        messageFormat: "Assembly '{0}' contains internal plugin type '{1}' but has no [GenerateTypeRegistry] attribute. 
 150        category: Category,
 151        defaultSeverity: DiagnosticSeverity.Error,
 152        isEnabledByDefault: true,
 153        description: "Referenced assemblies with internal plugin types must have a [GenerateTypeRegistry] attribute to g
 154        helpLinkUri: HelpLinkBase + "NDLRGEN002.md");
 55
 56    /// <summary>
 57    /// NDLRGEN003: [GenerateFactory] on type with all injectable parameters is unnecessary.
 58    /// </summary>
 159    public static readonly DiagnosticDescriptor FactoryAllParamsInjectable = new(
 160        id: "NDLRGEN003",
 161        title: "[GenerateFactory] unnecessary - all parameters are injectable",
 162        messageFormat: "Type '{0}' has [GenerateFactory] but all constructor parameters are injectable. Consider removin
 163        category: Category,
 164        defaultSeverity: DiagnosticSeverity.Warning,
 165        isEnabledByDefault: true,
 166        description: "The [GenerateFactory] attribute generates a factory for types with mixed injectable and runtime pa
 167        helpLinkUri: HelpLinkBase + "NDLRGEN003.md");
 68
 69    /// <summary>
 70    /// NDLRGEN004: [GenerateFactory] on type with no injectable parameters provides low value.
 71    /// </summary>
 172    public static readonly DiagnosticDescriptor FactoryNoInjectableParams = new(
 173        id: "NDLRGEN004",
 174        title: "[GenerateFactory] has low value - no injectable parameters",
 175        messageFormat: "Type '{0}' has [GenerateFactory] but no constructor parameters are injectable. The factory provi
 176        category: Category,
 177        defaultSeverity: DiagnosticSeverity.Warning,
 178        isEnabledByDefault: true,
 179        description: "The [GenerateFactory] attribute is most useful when a type has a mix of injectable and runtime par
 180        helpLinkUri: HelpLinkBase + "NDLRGEN004.md");
 81
 82    /// <summary>
 83    /// NDLRGEN005: [GenerateFactory&lt;T&gt;] type argument must be an interface implemented by the class.
 84    /// </summary>
 185    public static readonly DiagnosticDescriptor FactoryTypeArgNotImplemented = new(
 186        id: "NDLRGEN005",
 187        title: "[GenerateFactory<T>] type argument not implemented",
 188        messageFormat: "Type '{0}' has [GenerateFactory<{1}>] but does not implement '{1}'. The type argument must be an
 189        category: Category,
 190        defaultSeverity: DiagnosticSeverity.Error,
 191        isEnabledByDefault: true,
 192        description: "When using [GenerateFactory<T>], T must be an interface that the decorated class implements. The f
 193        helpLinkUri: HelpLinkBase + "NDLRGEN005.md");
 94
 95    /// <summary>
 96    /// NDLRGEN006: [OpenDecoratorFor] type argument must be an open generic interface.
 97    /// </summary>
 198    public static readonly DiagnosticDescriptor OpenDecoratorTypeNotOpenGeneric = new(
 199        id: "NDLRGEN006",
 1100        title: "[OpenDecoratorFor] type must be an open generic interface",
 1101        messageFormat: "Type argument '{0}' in [OpenDecoratorFor] is not an open generic interface: {1}",
 1102        category: Category,
 1103        defaultSeverity: DiagnosticSeverity.Error,
 1104        isEnabledByDefault: true,
 1105        description: "The [OpenDecoratorFor] attribute requires an open generic interface type. Use typeof(IInterface<>)
 1106        helpLinkUri: HelpLinkBase + "NDLRGEN006.md");
 107
 108    /// <summary>
 109    /// NDLRGEN007: [OpenDecoratorFor] decorator class must be an open generic with matching arity.
 110    /// </summary>
 1111    public static readonly DiagnosticDescriptor OpenDecoratorClassNotOpenGeneric = new(
 1112        id: "NDLRGEN007",
 1113        title: "[OpenDecoratorFor] decorator must be an open generic class",
 1114        messageFormat: "Class '{0}' with [OpenDecoratorFor({1})] must be an open generic class with {2} type parameter(s
 1115        category: Category,
 1116        defaultSeverity: DiagnosticSeverity.Error,
 1117        isEnabledByDefault: true,
 1118        description: "When using [OpenDecoratorFor(typeof(IInterface<>))], the decorated class must also be an open gene
 1119        helpLinkUri: HelpLinkBase + "NDLRGEN007.md");
 120
 121    /// <summary>
 122    /// NDLRGEN008: [OpenDecoratorFor] decorator class must implement the open generic interface.
 123    /// </summary>
 1124    public static readonly DiagnosticDescriptor OpenDecoratorNotImplementingInterface = new(
 1125        id: "NDLRGEN008",
 1126        title: "[OpenDecoratorFor] decorator must implement the interface",
 1127        messageFormat: "Class '{0}' has [OpenDecoratorFor({1})] but does not implement '{1}'. The decorator must impleme
 1128        category: Category,
 1129        defaultSeverity: DiagnosticSeverity.Error,
 1130        isEnabledByDefault: true,
 1131        description: "A decorator must implement the same interface as the services it wraps. Ensure the class implement
 1132        helpLinkUri: HelpLinkBase + "NDLRGEN008.md");
 133
 134    // ============================================================================
 135    // Options Validation Analyzers (NDLRGEN014-021)
 136    // ============================================================================
 137
 138    /// <summary>
 139    /// NDLRGEN014: Validator type must implement IOptionsValidator&lt;T&gt; or have a valid Validate method.
 140    /// </summary>
 1141    public static readonly DiagnosticDescriptor ValidatorTypeMissingInterface = new(
 1142        id: "NDLRGEN014",
 1143        title: "Validator type has no validation method",
 1144        messageFormat: "Validator type '{0}' must have a Validate method. Implement IOptionsValidator<{1}> or add a 'Val
 1145        category: Category,
 1146        defaultSeverity: DiagnosticSeverity.Error,
 1147        isEnabledByDefault: true,
 1148        description: "When using Validator property on [Options], the specified type must have a valid validation method
 1149        helpLinkUri: HelpLinkBase + "NDLRGEN014.md");
 150
 151    /// <summary>
 152    /// NDLRGEN015: Validator's generic type parameter doesn't match the options type.
 153    /// </summary>
 1154    public static readonly DiagnosticDescriptor ValidatorTypeMismatch = new(
 1155        id: "NDLRGEN015",
 1156        title: "Validator type mismatch",
 1157        messageFormat: "Validator '{0}' validates '{1}' but is applied to options type '{2}'. The validator must be for 
 1158        category: Category,
 1159        defaultSeverity: DiagnosticSeverity.Error,
 1160        isEnabledByDefault: true,
 1161        description: "The Validator specified in [Options(Validator = ...)] must validate the same type as the options c
 1162        helpLinkUri: HelpLinkBase + "NDLRGEN015.md");
 163
 164    /// <summary>
 165    /// NDLRGEN016: ValidateMethod specified but method not found on target type.
 166    /// </summary>
 1167    public static readonly DiagnosticDescriptor ValidateMethodNotFound = new(
 1168        id: "NDLRGEN016",
 1169        title: "Validation method not found",
 1170        messageFormat: "Method '{0}' not found on type '{1}'. Ensure the method exists and has the correct signature.",
 1171        category: Category,
 1172        defaultSeverity: DiagnosticSeverity.Error,
 1173        isEnabledByDefault: true,
 1174        description: "The ValidateMethod specified in [Options] must exist on the options type (or Validator type if spe
 1175        helpLinkUri: HelpLinkBase + "NDLRGEN016.md");
 176
 177    /// <summary>
 178    /// NDLRGEN017: Validation method has incorrect signature.
 179    /// </summary>
 1180    public static readonly DiagnosticDescriptor ValidateMethodWrongSignature = new(
 1181        id: "NDLRGEN017",
 1182        title: "Validation method has wrong signature",
 1183        messageFormat: "Method '{0}' on type '{1}' has wrong signature, expected {2}",
 1184        category: Category,
 1185        defaultSeverity: DiagnosticSeverity.Error,
 1186        isEnabledByDefault: true,
 1187        description: "Validation methods must return IEnumerable<ValidationError> or IEnumerable<string>. Instance metho
 1188        helpLinkUri: HelpLinkBase + "NDLRGEN017.md");
 189
 190    /// <summary>
 191    /// NDLRGEN018: Validator specified but ValidateOnStart is false.
 192    /// </summary>
 1193    public static readonly DiagnosticDescriptor ValidatorWontRun = new(
 1194        id: "NDLRGEN018",
 1195        title: "Validator won't run",
 1196        messageFormat: "Validator '{0}' will not run because ValidateOnStart is false. Set ValidateOnStart = true to ena
 1197        category: Category,
 1198        defaultSeverity: DiagnosticSeverity.Warning,
 1199        isEnabledByDefault: true,
 1200        description: "When Validator is specified but ValidateOnStart is not enabled, the validator will never be invoke
 1201        helpLinkUri: HelpLinkBase + "NDLRGEN018.md");
 202
 203    /// <summary>
 204    /// NDLRGEN019: ValidateMethod specified but ValidateOnStart is false.
 205    /// </summary>
 1206    public static readonly DiagnosticDescriptor ValidateMethodWontRun = new(
 1207        id: "NDLRGEN019",
 1208        title: "Validation method won't run",
 1209        messageFormat: "ValidateMethod '{0}' will not run because ValidateOnStart is false. Set ValidateOnStart = true t
 1210        category: Category,
 1211        defaultSeverity: DiagnosticSeverity.Warning,
 1212        isEnabledByDefault: true,
 1213        description: "When ValidateMethod is specified but ValidateOnStart is not enabled, the validation method will ne
 1214        helpLinkUri: HelpLinkBase + "NDLRGEN019.md");
 215
 216    // ============================================================================
 217    // AOT Compatibility Analyzers (NDLRGEN020+)
 218    // ============================================================================
 219
 220    /// <summary>
 221    /// NDLRGEN020: [Options] attribute is not compatible with AOT/trimming.
 222    /// </summary>
 223    /// <remarks>
 224    /// The [Options] feature generates calls to Configure&lt;T&gt;() and BindConfiguration()
 225    /// which use reflection for configuration binding. These APIs are not AOT-compatible.
 226    /// </remarks>
 1227    public static readonly DiagnosticDescriptor OptionsNotAotCompatible = new(
 1228        id: "NDLRGEN020",
 1229        title: "[Options] is not compatible with AOT",
 1230        messageFormat: "Type '{0}' has [Options] attribute but is in an AOT-enabled project. The [Options] feature uses 
 1231        category: Category,
 1232        defaultSeverity: DiagnosticSeverity.Error,
 1233        isEnabledByDefault: true,
 1234        description: "The [Options] attribute generates code that calls Configure<T>() and BindConfiguration() which use
 1235        helpLinkUri: HelpLinkBase + "NDLRGEN020.md");
 236
 237    /// <summary>
 238    /// NDLRGEN021: Positional record with [Options] must be declared partial.
 239    /// </summary>
 240    /// <remarks>
 241    /// Positional records lack parameterless constructors, which are required for
 242    /// configuration binding. When the record is partial, the generator can emit
 243    /// a parameterless constructor. Non-partial records cannot be extended.
 244    /// </remarks>
 1245    public static readonly DiagnosticDescriptor PositionalRecordMustBePartial = new(
 1246        id: "NDLRGEN021",
 1247        title: "Positional record must be partial for [Options]",
 1248        messageFormat: "Positional record '{0}' has [Options] but is not declared partial. Add the 'partial' modifier to
 1249        category: Category,
 1250        defaultSeverity: DiagnosticSeverity.Warning,
 1251        isEnabledByDefault: true,
 1252        description: "Positional records (records with primary constructor parameters) lack parameterless constructors, 
 1253        helpLinkUri: HelpLinkBase + "NDLRGEN021.md");
 254
 255    /// <summary>
 256    /// NDLRGEN022: Disposable service may be captured by a longer-lived service.
 257    /// </summary>
 258    /// <remarks>
 259    /// This error is emitted when a longer-lived service (e.g., Singleton) has a constructor
 260    /// dependency on a shorter-lived service (e.g., Scoped, Transient) that implements
 261    /// IDisposable or IAsyncDisposable. This is a "captive dependency" anti-pattern where
 262    /// the disposable may be disposed while the consuming service still holds a reference.
 263    ///
 264    /// Unlike NDLRCOR012 which only detects explicit lifetime attributes, this diagnostic
 265    /// uses inferred lifetimes from Needlr's convention-based discovery, catching more issues.
 266    /// </remarks>
 1267    public static readonly DiagnosticDescriptor DisposableCaptiveDependency = new(
 1268        id: "NDLRGEN022",
 1269        title: "Disposable captive dependency detected",
 1270        messageFormat: "'{0}' ({1}) depends on '{2}' ({3}) which implements IDisposable. The disposable may be disposed 
 1271        category: Category,
 1272        defaultSeverity: DiagnosticSeverity.Error,
 1273        isEnabledByDefault: true,
 1274        description: "A longer-lived service captures a shorter-lived disposable dependency. When the shorter-lived scop
 1275        helpLinkUri: HelpLinkBase + "NDLRGEN022.md");
 276
 277    /// <summary>
 278    /// NDLRGEN030: DataAnnotation attribute cannot be source-generated.
 279    /// </summary>
 1280    public static readonly DiagnosticDescriptor UnsupportedDataAnnotation = new(
 1281        id: "NDLRGEN030",
 1282        title: "DataAnnotation attribute cannot be source-generated",
 1283        messageFormat: "DataAnnotation '{0}' on '{1}.{2}' cannot be source-generated. In AOT mode, this validation will 
 1284        category: Category,
 1285        defaultSeverity: DiagnosticSeverity.Warning,
 1286        isEnabledByDefault: true,
 1287        description: "This DataAnnotation validation attribute cannot be source-generated because it requires runtime re
 1288        helpLinkUri: HelpLinkBase + "NDLRGEN030.md");
 289
 290    // ============================================================================
 291    // Provider Analyzers (NDLRGEN031-034)
 292    // ============================================================================
 293
 294    /// <summary>
 295    /// NDLRGEN031: [Provider] on class requires `partial` modifier.
 296    /// </summary>
 1297    public static readonly DiagnosticDescriptor ProviderClassNotPartial = new(
 1298        id: "NDLRGEN031",
 1299        title: "[Provider] on class requires partial modifier",
 1300        messageFormat: "Class '{0}' has [Provider] attribute but is not declared partial. Add the 'partial' modifier to 
 1301        category: Category,
 1302        defaultSeverity: DiagnosticSeverity.Error,
 1303        isEnabledByDefault: true,
 1304        description: "When applying [Provider] to a class (shorthand mode), the class must be declared partial so the ge
 1305        helpLinkUri: HelpLinkBase + "NDLRGEN031.md");
 306
 307    /// <summary>
 308    /// NDLRGEN032: [Provider] interface must only contain get-only properties.
 309    /// </summary>
 1310    public static readonly DiagnosticDescriptor ProviderInterfaceInvalidMember = new(
 1311        id: "NDLRGEN032",
 1312        title: "[Provider] interface has invalid member",
 1313        messageFormat: "Interface '{0}' has [Provider] attribute but contains {1}. Provider interfaces must only contain
 1314        category: Category,
 1315        defaultSeverity: DiagnosticSeverity.Error,
 1316        isEnabledByDefault: true,
 1317        description: "Provider interfaces should only contain get-only properties that represent the services to be prov
 1318        helpLinkUri: HelpLinkBase + "NDLRGEN032.md");
 319
 320    /// <summary>
 321    /// NDLRGEN033: Provider property type is a concrete class, consider using an interface.
 322    /// </summary>
 1323    public static readonly DiagnosticDescriptor ProviderPropertyConcreteType = new(
 1324        id: "NDLRGEN033",
 1325        title: "Provider property uses concrete type",
 1326        messageFormat: "Provider property '{0}.{1}' has type '{2}' which is a concrete class. Consider using an interfac
 1327        category: Category,
 1328        defaultSeverity: DiagnosticSeverity.Warning,
 1329        isEnabledByDefault: true,
 1330        description: "Provider properties represent services resolved from the DI container. Using interface types inste
 1331        helpLinkUri: HelpLinkBase + "NDLRGEN033.md");
 332
 333    /// <summary>
 334    /// NDLRGEN034: Circular provider dependency detected.
 335    /// </summary>
 1336    public static readonly DiagnosticDescriptor ProviderCircularDependency = new(
 1337        id: "NDLRGEN034",
 1338        title: "Circular provider dependency detected",
 1339        messageFormat: "Provider '{0}' has a circular dependency: {1}",
 1340        category: Category,
 1341        defaultSeverity: DiagnosticSeverity.Error,
 1342        isEnabledByDefault: true,
 1343        description: "A circular dependency was detected in the provider dependency graph. Provider A references Provide
 1344        helpLinkUri: HelpLinkBase + "NDLRGEN034.md");
 345
 346    // ============================================================================
 347    // Composition Analyzers (NDLRGEN035-038)
 348    // ============================================================================
 349
 350    /// <summary>
 351    /// NDLRGEN035: [RegisterClosedOverImplementationsOf] source type must be an open generic interface.
 352    /// </summary>
 1353    public static readonly DiagnosticDescriptor ComposedSourceNotOpenGenericInterface = new(
 1354        id: "NDLRGEN035",
 1355        title: "[RegisterClosedOverImplementationsOf] source type must be an open generic interface",
 1356        messageFormat: "Source type argument '{0}' in [RegisterClosedOverImplementationsOf] is not an open generic inter
 1357        category: Category,
 1358        defaultSeverity: DiagnosticSeverity.Error,
 1359        isEnabledByDefault: true,
 1360        description: "The [RegisterClosedOverImplementationsOf] attribute requires an open generic interface type whose 
 1361        helpLinkUri: HelpLinkBase + "NDLRGEN035.md");
 362
 363    /// <summary>
 364    /// NDLRGEN036: [RegisterClosedOverImplementationsOf] composition class must be an open generic with matching arity.
 365    /// </summary>
 1366    public static readonly DiagnosticDescriptor ComposedClassNotOpenGeneric = new(
 1367        id: "NDLRGEN036",
 1368        title: "[RegisterClosedOverImplementationsOf] composition must be an open generic class",
 1369        messageFormat: "Class '{0}' with [RegisterClosedOverImplementationsOf({1})] must be an open generic class with {
 1370        category: Category,
 1371        defaultSeverity: DiagnosticSeverity.Error,
 1372        isEnabledByDefault: true,
 1373        description: "When using [RegisterClosedOverImplementationsOf(typeof(IInterface<>))], the annotated composition 
 1374        helpLinkUri: HelpLinkBase + "NDLRGEN036.md");
 375
 376    /// <summary>
 377    /// NDLRGEN037: [RegisterClosedOverImplementationsOf] composition class must implement the As service type.
 378    /// </summary>
 1379    public static readonly DiagnosticDescriptor ComposedClassNotImplementingAs = new(
 1380        id: "NDLRGEN037",
 1381        title: "[RegisterClosedOverImplementationsOf] composition must implement the As service type",
 1382        messageFormat: "Class '{0}' must implement the 'As' service type specified by [RegisterClosedOverImplementations
 1383        category: Category,
 1384        defaultSeverity: DiagnosticSeverity.Error,
 1385        isEnabledByDefault: true,
 1386        description: "Each closed composition is registered as the service type named by 'As', so the annotated class mu
 1387        helpLinkUri: HelpLinkBase + "NDLRGEN037.md");
 388
 389    /// <summary>
 390    /// NDLRGEN038: A discovered type argument cannot legally close the composition type.
 391    /// </summary>
 392    /// <remarks>
 393    /// Emitted by the generator (not the per-attribute analyzer) because only the generator knows the
 394    /// full set of discovered implementations. The offending registration is skipped rather than emitting
 395    /// code that fails to compile, turning a would-be runtime absence into a build-time signal.
 396    /// </remarks>
 1397    public static readonly DiagnosticDescriptor ComposedTypeArgumentViolatesConstraints = new(
 1398        id: "NDLRGEN038",
 1399        title: "[RegisterClosedOverImplementationsOf] discovered type argument violates composition constraints",
 1400        messageFormat: "Composition '{0}' cannot be closed over type argument(s) '{1}' from an implementation of '{2}' b
 1401        category: Category,
 1402        defaultSeverity: DiagnosticSeverity.Warning,
 1403        isEnabledByDefault: true,
 1404        description: "An implementation of the designated open generic interface was discovered whose type argument(s) d
 1405        helpLinkUri: HelpLinkBase + "NDLRGEN038.md");
 406
 407    // ============================================================================
 408    // Generated Constructor Analyzer (NDLRGEN039-056)
 409    // ============================================================================
 410
 411    /// <summary>
 412    /// NDLRGEN039: A type using generated-constructor generation must be declared partial.
 413    /// </summary>
 1414    public static readonly DiagnosticDescriptor GeneratedConstructorRequiresPartialType = new(
 1415        id: "NDLRGEN039",
 1416        title: "Generated-constructor type must be partial",
 1417        messageFormat: "Type '{0}' must be declared 'partial' because it uses [GenerateConstructor] or a field-level con
 1418        category: Category,
 1419        defaultSeverity: DiagnosticSeverity.Error,
 1420        isEnabledByDefault: true,
 1421        description: "Roslyn source generators can only contribute new members to a type declared partial. Add the 'part
 1422        helpLinkUri: HelpLinkBase + "NDLRGEN039.md");
 423
 424    /// <summary>
 425    /// NDLRGEN040: A record type or nested type cannot use generated-constructor generation.
 426    /// </summary>
 1427    public static readonly DiagnosticDescriptor GeneratedConstructorUnsupportedTypeShape = new(
 1428        id: "NDLRGEN040",
 1429        title: "Generated-constructor type shape is unsupported",
 1430        messageFormat: "Type '{0}' cannot use generated-constructor generation because it is {1}",
 1431        category: Category,
 1432        defaultSeverity: DiagnosticSeverity.Error,
 1433        isEnabledByDefault: true,
 1434        description: "Generated-constructor generation only supports top-level, non-record partial classes. Records and 
 1435        helpLinkUri: HelpLinkBase + "NDLRGEN040.md");
 436
 437    /// <summary>
 438    /// NDLRGEN041: An explicit instance constructor conflicts with generated-constructor generation.
 439    /// </summary>
 1440    public static readonly DiagnosticDescriptor GeneratedConstructorConflictsWithExplicitConstructor = new(
 1441        id: "NDLRGEN041",
 1442        title: "Generated-constructor conflicts with an explicit constructor",
 1443        messageFormat: "Type '{0}' declares an explicit instance constructor, which conflicts with generated-constructor
 1444        category: Category,
 1445        defaultSeverity: DiagnosticSeverity.Error,
 1446        isEnabledByDefault: true,
 1447        description: "Generated-constructor generation requires exactly one unambiguous constructor. A type with its own
 1448        helpLinkUri: HelpLinkBase + "NDLRGEN041.md");
 449
 450    /// <summary>
 451    /// NDLRGEN042: The base type must expose an accessible parameterless constructor.
 452    /// </summary>
 1453    public static readonly DiagnosticDescriptor GeneratedConstructorBaseTypeRequiresParameterlessConstructor = new(
 1454        id: "NDLRGEN042",
 1455        title: "Generated-constructor base type requires a parameterless constructor",
 1456        messageFormat: "Type '{0}' derives from '{1}', which has no accessible parameterless constructor; generated-cons
 1457        category: Category,
 1458        defaultSeverity: DiagnosticSeverity.Error,
 1459        isEnabledByDefault: true,
 1460        description: "The generated constructor relies on the implicit base() call. A base type that requires constructo
 1461        helpLinkUri: HelpLinkBase + "NDLRGEN042.md");
 462
 463    /// <summary>
 464    /// NDLRGEN043: No eligible field exists for generated-constructor generation.
 465    /// </summary>
 1466    public static readonly DiagnosticDescriptor GeneratedConstructorNoEligibleFields = new(
 1467        id: "NDLRGEN043",
 1468        title: "No eligible field for generated-constructor generation",
 1469        messageFormat: "Type '{0}' has no private readonly instance field without an initializer, so generated-construct
 1470        category: Category,
 1471        defaultSeverity: DiagnosticSeverity.Error,
 1472        isEnabledByDefault: true,
 1473        description: "Generated-constructor generation requires at least one eligible field: a private, instance, readon
 1474        helpLinkUri: HelpLinkBase + "NDLRGEN043.md");
 475
 476    /// <summary>
 477    /// NDLRGEN044: Normalized constructor parameter names collide.
 478    /// </summary>
 1479    public static readonly DiagnosticDescriptor GeneratedConstructorParameterNameCollision = new(
 1480        id: "NDLRGEN044",
 1481        title: "Generated-constructor parameter names collide",
 1482        messageFormat: "Type '{0}' has two or more eligible fields that normalize to the same constructor parameter name
 1483        category: Category,
 1484        defaultSeverity: DiagnosticSeverity.Error,
 1485        isEnabledByDefault: true,
 1486        description: "Field names are normalized to constructor parameter names by removing a leading underscore and low
 1487        helpLinkUri: HelpLinkBase + "NDLRGEN044.md");
 488
 489    /// <summary>
 490    /// NDLRGEN045: A constructor guard exclusion attribute has no effect.
 491    /// </summary>
 1492    public static readonly DiagnosticDescriptor ConstructorGuardAttributeHasNoEffect = new(
 1493        id: "NDLRGEN045",
 1494        title: "Constructor guard attribute has no effect",
 1495        messageFormat: "Field '{0}' has {1}, but its containing type has no [GenerateConstructor] and no other field has
 1496        category: Category,
 1497        defaultSeverity: DiagnosticSeverity.Warning,
 1498        isEnabledByDefault: true,
 1499        description: "[ConstructorIgnore] and [ConstructorGuard(ConstructorGuardKind.None)] are exclusion-only modifiers
 1500        helpLinkUri: HelpLinkBase + "NDLRGEN045.md");
 501
 502    /// <summary>
 503    /// NDLRGEN046: A constructor guard attribute is applied to an ineligible field.
 504    /// </summary>
 1505    public static readonly DiagnosticDescriptor ConstructorGuardAttributeOnIneligibleField = new(
 1506        id: "NDLRGEN046",
 1507        title: "Constructor guard attribute applied to an ineligible field",
 1508        messageFormat: "Field '{0}' cannot participate in generated-constructor generation because it is {1}, so its con
 1509        category: Category,
 1510        defaultSeverity: DiagnosticSeverity.Error,
 1511        isEnabledByDefault: true,
 1512        description: "Only a private, instance, readonly field without an initializer can become a generated-constructor
 1513        helpLinkUri: HelpLinkBase + "NDLRGEN046.md");
 514
 515    /// <summary>
 516    /// NDLRGEN047: A guard-mode enum argument is not a defined value.
 517    /// </summary>
 1518    public static readonly DiagnosticDescriptor InvalidConstructorGuardEnumValue = new(
 1519        id: "NDLRGEN047",
 1520        title: "Invalid constructor guard enum value",
 1521        messageFormat: "'{0}' is not a defined {1} value",
 1522        category: Category,
 1523        defaultSeverity: DiagnosticSeverity.Error,
 1524        isEnabledByDefault: true,
 1525        description: "ConstructorGuardKind and ConstructorNullGuardMode arguments must be one of the enum's defined memb
 1526        helpLinkUri: HelpLinkBase + "NDLRGEN047.md");
 527
 528    /// <summary>
 529    /// NDLRGEN048: A built-in guard kind is incompatible with the field's type.
 530    /// </summary>
 1531    public static readonly DiagnosticDescriptor ConstructorGuardIncompatibleWithFieldType = new(
 1532        id: "NDLRGEN048",
 1533        title: "Constructor guard incompatible with member type",
 1534        messageFormat: "The '{0}' guard cannot be used on member '{1}' of type '{2}': {3}",
 1535        category: Category,
 1536        defaultSeverity: DiagnosticSeverity.Error,
 1537        isEnabledByDefault: true,
 1538        description: "NotNullOrEmpty and NotNullOrWhiteSpace only apply to string-compatible fields or properties. NotNu
 1539        helpLinkUri: HelpLinkBase + "NDLRGEN048.md");
 540
 541    /// <summary>
 542    /// NDLRGEN049: A custom guard type is missing or inaccessible.
 543    /// </summary>
 1544    public static readonly DiagnosticDescriptor ConstructorGuardTypeInvalid = new(
 1545        id: "NDLRGEN049",
 1546        title: "Custom constructor guard type is invalid",
 1547        messageFormat: "The custom guard type for member '{0}' is invalid: {1}",
 1548        category: Category,
 1549        defaultSeverity: DiagnosticSeverity.Error,
 1550        isEnabledByDefault: true,
 1551        description: "A custom constructor guard type must exist and be accessible from the generated constructor. Fix t
 1552        helpLinkUri: HelpLinkBase + "NDLRGEN049.md");
 553
 554    /// <summary>
 555    /// NDLRGEN050: A custom guard method name is empty or whitespace.
 556    /// </summary>
 1557    public static readonly DiagnosticDescriptor ConstructorGuardMethodNameInvalid = new(
 1558        id: "NDLRGEN050",
 1559        title: "Custom constructor guard method name is invalid",
 1560        messageFormat: "The custom guard method name for member '{0}' must not be empty or consist only of white space",
 1561        category: Category,
 1562        defaultSeverity: DiagnosticSeverity.Error,
 1563        isEnabledByDefault: true,
 1564        description: "An explicit custom guard method name must be a non-empty, non-whitespace identifier, typically sup
 1565        helpLinkUri: HelpLinkBase + "NDLRGEN050.md");
 566
 567    /// <summary>
 568    /// NDLRGEN051: No compatible custom guard method was found.
 569    /// </summary>
 1570    public static readonly DiagnosticDescriptor ConstructorGuardMethodInvalid = new(
 1571        id: "NDLRGEN051",
 1572        title: "Custom constructor guard method is invalid",
 1573        messageFormat: "Guard method '{0}' on type '{1}' is not valid for member '{2}' of type '{3}': {4}",
 1574        category: Category,
 1575        defaultSeverity: DiagnosticSeverity.Error,
 1576        isEnabledByDefault: true,
 1577        description: "A custom guard method must be an accessible static method returning void with exactly two paramete
 1578        helpLinkUri: HelpLinkBase + "NDLRGEN051.md");
 579
 580    /// <summary>
 581    /// NDLRGEN052: Multiple custom guard method overloads match the field.
 582    /// </summary>
 1583    public static readonly DiagnosticDescriptor ConstructorGuardMethodAmbiguous = new(
 1584        id: "NDLRGEN052",
 1585        title: "Custom constructor guard method is ambiguous",
 1586        messageFormat: "Multiple accessible static methods named '{0}' on type '{1}' are compatible with member '{2}' of
 1587        category: Category,
 1588        defaultSeverity: DiagnosticSeverity.Error,
 1589        isEnabledByDefault: true,
 1590        description: "Overload resolution for a custom guard method is intentionally simple and direct rather than full 
 1591        helpLinkUri: HelpLinkBase + "NDLRGEN052.md");
 592
 593    /// <summary>
 594    /// NDLRGEN053: [ConstructorGuardDefinition] is applied to an unsupported attribute type.
 595    /// </summary>
 1596    public static readonly DiagnosticDescriptor ConstructorGuardDefinitionTargetInvalid = new(
 1597        id: "NDLRGEN053",
 1598        title: "[ConstructorGuardDefinition] target is invalid",
 1599        messageFormat: "'{0}' cannot carry [ConstructorGuardDefinition] because it is {1}",
 1600        category: Category,
 1601        defaultSeverity: DiagnosticSeverity.Error,
 1602        isEnabledByDefault: true,
 1603        description: "[ConstructorGuardDefinition] must decorate an Attribute-derived type whose [AttributeUsage] allows
 1604        helpLinkUri: HelpLinkBase + "NDLRGEN053.md");
 605
 606    /// <summary>
 607    /// NDLRGEN054: A [ConstructorGuardDefinition]'s own guard reference is unresolved.
 608    /// </summary>
 1609    public static readonly DiagnosticDescriptor ConstructorGuardDefinitionUnresolvedGuard = new(
 1610        id: "NDLRGEN054",
 1611        title: "[ConstructorGuardDefinition] guard contract is unresolved",
 1612        messageFormat: "[ConstructorGuardDefinition] on '{0}' is invalid: {1}",
 1613        category: Category,
 1614        defaultSeverity: DiagnosticSeverity.Error,
 1615        isEnabledByDefault: true,
 1616        description: "The guard type and method referenced by [ConstructorGuardDefinition] must resolve to the same acce
 1617        helpLinkUri: HelpLinkBase + "NDLRGEN054.md");
 618
 619    /// <summary>
 620    /// NDLRGEN055: A parameterized custom guard alias usage forwards an argument shape
 621    /// or a named argument/property that this version does not support forwarding.
 622    /// </summary>
 1623    public static readonly DiagnosticDescriptor ConstructorGuardAliasUsageArgumentUnsupported = new(
 1624        id: "NDLRGEN055",
 1625        title: "Constructor guard alias usage argument is unsupported",
 1626        messageFormat: "The alias attribute usage on member '{0}' is invalid: {1}",
 1627        category: Category,
 1628        defaultSeverity: DiagnosticSeverity.Error,
 1629        isEnabledByDefault: true,
 1630        description: "A parameterized custom guard alias forwards every positional constructor argument of its usage ont
 1631        helpLinkUri: HelpLinkBase + "NDLRGEN055.md");
 632
 633    /// <summary>
 634    /// NDLRGEN056: A custom guard method is incompatible with the arguments a
 635    /// parameterized alias usage forwards to it, either because the method's effective
 636    /// arity does not match the number of forwarded arguments, or because one of the
 637    /// forwarded argument types is incompatible with the corresponding parameter.
 638    /// </summary>
 1639    public static readonly DiagnosticDescriptor ConstructorGuardForwardedArgumentIncompatible = new(
 1640        id: "NDLRGEN056",
 1641        title: "Custom constructor guard method is incompatible with forwarded alias arguments",
 1642        messageFormat: "Guard method '{0}' on type '{1}' cannot accept the arguments forwarded for member '{2}' of type 
 1643        category: Category,
 1644        defaultSeverity: DiagnosticSeverity.Error,
 1645        isEnabledByDefault: true,
 1646        description: "A parameterized custom guard alias usage forwards its own positional constructor arguments between
 1647        helpLinkUri: HelpLinkBase + "NDLRGEN056.md");
 648
 649    // ============================================================================
 650    // Record Constructor Overload Analyzer (NDLRGEN057-062)
 651    // ============================================================================
 652
 653    /// <summary>
 654    /// NDLRGEN057: A record receiving a generated constructor overload must be partial.
 655    /// </summary>
 1656    public static readonly DiagnosticDescriptor RecordConstructorOverloadRequiresPartialType = new(
 1657        id: "NDLRGEN057",
 1658        title: "Record constructor-overload type must be partial",
 1659        messageFormat: "Record '{0}' must be declared 'partial' because [RecordConstructorOverloadParameter] requires a 
 1660        category: Category,
 1661        defaultSeverity: DiagnosticSeverity.Error,
 1662        isEnabledByDefault: true,
 1663        description: "A generated forwarding constructor can only be contributed to a partial positional record class. A
 1664        helpLinkUri: HelpLinkBase + "NDLRGEN057.md");
 665
 666    /// <summary>
 667    /// NDLRGEN058: The marked property's containing type is not a supported record.
 668    /// </summary>
 1669    public static readonly DiagnosticDescriptor RecordConstructorOverloadUnsupportedTypeShape = new(
 1670        id: "NDLRGEN058",
 1671        title: "Record constructor-overload type shape is unsupported",
 1672        messageFormat: "Type '{0}' cannot use [RecordConstructorOverloadParameter] because it is {1}",
 1673        category: Category,
 1674        defaultSeverity: DiagnosticSeverity.Error,
 1675        isEnabledByDefault: true,
 1676        description: "Record constructor-overload generation supports only top-level, non-file-local positional record c
 1677        helpLinkUri: HelpLinkBase + "NDLRGEN058.md");
 678
 679    /// <summary>
 680    /// NDLRGEN059: A marked property cannot be assigned by the generated overload.
 681    /// </summary>
 1682    public static readonly DiagnosticDescriptor RecordConstructorOverloadPropertyUnsupported = new(
 1683        id: "NDLRGEN059",
 1684        title: "Record constructor-overload property is unsupported",
 1685        messageFormat: "Property '{0}' cannot participate in a generated record constructor overload because it is {1}",
 1686        category: Category,
 1687        defaultSeverity: DiagnosticSeverity.Error,
 1688        isEnabledByDefault: true,
 1689        description: "A participating property must be a non-static, non-indexer, non-required, assignable instance prop
 1690        helpLinkUri: HelpLinkBase + "NDLRGEN059.md");
 691
 692    /// <summary>
 693    /// NDLRGEN060: A property guard has no participating record-overload parameter.
 694    /// </summary>
 1695    public static readonly DiagnosticDescriptor ConstructorGuardOnNonparticipatingProperty = new(
 1696        id: "NDLRGEN060",
 1697        title: "Constructor guard property does not participate in an overload",
 1698        messageFormat: "Property '{0}' has a constructor guard but is not marked with [RecordConstructorOverloadParamete
 1699        category: Category,
 1700        defaultSeverity: DiagnosticSeverity.Error,
 1701        isEnabledByDefault: true,
 1702        description: "Constructor guards on properties are valid only for properties marked with [RecordConstructorOverl
 1703        helpLinkUri: HelpLinkBase + "NDLRGEN060.md");
 704
 705    /// <summary>
 706    /// NDLRGEN061: Field-based constructor generation conflicts with the record-only feature.
 707    /// </summary>
 1708    public static readonly DiagnosticDescriptor RecordConstructorOverloadConflictsWithGeneratedConstructor = new(
 1709        id: "NDLRGEN061",
 1710        title: "Record constructor overload conflicts with field-based generation",
 1711        messageFormat: "Type '{0}' cannot combine [RecordConstructorOverloadParameter] with [GenerateConstructor] or a f
 1712        category: Category,
 1713        defaultSeverity: DiagnosticSeverity.Error,
 1714        isEnabledByDefault: true,
 1715        description: "The field-based generated-constructor feature owns an ordinary class construction path and partici
 1716        helpLinkUri: HelpLinkBase + "NDLRGEN061.md");
 717
 718    /// <summary>
 719    /// NDLRGEN062: The proposed constructor signature already exists.
 720    /// </summary>
 1721    public static readonly DiagnosticDescriptor RecordConstructorOverloadSignatureCollision = new(
 1722        id: "NDLRGEN062",
 1723        title: "Record constructor-overload signature collides",
 1724        messageFormat: "Record '{0}' cannot receive the generated constructor overload because its C# signature would co
 1725        category: Category,
 1726        defaultSeverity: DiagnosticSeverity.Error,
 1727        isEnabledByDefault: true,
 1728        description: "Constructor signature identity ignores parameter names, nullable annotations, optional values, and
 1729        helpLinkUri: HelpLinkBase + "NDLRGEN062.md");
 730
 731    // ============================================================================
 732    // Generated Constructor Suggestions (NDLRGEN063)
 733    // ============================================================================
 734
 735    /// <summary>
 736    /// NDLRGEN063: A hand-written constructor is mechanically replaceable by
 737    /// Needlr's generated-constructor feature.
 738    /// </summary>
 1739    public static readonly DiagnosticDescriptor GenerateConstructorSuggested = new(
 1740        id: "NDLRGEN063",
 1741        title: "Constructor can be generated by Needlr",
 1742        messageFormat: "Constructor for type '{0}' can be replaced by Needlr's generated-constructor feature without cha
 1743        category: Category,
 1744        defaultSeverity: DiagnosticSeverity.Info,
 1745        isEnabledByDefault: true,
 1746        description: "The type has one public constructor whose parameters, supported guard calls, and private readonly 
 1747        helpLinkUri: HelpLinkBase + "NDLRGEN063.md");
 748
 749    // ============================================================================
 750    // HttpClient Analyzers (NDLRHTTP001-006)
 751    // ============================================================================
 752
 753    /// <summary>
 754    /// NDLRHTTP001: [HttpClientOptions] target must implement INamedHttpClientOptions.
 755    /// </summary>
 1756    public static readonly DiagnosticDescriptor HttpClientMustImplementMarker = new(
 1757        id: "NDLRHTTP001",
 1758        title: "[HttpClientOptions] target must implement INamedHttpClientOptions",
 1759        messageFormat: "Type '{0}' has [HttpClientOptions] but does not implement INamedHttpClientOptions. Implement the
 1760        category: Category,
 1761        defaultSeverity: DiagnosticSeverity.Error,
 1762        isEnabledByDefault: true,
 1763        description: "The INamedHttpClientOptions marker is the contract that opts a type into HttpClient source generat
 1764        helpLinkUri: HelpLinkBase + "NDLRHTTP001.md");
 765
 766    /// <summary>
 767    /// NDLRHTTP002: Attribute Name argument conflicts with the ClientName property on the type.
 768    /// </summary>
 1769    public static readonly DiagnosticDescriptor HttpClientNameSourceConflict = new(
 1770        id: "NDLRHTTP002",
 1771        title: "HttpClient name sources conflict",
 1772        messageFormat: "Type '{0}' has [HttpClientOptions(Name = \"{1}\")] but its ClientName property resolves to \"{2}
 1773        category: Category,
 1774        defaultSeverity: DiagnosticSeverity.Error,
 1775        isEnabledByDefault: true,
 1776        description: "The HttpClient name can come from the attribute argument, a ClientName property on the type, or ty
 1777        helpLinkUri: HelpLinkBase + "NDLRHTTP002.md");
 778
 779    /// <summary>
 780    /// NDLRHTTP003: ClientName property body is not a literal expression and no attribute Name override is supplied.
 781    /// </summary>
 1782    public static readonly DiagnosticDescriptor HttpClientNamePropertyNotLiteral = new(
 1783        id: "NDLRHTTP003",
 1784        title: "ClientName property body is not a literal expression",
 1785        messageFormat: "Type '{0}' has a ClientName property whose body is not a string literal. The generator resolves 
 1786        category: Category,
 1787        defaultSeverity: DiagnosticSeverity.Error,
 1788        isEnabledByDefault: true,
 1789        description: "HttpClient names must be known at compile time because the generator emits services.AddHttpClient(
 1790        helpLinkUri: HelpLinkBase + "NDLRHTTP003.md");
 791
 792    /// <summary>
 793    /// NDLRHTTP004: Resolved HttpClient name is empty or whitespace.
 794    /// </summary>
 1795    public static readonly DiagnosticDescriptor HttpClientNameEmpty = new(
 1796        id: "NDLRHTTP004",
 1797        title: "Resolved HttpClient name is empty",
 1798        messageFormat: "Type '{0}' has [HttpClientOptions] but no name source resolves to a non-empty value",
 1799        category: Category,
 1800        defaultSeverity: DiagnosticSeverity.Error,
 1801        isEnabledByDefault: true,
 1802        description: "At least one of the three name sources must yield a non-empty string. This typically happens when 
 1803        helpLinkUri: HelpLinkBase + "NDLRHTTP004.md");
 804
 805    /// <summary>
 806    /// NDLRHTTP005: Two [HttpClientOptions] types in the compilation resolve to the same client name.
 807    /// </summary>
 1808    public static readonly DiagnosticDescriptor HttpClientNameCollision = new(
 1809        id: "NDLRHTTP005",
 1810        title: "Duplicate HttpClient name",
 1811        messageFormat: "Type '{0}' resolves to HttpClient name \"{1}\" which is already used by type '{2}'. HttpClient n
 1812        category: Category,
 1813        defaultSeverity: DiagnosticSeverity.Error,
 1814        isEnabledByDefault: true,
 1815        description: "services.AddHttpClient(\"name\", ...) registrations are keyed by name. Two options types resolving
 1816        helpLinkUri: HelpLinkBase + "NDLRHTTP005.md",
 1817        customTags: WellKnownDiagnosticTags.CompilationEnd);
 818
 819    /// <summary>
 820    /// NDLRHTTP006: ClientName property must be an instance property returning string.
 821    /// </summary>
 1822    public static readonly DiagnosticDescriptor HttpClientNamePropertyWrongShape = new(
 1823        id: "NDLRHTTP006",
 1824        title: "ClientName property has wrong shape",
 1825        messageFormat: "Type '{0}' has a ClientName property that is not a readable instance property of type 'string'. 
 1826        category: Category,
 1827        defaultSeverity: DiagnosticSeverity.Error,
 1828        isEnabledByDefault: true,
 1829        description: "The ClientName property is one of the three name sources recognized by the generator. To avoid amb
 1830        helpLinkUri: HelpLinkBase + "NDLRHTTP006.md");
 831}

Methods/Properties

.cctor()