| | | 1 | | using Microsoft.CodeAnalysis; |
| | | 2 | | |
| | | 3 | | namespace NexusLabs.Needlr.Analyzers; |
| | | 4 | | |
| | | 5 | | /// <summary> |
| | | 6 | | /// Contains diagnostic descriptors for all Needlr analyzers. |
| | | 7 | | /// </summary> |
| | | 8 | | public static class DiagnosticDescriptors |
| | | 9 | | { |
| | | 10 | | private const string Category = "NexusLabs.Needlr"; |
| | | 11 | | private const string HelpLinkBase = "https://github.com/nexus-labs/needlr/blob/main/docs/analyzers/"; |
| | | 12 | | |
| | | 13 | | /// <summary> |
| | | 14 | | /// NDLRCOR001: Reflection API used in AOT project. |
| | | 15 | | /// </summary> |
| | 1 | 16 | | public static readonly DiagnosticDescriptor ReflectionInAotProject = new( |
| | 1 | 17 | | id: DiagnosticIds.ReflectionInAotProject, |
| | 1 | 18 | | title: "Reflection API used in AOT project", |
| | 1 | 19 | | messageFormat: "'{0}' uses reflection and is not compatible with AOT/trimming. Use source-generated alternatives |
| | 1 | 20 | | category: Category, |
| | 1 | 21 | | defaultSeverity: DiagnosticSeverity.Error, |
| | 1 | 22 | | isEnabledByDefault: true, |
| | 1 | 23 | | description: "Reflection-based Needlr APIs cannot be used in projects with PublishAot or IsAotCompatible enabled |
| | 1 | 24 | | helpLinkUri: HelpLinkBase + "NDLRCOR001.md"); |
| | | 25 | | |
| | | 26 | | /// <summary> |
| | | 27 | | /// NDLRCOR002: Plugin has constructor dependencies. |
| | | 28 | | /// </summary> |
| | 1 | 29 | | public static readonly DiagnosticDescriptor PluginHasConstructorDependencies = new( |
| | 1 | 30 | | id: DiagnosticIds.PluginHasConstructorDependencies, |
| | 1 | 31 | | title: "Plugin has constructor dependencies", |
| | 1 | 32 | | messageFormat: "Plugin '{0}' has constructor parameters but is instantiated before DI is available. Use a parame |
| | 1 | 33 | | category: Category, |
| | 1 | 34 | | defaultSeverity: DiagnosticSeverity.Warning, |
| | 1 | 35 | | isEnabledByDefault: true, |
| | 1 | 36 | | description: "IServiceCollectionPlugin and IPostBuildServiceCollectionPlugin implementations are instantiated be |
| | 1 | 37 | | helpLinkUri: HelpLinkBase + "NDLRCOR002.md"); |
| | | 38 | | |
| | | 39 | | /// <summary> |
| | | 40 | | /// NDLRCOR003: DeferToContainer attribute in generated code. |
| | | 41 | | /// </summary> |
| | 1 | 42 | | public static readonly DiagnosticDescriptor DeferToContainerInGeneratedCode = new( |
| | 1 | 43 | | id: DiagnosticIds.DeferToContainerInGeneratedCode, |
| | 1 | 44 | | title: "[DeferToContainer] attribute in generated code is ignored", |
| | 1 | 45 | | messageFormat: "[DeferToContainer] on '{0}' is in generated code and will be ignored by Needlr. Move the attribu |
| | 1 | 46 | | category: Category, |
| | 1 | 47 | | defaultSeverity: DiagnosticSeverity.Error, |
| | 1 | 48 | | isEnabledByDefault: true, |
| | 1 | 49 | | description: "Source generators run in isolation and cannot see attributes added by other generators. The [Defer |
| | 1 | 50 | | helpLinkUri: HelpLinkBase + "NDLRCOR003.md"); |
| | | 51 | | |
| | | 52 | | /// <summary> |
| | | 53 | | /// NDLRCOR004: Injectable type in global namespace may not be discovered. |
| | | 54 | | /// </summary> |
| | 1 | 55 | | public static readonly DiagnosticDescriptor GlobalNamespaceTypeNotDiscovered = new( |
| | 1 | 56 | | id: DiagnosticIds.GlobalNamespaceTypeNotDiscovered, |
| | 1 | 57 | | title: "Injectable type in global namespace may not be discovered", |
| | 1 | 58 | | messageFormat: "Type '{0}' is in the global namespace and won't be discovered when IncludeNamespacePrefixes is s |
| | 1 | 59 | | category: Category, |
| | 1 | 60 | | defaultSeverity: DiagnosticSeverity.Warning, |
| | 1 | 61 | | isEnabledByDefault: true, |
| | 1 | 62 | | description: "Types in the global namespace are not matched by namespace prefix filters. Either move the type to |
| | 1 | 63 | | helpLinkUri: HelpLinkBase + "NDLRCOR004.md"); |
| | | 64 | | |
| | | 65 | | /// <summary> |
| | | 66 | | /// NDLRCOR005: Lifetime mismatch - longer-lived service depends on shorter-lived service. |
| | | 67 | | /// </summary> |
| | 1 | 68 | | public static readonly DiagnosticDescriptor LifetimeMismatch = new( |
| | 1 | 69 | | id: DiagnosticIds.LifetimeMismatch, |
| | 1 | 70 | | title: "Lifetime mismatch: longer-lived service depends on shorter-lived service", |
| | 1 | 71 | | messageFormat: "'{0}' is registered as {1} but depends on '{2}' which is registered as {3}. This causes a captiv |
| | 1 | 72 | | category: Category, |
| | 1 | 73 | | defaultSeverity: DiagnosticSeverity.Warning, |
| | 1 | 74 | | isEnabledByDefault: true, |
| | 1 | 75 | | description: "A longer-lived service (e.g., Singleton) depending on a shorter-lived service (e.g., Scoped or Tra |
| | 1 | 76 | | helpLinkUri: HelpLinkBase + "NDLRCOR005.md"); |
| | | 77 | | |
| | | 78 | | /// <summary> |
| | | 79 | | /// NDLRCOR006: Circular dependency detected in service registration. |
| | | 80 | | /// </summary> |
| | 1 | 81 | | public static readonly DiagnosticDescriptor CircularDependency = new( |
| | 1 | 82 | | id: DiagnosticIds.CircularDependency, |
| | 1 | 83 | | title: "Circular dependency detected", |
| | 1 | 84 | | messageFormat: "Circular dependency detected: {0}. This will cause a runtime exception when resolving the servic |
| | 1 | 85 | | category: Category, |
| | 1 | 86 | | defaultSeverity: DiagnosticSeverity.Error, |
| | 1 | 87 | | isEnabledByDefault: true, |
| | 1 | 88 | | description: "A circular dependency occurs when a service directly or indirectly depends on itself. This will ca |
| | 1 | 89 | | helpLinkUri: HelpLinkBase + "NDLRCOR006.md", |
| | 1 | 90 | | customTags: WellKnownDiagnosticTags.CompilationEnd); |
| | | 91 | | |
| | | 92 | | /// <summary> |
| | | 93 | | /// NDLRCOR007: Intercept attribute type must implement IMethodInterceptor. |
| | | 94 | | /// </summary> |
| | 1 | 95 | | public static readonly DiagnosticDescriptor InterceptTypeMustImplementInterface = new( |
| | 1 | 96 | | id: DiagnosticIds.InterceptTypeMustImplementInterface, |
| | 1 | 97 | | title: "Intercept type must implement IMethodInterceptor", |
| | 1 | 98 | | messageFormat: "Type '{0}' used in [Intercept] attribute does not implement IMethodInterceptor", |
| | 1 | 99 | | category: Category, |
| | 1 | 100 | | defaultSeverity: DiagnosticSeverity.Error, |
| | 1 | 101 | | isEnabledByDefault: true, |
| | 1 | 102 | | description: "The type specified in an [Intercept] or [Intercept<T>] attribute must implement IMethodInterceptor |
| | 1 | 103 | | helpLinkUri: HelpLinkBase + "NDLRCOR007.md"); |
| | | 104 | | |
| | | 105 | | /// <summary> |
| | | 106 | | /// NDLRCOR008: [Intercept] applied to class without interfaces. |
| | | 107 | | /// </summary> |
| | 1 | 108 | | public static readonly DiagnosticDescriptor InterceptOnClassWithoutInterfaces = new( |
| | 1 | 109 | | id: DiagnosticIds.InterceptOnClassWithoutInterfaces, |
| | 1 | 110 | | title: "[Intercept] applied to class without interfaces", |
| | 1 | 111 | | messageFormat: "Class '{0}' has [Intercept] attribute but does not implement any interfaces. Interceptors requir |
| | 1 | 112 | | category: Category, |
| | 1 | 113 | | defaultSeverity: DiagnosticSeverity.Warning, |
| | 1 | 114 | | isEnabledByDefault: true, |
| | 1 | 115 | | description: "Interceptors work by generating a proxy class that implements the service's interfaces. If the cla |
| | 1 | 116 | | helpLinkUri: HelpLinkBase + "NDLRCOR008.md"); |
| | | 117 | | |
| | | 118 | | /// <summary> |
| | | 119 | | /// NDLRCOR009: Lazy<T> references type not discovered by source generation. |
| | | 120 | | /// </summary> |
| | 1 | 121 | | public static readonly DiagnosticDescriptor LazyResolutionUnknown = new( |
| | 1 | 122 | | id: DiagnosticIds.LazyResolutionUnknown, |
| | 1 | 123 | | title: "Lazy<T> references undiscovered type", |
| | 1 | 124 | | messageFormat: "Type '{0}' in Lazy<{0}> was not discovered by source generation. If registered via reflection, t |
| | 1 | 125 | | category: Category, |
| | 1 | 126 | | defaultSeverity: DiagnosticSeverity.Info, |
| | 1 | 127 | | isEnabledByDefault: true, |
| | 1 | 128 | | description: "The type parameter in Lazy<T> was not found in the source-generated type registry. This may indica |
| | 1 | 129 | | helpLinkUri: HelpLinkBase + "NDLRCOR009.md", |
| | 1 | 130 | | customTags: WellKnownDiagnosticTags.CompilationEnd); |
| | | 131 | | |
| | | 132 | | /// <summary> |
| | | 133 | | /// NDLRCOR010: IEnumerable<T> has no implementations discovered. |
| | | 134 | | /// </summary> |
| | 1 | 135 | | public static readonly DiagnosticDescriptor CollectionResolutionEmpty = new( |
| | 1 | 136 | | id: DiagnosticIds.CollectionResolutionEmpty, |
| | 1 | 137 | | title: "IEnumerable<T> has no discovered implementations", |
| | 1 | 138 | | messageFormat: "No implementations of '{0}' were discovered by source generation. The collection will be empty u |
| | 1 | 139 | | category: Category, |
| | 1 | 140 | | defaultSeverity: DiagnosticSeverity.Info, |
| | 1 | 141 | | isEnabledByDefault: true, |
| | 1 | 142 | | description: "No types implementing the interface were found in the source-generated type registry. This may ind |
| | 1 | 143 | | helpLinkUri: HelpLinkBase + "NDLRCOR010.md", |
| | 1 | 144 | | customTags: WellKnownDiagnosticTags.CompilationEnd); |
| | | 145 | | |
| | | 146 | | /// <summary> |
| | | 147 | | /// NDLRCOR011: [FromKeyedServices] references a key with no known registration. |
| | | 148 | | /// </summary> |
| | 1 | 149 | | public static readonly DiagnosticDescriptor KeyedServiceUnknownKey = new( |
| | 1 | 150 | | id: DiagnosticIds.KeyedServiceUnknownKey, |
| | 1 | 151 | | title: "[FromKeyedServices] references unknown key", |
| | 1 | 152 | | messageFormat: "Keyed service '{0}' with key \"{1}\" has no known registration. If registered via plugin or refl |
| | 1 | 153 | | category: Category, |
| | 1 | 154 | | defaultSeverity: DiagnosticSeverity.Info, |
| | 1 | 155 | | isEnabledByDefault: true, |
| | 1 | 156 | | description: "The keyed service reference was not found in source-generated registrations. Keyed services are ty |
| | 1 | 157 | | helpLinkUri: HelpLinkBase + "NDLRCOR011.md", |
| | 1 | 158 | | customTags: WellKnownDiagnosticTags.CompilationEnd); |
| | | 159 | | |
| | | 160 | | /// <summary> |
| | | 161 | | /// NDLRCOR015: [RegisterAs<T>] type argument must be an interface implemented by the class. |
| | | 162 | | /// </summary> |
| | 1 | 163 | | public static readonly DiagnosticDescriptor RegisterAsTypeArgNotImplemented = new( |
| | 1 | 164 | | id: DiagnosticIds.RegisterAsTypeArgNotImplemented, |
| | 1 | 165 | | title: "[RegisterAs<T>] type argument not implemented", |
| | 1 | 166 | | messageFormat: "Type '{0}' has [RegisterAs<{1}>] but does not implement '{1}'. The type argument must be an inte |
| | 1 | 167 | | category: Category, |
| | 1 | 168 | | defaultSeverity: DiagnosticSeverity.Error, |
| | 1 | 169 | | isEnabledByDefault: true, |
| | 1 | 170 | | description: "When using [RegisterAs<T>], T must be an interface that the decorated class implements. The class |
| | 1 | 171 | | helpLinkUri: HelpLinkBase + "NDLRCOR015.md"); |
| | | 172 | | |
| | | 173 | | /// <summary> |
| | | 174 | | /// NDLRCOR012: Disposable captive dependency - longer-lived service holds IDisposable with shorter lifetime. |
| | | 175 | | /// </summary> |
| | 1 | 176 | | public static readonly DiagnosticDescriptor DisposableCaptiveDependency = new( |
| | 1 | 177 | | id: DiagnosticIds.DisposableCaptiveDependency, |
| | 1 | 178 | | title: "Disposable captive dependency", |
| | 1 | 179 | | messageFormat: "'{0}' ({1}) depends on '{2}' ({3}) which implements {4}. The disposable will be disposed when it |
| | 1 | 180 | | category: Category, |
| | 1 | 181 | | defaultSeverity: DiagnosticSeverity.Error, |
| | 1 | 182 | | isEnabledByDefault: true, |
| | 1 | 183 | | description: "A longer-lived service holds a reference to a shorter-lived IDisposable/IAsyncDisposable. When the |
| | 1 | 184 | | helpLinkUri: HelpLinkBase + "NDLRCOR012.md"); |
| | | 185 | | } |