diff --git a/src/DependencyModules.Runtime/Attributes/InterceptAttribute.cs b/src/DependencyModules.Runtime/Attributes/InterceptAttribute.cs
index 16cd910..1adba36 100644
--- a/src/DependencyModules.Runtime/Attributes/InterceptAttribute.cs
+++ b/src/DependencyModules.Runtime/Attributes/InterceptAttribute.cs
@@ -34,4 +34,16 @@ public class InterceptAttribute(params Type[] interceptors) : Attribute {
/// .
///
public int Order { get; set; }
+
+ ///
+ /// Restricts the interception to a single module, matching the Realm property on the service
+ /// registration attributes and on .
+ ///
+ ///
+ /// Without a realm the interception belongs to every module that is not realm-only, which is how
+ /// services and decorators already behave. An OnlyRealm module previously received every
+ /// interception in the compilation regardless — including ones naming no realm at all — so a
+ /// module built to be isolated wrapped services it had never heard of.
+ ///
+ public Type? Realm { get; set; }
}
diff --git a/src/DependencyModules.SourceGenerator.Impl/AnalyzerReleases.Unshipped.md b/src/DependencyModules.SourceGenerator.Impl/AnalyzerReleases.Unshipped.md
index f2b7fad..fa9d395 100644
--- a/src/DependencyModules.SourceGenerator.Impl/AnalyzerReleases.Unshipped.md
+++ b/src/DependencyModules.SourceGenerator.Impl/AnalyzerReleases.Unshipped.md
@@ -1,2 +1,9 @@
; Unshipped analyzer release
; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
+
+### New Rules
+
+Rule ID | Category | Severity | Notes
+--------|----------|----------|-------
+DM0017 | DependencyModules | Error | A dependency module is declared inside another type.
+DM0018 | DependencyModules | Info | A module with settable properties relies on generated equality.
diff --git a/src/DependencyModules.SourceGenerator.Impl/BaseSourceGenerator.cs b/src/DependencyModules.SourceGenerator.Impl/BaseSourceGenerator.cs
index de44627..a1ab779 100644
--- a/src/DependencyModules.SourceGenerator.Impl/BaseSourceGenerator.cs
+++ b/src/DependencyModules.SourceGenerator.Impl/BaseSourceGenerator.cs
@@ -230,10 +230,18 @@ private ModuleEntryPointModel GetClassEntryPointModel(GeneratorSyntaxContext con
if (!typeDeclarationSyntax.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword))) {
features |= ModuleEntryPointFeatures.NotPartial;
}
+
+ // A module nested inside another type cannot be completed where it was written: the
+ // generated half is emitted at namespace level, so it becomes a second, unrelated type and
+ // the nested declaration never implements IDependencyModule.
+ if (typeDeclarationSyntax.Parent is TypeDeclarationSyntax) {
+ features |= ModuleEntryPointFeatures.NestedInType;
+ }
return new ModuleEntryPointModel(
features,
context.Node.SyntaxTree?.FilePath ?? "",
+ LocationModel.From(context.Node),
((TypeDeclarationSyntax)context.Node).GetTypeDefinition(),
dependencyFlags.RegistrationType,
dependencyFlags.GenerateAttribute,
@@ -276,6 +284,7 @@ private ModuleEntryPointModel GetCompilationUnitSyntaxEntry(GeneratorSyntaxConte
return new ModuleEntryPointModel(
ModuleEntryPointFeatures.AutoGenerateModule,
context.Node.SyntaxTree?.FilePath ?? "",
+ LocationModel.From(context.Node),
TypeDefinition.Get("", "ApplicationModule"),
null,
true,
diff --git a/src/DependencyModules.SourceGenerator.Impl/DependencyModuleDiagnostics.cs b/src/DependencyModules.SourceGenerator.Impl/DependencyModuleDiagnostics.cs
index 5505421..cb5a394 100644
--- a/src/DependencyModules.SourceGenerator.Impl/DependencyModuleDiagnostics.cs
+++ b/src/DependencyModules.SourceGenerator.Impl/DependencyModuleDiagnostics.cs
@@ -56,6 +56,55 @@ public static class DependencyModuleDiagnostics {
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);
+ ///
+ /// Raised for a module declared inside another type.
+ ///
+ ///
+ /// The generated half is written at namespace level, so a nested declaration produces a second,
+ /// detached type of the same name while the nested one never implements
+ /// IDependencyModule. AddModule<Outer.Nested>() then fails to compile, but
+ /// [assembly: Nested] binds to the detached type's attribute and builds green, registering
+ /// nothing.
+ ///
+ public static readonly DiagnosticDescriptor ModuleCannotBeNested = new(
+ id: "DM0017",
+ title: "Dependency module cannot be nested inside another type",
+ messageFormat:
+ "'{0}' is marked with [DependencyModule] but is declared inside another type. " +
+ "The generator completes a module at namespace level, so this would produce a second, " +
+ "unrelated type and register nothing. Move it out to the namespace.",
+ category: Category,
+ defaultSeverity: DiagnosticSeverity.Error,
+ isEnabledByDefault: true);
+
+ ///
+ /// Raised for a module carrying settable properties while relying on the generated
+ /// Equals/GetHashCode.
+ ///
+ ///
+ /// Modules de-duplicate by type, which is what stops a module reached twice from registering
+ /// everything twice. A module with parameters is a different case: two instances carrying
+ /// different values are the same module by that rule, so the first one reached wins and the
+ /// other is silently discarded. Supplying an Equals that accounts for the properties
+ /// suppresses the generated one and makes the intent explicit either way.
+ ///
+ public static readonly DiagnosticDescriptor ModuleWithPropertiesShouldImplementEquals = new(
+ id: "DM0018",
+ title: "Module with properties relies on generated equality",
+ messageFormat:
+ "'{0}' has settable properties but does not declare Equals, so the generated equality " +
+ "compares by type alone. Two instances carrying different values count as the same module " +
+ "and the first one reached wins. Declare Equals and GetHashCode on '{0}' to say which " +
+ "instances are the same.",
+ category: Category,
+ // Info, not Warning. Dedupe-by-type is correct for the common case — a parameterised module
+ // composed once — and this repo's own integration tests carry eleven such modules that are
+ // not doing anything wrong. Only a module reached twice with different values is actually
+ // bitten, and that is not decidable here. Promote it per project with
+ // dotnet_diagnostic.DM0018.severity = warning if you want it enforced.
+ defaultSeverity: DiagnosticSeverity.Info,
+ isEnabledByDefault: true);
+
///
/// Raised when two conventions in one module register a type as the same service type.
///
@@ -89,10 +138,11 @@ public static class DependencyModuleDiagnostics {
public static readonly DiagnosticDescriptor ConventionMatchedNothing = new(
id: "DM0005",
title: "Convention matched no types",
- messageFormat:
- "The convention registering '{0}' in '{1}' matched no types. " +
- "Conventions match a type that declares the service type, or declares an interface " +
- "extending it; call IncludeBaseClasses() to also match types that reach it through a base class.",
+ // The advice is a parameter rather than a fixed tail, because the causes need different
+ // fixes. Suggesting IncludeBaseClasses() unconditionally told a reader who had already
+ // called it — or who had named a class, which can never match whatever they call — to go
+ // looking for a typo in their own code.
+ messageFormat: "The convention registering '{0}' in '{1}' matched no types. {2}.",
category: Category,
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);
diff --git a/src/DependencyModules.SourceGenerator.Impl/DependencyModuleWriter.cs b/src/DependencyModules.SourceGenerator.Impl/DependencyModuleWriter.cs
index 7e36124..4345f0c 100644
--- a/src/DependencyModules.SourceGenerator.Impl/DependencyModuleWriter.cs
+++ b/src/DependencyModules.SourceGenerator.Impl/DependencyModuleWriter.cs
@@ -75,12 +75,35 @@ private void ProcessEntryPoint(
context.ReportDiagnostic(
Diagnostic.Create(
DependencyModuleDiagnostics.ModuleMustBePartial,
- Location.None,
+ entryPointModel.Location.ToLocationOrNone(),
entryPointModel.EntryPointType.Name));
return;
}
+ // Generating anyway would emit a same-named type at namespace level, which compiles and
+ // registers nothing — the failure this is here to replace.
+ if (entryPointModel.ModuleFeatures.HasFlag(ModuleEntryPointFeatures.NestedInType)) {
+ context.ReportDiagnostic(
+ Diagnostic.Create(
+ DependencyModuleDiagnostics.ModuleCannotBeNested,
+ entryPointModel.Location.ToLocationOrNone(),
+ entryPointModel.EntryPointType.Name));
+
+ return;
+ }
+
+ // Only when the generator is supplying equality. A module that declares its own Equals has
+ // already answered the question this asks about.
+ if (entryPointModel.PropertyInfoModels.Count > 0 &&
+ entryPointModel.ModuleFeatures.HasFlag(ModuleEntryPointFeatures.ShouldImplementEquals)) {
+ context.ReportDiagnostic(
+ Diagnostic.Create(
+ DependencyModuleDiagnostics.ModuleWithPropertiesShouldImplementEquals,
+ entryPointModel.Location.ToLocationOrNone(),
+ entryPointModel.EntryPointType.Name));
+ }
+
entryPointModel = EntryModelUtil.EnsureNamespace(entryPointModel,configurationModel);
var csharpFile = new CSharpFileDefinition(entryPointModel.EntryPointType.Namespace);
diff --git a/src/DependencyModules.SourceGenerator.Impl/Models/DecoratorModel.cs b/src/DependencyModules.SourceGenerator.Impl/Models/DecoratorModel.cs
index 2acc6c6..496e90d 100644
--- a/src/DependencyModules.SourceGenerator.Impl/Models/DecoratorModel.cs
+++ b/src/DependencyModules.SourceGenerator.Impl/Models/DecoratorModel.cs
@@ -42,7 +42,14 @@ public record DecoratorModel(
IReadOnlyList? Conditions = null,
ConstructorInfoModel? Constructor = null,
int InnerParameterIndex = -1,
- bool TypeParametersMatchService = true) {
+ bool TypeParametersMatchService = true,
+
+ ///
+ /// Where the decorator was declared, so DM0007 and DM0013 can point at it rather than at the
+ /// project. Null for a decorator declared through [Decorate] on a module, which names two types
+ /// and has no declaration of its own to point at.
+ ///
+ LocationModel? Location = null) {
///
/// Whether the decorator can be constructed by generated code.
diff --git a/src/DependencyModules.SourceGenerator.Impl/Models/InterceptorModel.cs b/src/DependencyModules.SourceGenerator.Impl/Models/InterceptorModel.cs
index 08e8669..ee273a2 100644
--- a/src/DependencyModules.SourceGenerator.Impl/Models/InterceptorModel.cs
+++ b/src/DependencyModules.SourceGenerator.Impl/Models/InterceptorModel.cs
@@ -344,7 +344,14 @@ public record InterceptorModel(
IReadOnlyList Declarations,
int Order,
InterceptionRefusal? Refusal = null,
- IReadOnlyList? TypeParameters = null) {
+ IReadOnlyList? TypeParameters = null,
+ ITypeDefinition? Realm = null,
+
+ ///
+ /// Where the intercepted class was declared, so DM0008 and DM0015 can point at it rather than
+ /// at the project.
+ ///
+ LocationModel? Location = null) {
///
/// Whether the intercepted service is an open generic, and so registers as an implementation type
@@ -368,8 +375,8 @@ public record InterceptorModel(
/// A model that generates nothing and explains why, so an unsupported shape produces a
/// diagnostic rather than a wrapper that does not compile.
///
- public static InterceptorModel Refused(string message) =>
- Ignore with { Refusal = new InterceptionRefusal(message) };
+ public static InterceptorModel Refused(string message, LocationModel? location = null) =>
+ Ignore with { Refusal = new InterceptionRefusal(message), Location = location };
public bool IsIgnored => ReferenceEquals(this, Ignore);
}
diff --git a/src/DependencyModules.SourceGenerator/Conventions/Models/LocationModel.cs b/src/DependencyModules.SourceGenerator.Impl/Models/LocationModel.cs
similarity index 98%
rename from src/DependencyModules.SourceGenerator/Conventions/Models/LocationModel.cs
rename to src/DependencyModules.SourceGenerator.Impl/Models/LocationModel.cs
index 93c302f..1c3a68e 100644
--- a/src/DependencyModules.SourceGenerator/Conventions/Models/LocationModel.cs
+++ b/src/DependencyModules.SourceGenerator.Impl/Models/LocationModel.cs
@@ -2,7 +2,7 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
-namespace DependencyModules.Conventions.Models;
+namespace DependencyModules.SourceGenerator.Impl.Models;
///
/// Where a declaration sits in source, in a form that can live in an incremental model.
diff --git a/src/DependencyModules.SourceGenerator.Impl/Models/ModuleEntryPointModel.cs b/src/DependencyModules.SourceGenerator.Impl/Models/ModuleEntryPointModel.cs
index 7e76bc6..01afeb2 100644
--- a/src/DependencyModules.SourceGenerator.Impl/Models/ModuleEntryPointModel.cs
+++ b/src/DependencyModules.SourceGenerator.Impl/Models/ModuleEntryPointModel.cs
@@ -14,11 +14,22 @@ public enum ModuleEntryPointFeatures {
/// The module was declared without the partial modifier, so the generator cannot complete it.
///
NotPartial = 16,
+
+ ///
+ /// The module was declared inside another type rather than directly in a namespace.
+ ///
+ ///
+ /// Documented as always wrong in the README, the modules guide and the troubleshooting guide,
+ /// and until now reported by none of them: the generator emitted a second, detached type at
+ /// namespace level, the nested declaration never became a module, and the build stayed green.
+ ///
+ NestedInType = 32,
}
public record ModuleEntryPointModel(
ModuleEntryPointFeatures ModuleFeatures,
string FileLocation,
+ LocationModel Location,
ITypeDefinition EntryPointType,
RegistrationType? RegistrationType,
bool? GenerateAttribute,
@@ -39,6 +50,12 @@ public bool Equals(ModuleEntryPointModel? x, ModuleEntryPointModel? y) {
if (x is null && y is null) return true;
if (x is null || y is null) return false;
+ // Location is deliberately absent. It is carried so diagnostics can point at the
+ // declaration, but it shifts whenever anything above the module is edited — including a
+ // comment — and this comparer is the incremental cache key. Including it regenerated every
+ // module on a keystroke that changed nothing (IncrementalGenerationTests covers exactly
+ // that). The cost is that a diagnostic replayed from cache can sit a line or two off until
+ // the next semantic edit, which is the cheaper of the two mistakes.
return x.FileLocation == y.FileLocation &&
x.EntryPointType.Equals(y.EntryPointType) &&
x.ModuleFeatures == y.ModuleFeatures &&
diff --git a/src/DependencyModules.SourceGenerator.Impl/Models/ServiceModel.cs b/src/DependencyModules.SourceGenerator.Impl/Models/ServiceModel.cs
index 874fe42..aa2f5ed 100644
--- a/src/DependencyModules.SourceGenerator.Impl/Models/ServiceModel.cs
+++ b/src/DependencyModules.SourceGenerator.Impl/Models/ServiceModel.cs
@@ -79,7 +79,15 @@ public record ServiceModel(
/// attributes are declared on the class, so every registration it produces shares them and the
/// writer emits one guard around the lot.
///
- IReadOnlyList? Conditions = null) {
+ IReadOnlyList? Conditions = null,
+
+ ///
+ /// Where the implementation was declared, so a diagnostic about it can point at the class
+ /// rather than at the project. Deliberately absent from
+ /// — it is not part of what makes two models the same
+ /// registration, and including it would miss the incremental cache on an edit above the class.
+ ///
+ LocationModel? Location = null) {
public static ServiceModel Ignore = new ServiceModel(
TypeDefinition.Get("", "Ignore"),
null,
diff --git a/src/DependencyModules.SourceGenerator.Impl/Utilities/DecoratorModelUtility.cs b/src/DependencyModules.SourceGenerator.Impl/Utilities/DecoratorModelUtility.cs
index 405a28b..21a3d14 100644
--- a/src/DependencyModules.SourceGenerator.Impl/Utilities/DecoratorModelUtility.cs
+++ b/src/DependencyModules.SourceGenerator.Impl/Utilities/DecoratorModelUtility.cs
@@ -83,7 +83,8 @@ public static class DecoratorModelUtility {
conditions,
constructor,
IndexOfInnerParameter(constructor, written),
- TypeParametersMatchService(typeDeclarationSyntax, written));
+ TypeParametersMatchService(typeDeclarationSyntax, written),
+ LocationModel.From(typeDeclarationSyntax));
}
///
diff --git a/src/DependencyModules.SourceGenerator.Impl/Utilities/InterceptorModelUtility.cs b/src/DependencyModules.SourceGenerator.Impl/Utilities/InterceptorModelUtility.cs
index 65b3f23..6f7ddf3 100644
--- a/src/DependencyModules.SourceGenerator.Impl/Utilities/InterceptorModelUtility.cs
+++ b/src/DependencyModules.SourceGenerator.Impl/Utilities/InterceptorModelUtility.cs
@@ -55,12 +55,14 @@ public static InterceptorModel GetInterceptorModel(
var interceptorSymbols = new List();
var order = 0;
INamedTypeSymbol? explicitService = null;
+ INamedTypeSymbol? realm = null;
// Every [Intercept], not the first. The attribute is AllowMultiple, so stacking them is a
// supported way to write what one attribute can also express as a params list — and reading
// only the first dropped every interceptor after it, with nothing to say so.
foreach (var attribute in attributes) {
- ReadAttribute(attribute, context, cancellationToken, interceptorSymbols, ref order, ref explicitService);
+ ReadAttribute(
+ attribute, context, cancellationToken, interceptorSymbols, ref order, ref explicitService, ref realm);
}
if (interceptorSymbols.Count == 0) {
@@ -70,11 +72,11 @@ public static InterceptorModel GetInterceptorModel(
var serviceSymbol = ResolveServiceInterface(implementationSymbol, explicitService, out var unsupported);
if (serviceSymbol == null) {
- return Refuse(unsupported);
+ return Refuse(unsupported, context);
}
if (!InterceptedMemberReader.Read(serviceSymbol, out var members, out var declarations, out unsupported)) {
- return Refuse(unsupported);
+ return Refuse(unsupported, context);
}
if (members.Count == 0) {
@@ -101,13 +103,15 @@ public static InterceptorModel GetInterceptorModel(
members,
declarations,
order,
- TypeParameters: TypeParameterModels(implementationSymbol));
+ TypeParameters: TypeParameterModels(implementationSymbol),
+ Realm: realm?.GetTypeDefinition(),
+ Location: LocationModel.From(context.Node));
}
- private static InterceptorModel Refuse(string? reason) =>
+ private static InterceptorModel Refuse(string? reason, SyntaxTransformContext context) =>
reason == null
? InterceptorModel.Ignore
- : InterceptorModel.Refused(reason);
+ : InterceptorModel.Refused(reason, LocationModel.From(context.Node));
///
/// The interfaces an interceptor implements, which decide the members it can be placed around.
@@ -167,7 +171,8 @@ private static void ReadAttribute(
CancellationToken cancellationToken,
List interceptors,
ref int order,
- ref INamedTypeSymbol? explicitService) {
+ ref INamedTypeSymbol? explicitService,
+ ref INamedTypeSymbol? realm) {
if (attribute.ArgumentList == null) {
return;
@@ -199,6 +204,11 @@ private static void ReadAttribute(
explicitService = ResolveType(serviceTypeOf, context, cancellationToken);
}
break;
+ case "Realm":
+ if (argument.Expression is TypeOfExpressionSyntax realmTypeOf) {
+ realm = ResolveType(realmTypeOf, context, cancellationToken);
+ }
+ break;
}
}
}
diff --git a/src/DependencyModules.SourceGenerator.Impl/Utilities/ServiceModelUtility.cs b/src/DependencyModules.SourceGenerator.Impl/Utilities/ServiceModelUtility.cs
index 019b700..60cc613 100644
--- a/src/DependencyModules.SourceGenerator.Impl/Utilities/ServiceModelUtility.cs
+++ b/src/DependencyModules.SourceGenerator.Impl/Utilities/ServiceModelUtility.cs
@@ -70,6 +70,21 @@ public class ServiceModelUtility {
KnownTypes.DependencyModules.Attributes.TransientServiceAttribute, KnownTypes.DependencyModules.Attributes.ScopedServiceAttribute, KnownTypes.DependencyModules.Attributes.SingletonServiceAttribute,
};
+ ///
+ /// The lifetime a service attribute type carries.
+ ///
+ private static ServiceLifestyle LifestyleOf(ITypeDefinition attributeType) {
+ if (attributeType.Name == KnownTypes.DependencyModules.Attributes.SingletonServiceAttribute.Name) {
+ return ServiceLifestyle.Singleton;
+ }
+
+ if (attributeType.Name == KnownTypes.DependencyModules.Attributes.ScopedServiceAttribute.Name) {
+ return ServiceLifestyle.Scoped;
+ }
+
+ return ServiceLifestyle.Transient;
+ }
+
public static ServiceModel? GetServiceModel(
SyntaxTransformContext context, CancellationToken cancellationToken) {
cancellationToken.ThrowIfCancellationRequested();
@@ -111,7 +126,8 @@ public class ServiceModelUtility {
null,
factoryModel, null,
GetRegistrations(context, returnType, models, cancellationToken),
- RegistrationFeature.None);
+ RegistrationFeature.None,
+ Location: LocationModel.From(context.Node));
}
private static ServiceFactoryModel? GetFactoryModel(SyntaxTransformContext context, MethodDeclarationSyntax methodDeclarationSyntax, CancellationToken cancellationToken) {
@@ -173,7 +189,8 @@ private static RegistrationFeature GetConstructionFeatures(SyntaxNode node) {
ServiceLifestyle.Transient
)
},
- RegistrationFeature.AutoRegisterSourceGenerator
+ RegistrationFeature.AutoRegisterSourceGenerator,
+ Location: LocationModel.From(context.Node)
);
}
@@ -190,7 +207,8 @@ private static RegistrationFeature GetConstructionFeatures(SyntaxNode node) {
factoryOutput,
registrations,
GetConstructionFeatures(context.Node),
- EnvironmentConditionUtility.GetConditions(context, context.Node, cancellationToken));
+ EnvironmentConditionUtility.GetConditions(context, context.Node, cancellationToken),
+ LocationModel.From(context.Node));
}
///
@@ -328,7 +346,7 @@ private static List GetRegistrations(SyntaxTransformCo
// skipped — leaving the class unregistered with nothing to say so.
if (AttributeTypeMatcher.Matches(
context.SemanticModel, attributeSyntax, typeDefinition, cancellationToken)) {
- list.Add(GetServiceRegistration(context, attributeSyntax, classDefinition));
+ list.Add(GetServiceRegistration(context, attributeSyntax, classDefinition, typeDefinition));
}
}
@@ -418,15 +436,22 @@ private static ServiceLifestyle GetLifestyle(string toString) {
return ServiceLifestyle.Singleton;
}
- private static ServiceRegistrationModel GetServiceRegistration(SyntaxTransformContext context, AttributeSyntax attributeSyntax, ITypeDefinition classDefinition) {
- var lifestyle = ServiceLifestyle.Transient;
-
- if (attributeSyntax.Name.ToString().StartsWith("Singleton")) {
- lifestyle = ServiceLifestyle.Singleton;
- }
- else if (attributeSyntax.Name.ToString().StartsWith("Scoped")) {
- lifestyle = ServiceLifestyle.Scoped;
- }
+ private static ServiceRegistrationModel GetServiceRegistration(
+ SyntaxTransformContext context,
+ AttributeSyntax attributeSyntax,
+ ITypeDefinition classDefinition,
+ ITypeDefinition attributeType) {
+
+ // The lifetime comes from the attribute type the usage resolved to, not from how the usage
+ // was spelled. Reading it back off `attributeSyntax.Name` meant only a spelling that literally
+ // began with "Singleton" or "Scoped" produced that lifetime: a qualified name, a global::
+ // prefix, and any `using` alias not named after its target all fell through to Transient —
+ // registered, with the wrong lifetime, and nothing said so.
+ //
+ // This costs nothing extra. AttributeTypeMatcher has already resolved the usage through the
+ // semantic model to decide that this attribute matched at all; the caller simply passes on
+ // which one it was rather than discarding the answer.
+ var lifestyle = LifestyleOf(attributeType);
ITypeDefinition? registration = null;
RegistrationType? registrationType = null;
diff --git a/src/DependencyModules.SourceGenerator/Conventions/ConventionGenerator.cs b/src/DependencyModules.SourceGenerator/Conventions/ConventionGenerator.cs
index 89177dc..845ba5f 100644
--- a/src/DependencyModules.SourceGenerator/Conventions/ConventionGenerator.cs
+++ b/src/DependencyModules.SourceGenerator/Conventions/ConventionGenerator.cs
@@ -456,7 +456,7 @@ private static void ReportOpenGenericDecoration(
context.ReportDiagnostic(
Diagnostic.Create(
DependencyModuleDiagnostics.OpenGenericCannotBeDecorated,
- Location.None,
+ decorator.Location?.ToLocationOrNone() ?? Location.None,
serviceName,
decoratorName));
}
@@ -483,7 +483,7 @@ private static void ReportAmbiguousOrdering(
context.ReportDiagnostic(
Diagnostic.Create(
DependencyModuleDiagnostics.AmbiguousDecoratorOrder,
- Location.None,
+ decorators[i].Location?.ToLocationOrNone() ?? Location.None,
decorators[i].DecoratorType.Name,
decorators[j].DecoratorType.Name,
decorators[i].ServiceType.Name,
@@ -516,7 +516,7 @@ private static void ReportUnclaimedModules(
context.ReportDiagnostic(Diagnostic.Create(
DependencyModuleDiagnostics.ConventionCannotBeRead,
- Location.None,
+ conventionModule.Location?.ToLocationOrNone() ?? Location.None,
"the declaring type is not marked with [DependencyModule], so it registers nothing",
name));
}
diff --git a/src/DependencyModules.SourceGenerator/Conventions/Models/ConventionModel.cs b/src/DependencyModules.SourceGenerator/Conventions/Models/ConventionModel.cs
index 0a0a0ef..df23c2c 100644
--- a/src/DependencyModules.SourceGenerator/Conventions/Models/ConventionModel.cs
+++ b/src/DependencyModules.SourceGenerator/Conventions/Models/ConventionModel.cs
@@ -291,7 +291,12 @@ public record UnreadableStatementModel(string Text, string Reason, LocationModel
public record ConventionModuleModel(
ITypeDefinition ModuleType,
IReadOnlyList Conventions,
- IReadOnlyList Unreadable) {
+ IReadOnlyList Unreadable,
+
+ ///
+ /// Where the declaring type sits, so a diagnostic about the module itself can point at it.
+ ///
+ LocationModel? Location = null) {
///
/// The sentinel for a declaration this generator does not own, matching how every other model
diff --git a/src/DependencyModules.SourceGenerator/Conventions/Utilities/ConventionCandidateCache.cs b/src/DependencyModules.SourceGenerator/Conventions/Utilities/ConventionCandidateCache.cs
index 4daf43b..55b243a 100644
--- a/src/DependencyModules.SourceGenerator/Conventions/Utilities/ConventionCandidateCache.cs
+++ b/src/DependencyModules.SourceGenerator/Conventions/Utilities/ConventionCandidateCache.cs
@@ -2,6 +2,7 @@
using DependencyModules.Conventions.Models;
using DependencyModules.SourceGenerator.Impl.Utilities;
using Microsoft.CodeAnalysis;
+using DependencyModules.SourceGenerator.Impl.Models;
namespace DependencyModules.Conventions.Utilities;
diff --git a/src/DependencyModules.SourceGenerator/Conventions/Utilities/ConventionMatcher.cs b/src/DependencyModules.SourceGenerator/Conventions/Utilities/ConventionMatcher.cs
index 3c3c635..62bbc55 100644
--- a/src/DependencyModules.SourceGenerator/Conventions/Utilities/ConventionMatcher.cs
+++ b/src/DependencyModules.SourceGenerator/Conventions/Utilities/ConventionMatcher.cs
@@ -190,10 +190,40 @@ or ConventionRegisterAs.AlsoSelf
DependencyModuleDiagnostics.ConventionMatchedNothing,
convention.Location.ToLocationOrNone(),
serviceName,
- moduleName));
+ moduleName,
+ AdviceForEmptyMatch(convention, serviceName)));
}
}
+ ///
+ /// What to tell someone whose convention matched nothing, chosen by why it could not match.
+ ///
+ ///
+ /// A class named as the service type is the sharpest case: conventions match a type through the
+ /// interfaces it declares, so no call and no filter change can ever make one match. That is also
+ /// the shape every MVVM project starts from — class FooViewModel : ViewModelBase — so
+ /// saying "call IncludeBaseClasses()" there sent readers looking for a mistake they had not made.
+ ///
+ private static string AdviceForEmptyMatch(ConventionModel convention, string serviceName) {
+ if (convention.ServiceType is { TypeDefinitionEnum: TypeDefinitionEnum.ClassDefinition }) {
+ return
+ $"Conventions match the interfaces a type declares, and '{serviceName}' is a class, " +
+ "so no type can match it. Register an interface the types declare — a marker " +
+ "interface on the base class is enough — or register them by attribute instead";
+ }
+
+ if (convention.IncludeBaseClasses) {
+ return
+ "Conventions match a type that declares the service type, or declares an interface " +
+ "extending it. IncludeBaseClasses() is already applied, so check the name, namespace " +
+ "and assembly filters on this convention";
+ }
+
+ return
+ "Conventions match a type that declares the service type, or declares an interface " +
+ "extending it; call IncludeBaseClasses() to also match types that reach it through a base class";
+ }
+
///
/// Translates each name glob to an anchored regular expression, once per convention.
///
diff --git a/src/DependencyModules.SourceGenerator/Conventions/Utilities/ConventionModelUtility.cs b/src/DependencyModules.SourceGenerator/Conventions/Utilities/ConventionModelUtility.cs
index a8f1a79..7a32e00 100644
--- a/src/DependencyModules.SourceGenerator/Conventions/Utilities/ConventionModelUtility.cs
+++ b/src/DependencyModules.SourceGenerator/Conventions/Utilities/ConventionModelUtility.cs
@@ -146,7 +146,8 @@ public static ConventionModuleModel GetConventionModuleModel(
ReadStatement(context, statement, parameterName!, conventions, unreadable);
}
- return new ConventionModuleModel(typeDeclaration.GetTypeDefinition(), conventions, unreadable);
+ return new ConventionModuleModel(
+ typeDeclaration.GetTypeDefinition(), conventions, unreadable, LocationModel.From(typeDeclaration));
}
private static void ReadStatement(
diff --git a/src/DependencyModules.SourceGenerator/InterceptorSourceGenerator.cs b/src/DependencyModules.SourceGenerator/InterceptorSourceGenerator.cs
index d894a98..a989df6 100644
--- a/src/DependencyModules.SourceGenerator/InterceptorSourceGenerator.cs
+++ b/src/DependencyModules.SourceGenerator/InterceptorSourceGenerator.cs
@@ -73,7 +73,11 @@ protected override void GenerateSourceOutput(
writer.Write(model, wrapperName, model.ImplementationType.Namespace));
}
- // One registration file per module, so every wrapper is applied wherever its service is.
+ // One registration file per module, carrying the interceptions that belong to that module.
+ // This used to hand every module the whole compilation's worth: an OnlyRealm module emitted
+ // applicators for interceptions that named no realm and had nothing to do with it, which
+ // either wrapped an unrelated service or — when the leaked interceptor needed a dependency
+ // the isolated container did not have — threw while building the provider.
foreach (var entryPointModel in EntryModelUtil.RegistrationTargets(entryPointList)) {
var registrationWriter = new InterceptorRegistrationWriter();
@@ -83,10 +87,41 @@ protected override void GenerateSourceOutput(
registrationWriter.Write(
EntryModelUtil.EnsureNamespace(entryPointModel, configurationModel),
configurationModel,
- usable));
+ ForModule(usable, entryPointModel)));
}
}
+ ///
+ /// The interceptions one module is responsible for applying.
+ ///
+ ///
+ /// The same rule services and decorators already follow: a realm-scoped interception belongs
+ /// only to the module it names, and an unscoped one belongs to every module that is not
+ /// realm-only.
+ ///
+ private static IReadOnlyList ForModule(
+ IReadOnlyList models, ModuleEntryPointModel entryPointModel) {
+
+ var onlyRealm = entryPointModel.ModuleFeatures.HasFlag(ModuleEntryPointFeatures.OnlyRealm);
+ var selected = new List();
+
+ foreach (var model in models) {
+ if (model.Realm != null) {
+ if (model.Realm.Equals(entryPointModel.EntryPointType)) {
+ selected.Add(model);
+ }
+
+ continue;
+ }
+
+ if (!onlyRealm) {
+ selected.Add(model);
+ }
+ }
+
+ return selected;
+ }
+
///
/// Reports declarations that cannot be intercepted and drops them, so an unsupported shape
/// produces an explanation rather than a wrapper that does not compile.
@@ -103,7 +138,7 @@ private static IReadOnlyList ReportUnsupported(
context.ReportDiagnostic(
Diagnostic.Create(
DependencyModuleDiagnostics.CannotIntercept,
- Location.None,
+ model.Location?.ToLocationOrNone() ?? Location.None,
model.Refusal.Message));
continue;
@@ -172,7 +207,7 @@ private static void ReportUnservedMembers(
context.ReportDiagnostic(
Diagnostic.Create(
DependencyModuleDiagnostics.InterceptorCannotServeMembers,
- Location.None,
+ model.Location?.ToLocationOrNone() ?? Location.None,
interceptor.Type.Name,
InterfaceFor(kind),
DescriptionFor(kind),
diff --git a/src/DependencyModules.SourceGenerator/ServiceSourceGenerator.cs b/src/DependencyModules.SourceGenerator/ServiceSourceGenerator.cs
index b356e56..6acc624 100644
--- a/src/DependencyModules.SourceGenerator/ServiceSourceGenerator.cs
+++ b/src/DependencyModules.SourceGenerator/ServiceSourceGenerator.cs
@@ -154,7 +154,7 @@ private static ImmutableArray ReportUnconstructableServices(
context.ReportDiagnostic(
Diagnostic.Create(
DependencyModuleDiagnostics.ServiceCannotBeConstructed,
- Location.None,
+ serviceModel.Location?.ToLocationOrNone() ?? Location.None,
typeName,
reason));
}
@@ -200,7 +200,7 @@ private static ImmutableArray ReportCrossWiredGenerics(
context.ReportDiagnostic(
Diagnostic.Create(
DependencyModuleDiagnostics.CrossWireCannotBeGeneric,
- Location.None,
+ serviceModel.Location?.ToLocationOrNone() ?? Location.None,
typeName));
}
@@ -246,7 +246,7 @@ private static void ReportEnvironmentConditions(
context.ReportDiagnostic(
Diagnostic.Create(
DependencyModuleDiagnostics.EmptyEnvironmentCondition,
- Location.None,
+ serviceModel.Location?.ToLocationOrNone() ?? Location.None,
typeName,
kind));
}
@@ -267,7 +267,7 @@ private static void ReportEnvironmentConditions(
context.ReportDiagnostic(
Diagnostic.Create(
DependencyModuleDiagnostics.RegisteredConditionally,
- Location.None,
+ serviceModel.Location?.ToLocationOrNone() ?? Location.None,
summary));
}
}
diff --git a/tests/DependencyModules.Tests/Infrastructure/ModelFactory.cs b/tests/DependencyModules.Tests/Infrastructure/ModelFactory.cs
index 6e0b922..b4c5721 100644
--- a/tests/DependencyModules.Tests/Infrastructure/ModelFactory.cs
+++ b/tests/DependencyModules.Tests/Infrastructure/ModelFactory.cs
@@ -26,6 +26,7 @@ public static ModuleEntryPointModel EntryPoint(
new(
features,
fileLocation,
+ LocationModel.None,
entryPointType ?? TypeDefinition.Get("TestNamespace", "TestModule"),
registrationType,
generateAttribute,
diff --git a/tests/DependencyModules.Tests/Snapshots/PublicApiTests.RuntimeApi.verified.txt b/tests/DependencyModules.Tests/Snapshots/PublicApiTests.RuntimeApi.verified.txt
index 6f3b991..33fc788 100644
--- a/tests/DependencyModules.Tests/Snapshots/PublicApiTests.RuntimeApi.verified.txt
+++ b/tests/DependencyModules.Tests/Snapshots/PublicApiTests.RuntimeApi.verified.txt
@@ -86,6 +86,7 @@ namespace DependencyModules.Runtime.Attributes
public InterceptAttribute(params System.Type[] interceptors) { }
public System.Type[] Interceptors { get; }
public int Order { get; set; }
+ public System.Type? Realm { get; set; }
public System.Type? Service { get; set; }
}
public enum RegistrationType
diff --git a/tests/DependencyModules.Tests/Snapshots/PublicApiTests.SourceGeneratorApi.verified.txt b/tests/DependencyModules.Tests/Snapshots/PublicApiTests.SourceGeneratorApi.verified.txt
index ccfaa07..bdf9ec7 100644
--- a/tests/DependencyModules.Tests/Snapshots/PublicApiTests.SourceGeneratorApi.verified.txt
+++ b/tests/DependencyModules.Tests/Snapshots/PublicApiTests.SourceGeneratorApi.verified.txt
@@ -838,7 +838,7 @@ namespace DependencyModules.Conventions.Models
public class ConventionCandidateModel : System.IEquatable
{
public static readonly DependencyModules.Conventions.Models.ConventionCandidateModel Ignore;
- public ConventionCandidateModel(CSharpAuthor.ITypeDefinition ImplementationType, System.Collections.Generic.IReadOnlyList DeclaredInterfaces, System.Collections.Generic.IReadOnlyList BaseClassInterfaces, DependencyModules.SourceGenerator.Impl.Models.ConstructorInfoModel? Constructor, bool HasAccessibleConstructor, DependencyModules.Conventions.Models.LocationModel Location, System.Collections.Generic.IReadOnlyList? Conditions = null, System.Collections.Generic.IReadOnlyList? AttributeTypeKeys = null, string? AssemblyName = null) { }
+ public ConventionCandidateModel(CSharpAuthor.ITypeDefinition ImplementationType, System.Collections.Generic.IReadOnlyList DeclaredInterfaces, System.Collections.Generic.IReadOnlyList BaseClassInterfaces, DependencyModules.SourceGenerator.Impl.Models.ConstructorInfoModel? Constructor, bool HasAccessibleConstructor, DependencyModules.SourceGenerator.Impl.Models.LocationModel Location, System.Collections.Generic.IReadOnlyList? Conditions = null, System.Collections.Generic.IReadOnlyList? AttributeTypeKeys = null, string? AssemblyName = null) { }
public string? AssemblyName { get; init; }
public System.Collections.Generic.IReadOnlyList? AttributeTypeKeys { get; init; }
public System.Collections.Generic.IReadOnlyList BaseClassInterfaces { get; init; }
@@ -848,7 +848,7 @@ namespace DependencyModules.Conventions.Models
public bool HasAccessibleConstructor { get; init; }
public CSharpAuthor.ITypeDefinition ImplementationType { get; init; }
public bool IsIgnored { get; }
- public DependencyModules.Conventions.Models.LocationModel Location { get; init; }
+ public DependencyModules.SourceGenerator.Impl.Models.LocationModel Location { get; init; }
public virtual bool Equals(DependencyModules.Conventions.Models.ConventionCandidateModel? other) { }
public override int GetHashCode() { }
public System.Collections.Generic.IEnumerable InterfacesInReach(bool includeBaseClasses) { }
@@ -861,7 +861,7 @@ namespace DependencyModules.Conventions.Models
bool IsOpenGeneric,
DependencyModules.SourceGenerator.Impl.Models.ServiceLifestyle? Lifestyle,
bool IncludeBaseClasses,
- DependencyModules.Conventions.Models.LocationModel Location,
+ DependencyModules.SourceGenerator.Impl.Models.LocationModel Location,
DependencyModules.Conventions.Models.ConventionRegisterAs RegisterAs = 0,
System.Collections.Generic.IReadOnlyList? NamespaceFilters = null,
DependencyModules.SourceGenerator.Impl.Models.RegistrationType? RegistrationType = default,
@@ -883,7 +883,7 @@ namespace DependencyModules.Conventions.Models
public object? Key { get; init; }
public System.Collections.Generic.IReadOnlyList? KeyNamespaces { get; init; }
public DependencyModules.SourceGenerator.Impl.Models.ServiceLifestyle? Lifestyle { get; init; }
- public DependencyModules.Conventions.Models.LocationModel Location { get; init; }
+ public DependencyModules.SourceGenerator.Impl.Models.LocationModel Location { get; init; }
public System.Collections.Generic.IReadOnlyList? NameFilters { get; init; }
public System.Collections.Generic.IReadOnlyList? NamespaceFilters { get; init; }
public DependencyModules.Conventions.Models.ConventionRegisterAs RegisterAs { get; init; }
@@ -897,9 +897,10 @@ namespace DependencyModules.Conventions.Models
public class ConventionModuleModel : System.IEquatable
{
public static readonly DependencyModules.Conventions.Models.ConventionModuleModel Ignore;
- public ConventionModuleModel(CSharpAuthor.ITypeDefinition ModuleType, System.Collections.Generic.IReadOnlyList Conventions, System.Collections.Generic.IReadOnlyList Unreadable) { }
+ public ConventionModuleModel(CSharpAuthor.ITypeDefinition ModuleType, System.Collections.Generic.IReadOnlyList Conventions, System.Collections.Generic.IReadOnlyList Unreadable, DependencyModules.SourceGenerator.Impl.Models.LocationModel? Location = null) { }
public System.Collections.Generic.IReadOnlyList Conventions { get; init; }
public bool IsIgnored { get; }
+ public DependencyModules.SourceGenerator.Impl.Models.LocationModel? Location { get; init; }
public CSharpAuthor.ITypeDefinition ModuleType { get; init; }
public System.Collections.Generic.IReadOnlyList Unreadable { get; init; }
public virtual bool Equals(DependencyModules.Conventions.Models.ConventionModuleModel? other) { }
@@ -934,21 +935,6 @@ namespace DependencyModules.Conventions.Models
public CSharpAuthor.ITypeDefinition InterfaceType { get; init; }
public string? ViaTypeName { get; init; }
}
- public class LocationModel : System.IEquatable
- {
- public static readonly DependencyModules.Conventions.Models.LocationModel None;
- public LocationModel(string FilePath, int SpanStart, int SpanLength, int StartLine, int StartCharacter, int EndLine, int EndCharacter) { }
- public int EndCharacter { get; init; }
- public int EndLine { get; init; }
- public string FilePath { get; init; }
- public int SpanLength { get; init; }
- public int SpanStart { get; init; }
- public int StartCharacter { get; init; }
- public int StartLine { get; init; }
- public Microsoft.CodeAnalysis.Location ToLocation() { }
- public Microsoft.CodeAnalysis.Location ToLocationOrNone() { }
- public static DependencyModules.Conventions.Models.LocationModel From(Microsoft.CodeAnalysis.SyntaxNode node) { }
- }
public class NameFilterModel : System.IEquatable
{
public NameFilterModel(string Pattern, bool Exclude) { }
@@ -966,8 +952,8 @@ namespace DependencyModules.Conventions.Models
}
public class UnreadableStatementModel : System.IEquatable
{
- public UnreadableStatementModel(string Text, string Reason, DependencyModules.Conventions.Models.LocationModel Location) { }
- public DependencyModules.Conventions.Models.LocationModel Location { get; init; }
+ public UnreadableStatementModel(string Text, string Reason, DependencyModules.SourceGenerator.Impl.Models.LocationModel Location) { }
+ public DependencyModules.SourceGenerator.Impl.Models.LocationModel Location { get; init; }
public string Reason { get; init; }
public string Text { get; init; }
}
@@ -1091,7 +1077,9 @@ namespace DependencyModules.SourceGenerator.Impl
public static readonly Microsoft.CodeAnalysis.DiagnosticDescriptor GeneratorFailure;
public static readonly Microsoft.CodeAnalysis.DiagnosticDescriptor InterceptorCannotServeMembers;
public static readonly Microsoft.CodeAnalysis.DiagnosticDescriptor ModuleAttributeNamespaceNotImported;
+ public static readonly Microsoft.CodeAnalysis.DiagnosticDescriptor ModuleCannotBeNested;
public static readonly Microsoft.CodeAnalysis.DiagnosticDescriptor ModuleMustBePartial;
+ public static readonly Microsoft.CodeAnalysis.DiagnosticDescriptor ModuleWithPropertiesShouldImplementEquals;
public static readonly Microsoft.CodeAnalysis.DiagnosticDescriptor OpenGenericCannotBeDecorated;
public static readonly Microsoft.CodeAnalysis.DiagnosticDescriptor RegisteredConditionally;
public static readonly Microsoft.CodeAnalysis.DiagnosticDescriptor ServiceCannotBeConstructed;
@@ -1262,7 +1250,7 @@ namespace DependencyModules.SourceGenerator.Impl.Models
public class DecoratorModel : System.IEquatable
{
public static readonly DependencyModules.SourceGenerator.Impl.Models.DecoratorModel Ignore;
- public DecoratorModel(CSharpAuthor.ITypeDefinition ServiceType, CSharpAuthor.ITypeDefinition DecoratorType, int Order, CSharpAuthor.ITypeDefinition? Realm, System.Collections.Generic.IReadOnlyList? Conditions = null, DependencyModules.SourceGenerator.Impl.Models.ConstructorInfoModel? Constructor = null, int InnerParameterIndex = -1, bool TypeParametersMatchService = true) { }
+ public DecoratorModel(CSharpAuthor.ITypeDefinition ServiceType, CSharpAuthor.ITypeDefinition DecoratorType, int Order, CSharpAuthor.ITypeDefinition? Realm, System.Collections.Generic.IReadOnlyList? Conditions = null, DependencyModules.SourceGenerator.Impl.Models.ConstructorInfoModel? Constructor = null, int InnerParameterIndex = -1, bool TypeParametersMatchService = true, DependencyModules.SourceGenerator.Impl.Models.LocationModel? Location = null) { }
public bool CanMonomorphise { get; }
public System.Collections.Generic.IReadOnlyList? Conditions { get; init; }
public DependencyModules.SourceGenerator.Impl.Models.ConstructorInfoModel? Constructor { get; init; }
@@ -1271,6 +1259,7 @@ namespace DependencyModules.SourceGenerator.Impl.Models
public int InnerParameterIndex { get; init; }
public bool IsIgnored { get; }
public bool IsOpenGeneric { get; }
+ public DependencyModules.SourceGenerator.Impl.Models.LocationModel? Location { get; init; }
public int Order { get; init; }
public CSharpAuthor.ITypeDefinition? Realm { get; init; }
public CSharpAuthor.ITypeDefinition ServiceType { get; init; }
@@ -1374,18 +1363,20 @@ namespace DependencyModules.SourceGenerator.Impl.Models
public class InterceptorModel : System.IEquatable
{
public static readonly DependencyModules.SourceGenerator.Impl.Models.InterceptorModel Ignore;
- public InterceptorModel(CSharpAuthor.ITypeDefinition ServiceType, CSharpAuthor.ITypeDefinition ImplementationType, System.Collections.Generic.IReadOnlyList Interceptors, System.Collections.Generic.IReadOnlyList Members, System.Collections.Generic.IReadOnlyList Declarations, int Order, DependencyModules.SourceGenerator.Impl.Models.InterceptionRefusal? Refusal = null, System.Collections.Generic.IReadOnlyList? TypeParameters = null) { }
+ public InterceptorModel(CSharpAuthor.ITypeDefinition ServiceType, CSharpAuthor.ITypeDefinition ImplementationType, System.Collections.Generic.IReadOnlyList Interceptors, System.Collections.Generic.IReadOnlyList Members, System.Collections.Generic.IReadOnlyList Declarations, int Order, DependencyModules.SourceGenerator.Impl.Models.InterceptionRefusal? Refusal = null, System.Collections.Generic.IReadOnlyList? TypeParameters = null, CSharpAuthor.ITypeDefinition? Realm = null, DependencyModules.SourceGenerator.Impl.Models.LocationModel? Location = null) { }
public System.Collections.Generic.IReadOnlyList Declarations { get; init; }
public CSharpAuthor.ITypeDefinition ImplementationType { get; init; }
public System.Collections.Generic.IReadOnlyList Interceptors { get; init; }
public bool IsIgnored { get; }
public bool IsOpenGeneric { get; }
+ public DependencyModules.SourceGenerator.Impl.Models.LocationModel? Location { get; init; }
public System.Collections.Generic.IReadOnlyList Members { get; init; }
public int Order { get; init; }
+ public CSharpAuthor.ITypeDefinition? Realm { get; init; }
public DependencyModules.SourceGenerator.Impl.Models.InterceptionRefusal? Refusal { get; init; }
public CSharpAuthor.ITypeDefinition ServiceType { get; init; }
public System.Collections.Generic.IReadOnlyList? TypeParameters { get; init; }
- public static DependencyModules.SourceGenerator.Impl.Models.InterceptorModel Refused(string message) { }
+ public static DependencyModules.SourceGenerator.Impl.Models.InterceptorModel Refused(string message, DependencyModules.SourceGenerator.Impl.Models.LocationModel? location = null) { }
}
public class InterceptorModelComparer : System.Collections.Generic.IEqualityComparer
{
@@ -1402,6 +1393,21 @@ namespace DependencyModules.SourceGenerator.Impl.Models
public CSharpAuthor.ITypeDefinition Type { get; init; }
public bool CanServe(DependencyModules.SourceGenerator.Impl.Models.InterceptorKind kind) { }
}
+ public class LocationModel : System.IEquatable
+ {
+ public static readonly DependencyModules.SourceGenerator.Impl.Models.LocationModel None;
+ public LocationModel(string FilePath, int SpanStart, int SpanLength, int StartLine, int StartCharacter, int EndLine, int EndCharacter) { }
+ public int EndCharacter { get; init; }
+ public int EndLine { get; init; }
+ public string FilePath { get; init; }
+ public int SpanLength { get; init; }
+ public int SpanStart { get; init; }
+ public int StartCharacter { get; init; }
+ public int StartLine { get; init; }
+ public Microsoft.CodeAnalysis.Location ToLocation() { }
+ public Microsoft.CodeAnalysis.Location ToLocationOrNone() { }
+ public static DependencyModules.SourceGenerator.Impl.Models.LocationModel From(Microsoft.CodeAnalysis.SyntaxNode node) { }
+ }
public enum LogOutputLevel
{
Debug = 1,
@@ -1428,10 +1434,11 @@ namespace DependencyModules.SourceGenerator.Impl.Models
ShouldImplementEquals = 4,
IsRecord = 8,
NotPartial = 16,
+ NestedInType = 32,
}
public class ModuleEntryPointModel : DependencyModules.SourceGenerator.Impl.Models.IClassModel, System.IEquatable
{
- public ModuleEntryPointModel(DependencyModules.SourceGenerator.Impl.Models.ModuleEntryPointFeatures ModuleFeatures, string FileLocation, CSharpAuthor.ITypeDefinition EntryPointType, DependencyModules.SourceGenerator.Impl.Models.RegistrationType? RegistrationType, bool? GenerateAttribute, bool? RegisterJsonSerializers, string? UseMethod, bool? GenerateFactories, System.Collections.Generic.IReadOnlyList Parameters, System.Collections.Generic.IReadOnlyList PropertyInfoModels, System.Collections.Generic.IReadOnlyList AttributeModels, System.Collections.Generic.IReadOnlyList AdditionalModules, System.Collections.Generic.IReadOnlyList Features) { }
+ public ModuleEntryPointModel(DependencyModules.SourceGenerator.Impl.Models.ModuleEntryPointFeatures ModuleFeatures, string FileLocation, DependencyModules.SourceGenerator.Impl.Models.LocationModel Location, CSharpAuthor.ITypeDefinition EntryPointType, DependencyModules.SourceGenerator.Impl.Models.RegistrationType? RegistrationType, bool? GenerateAttribute, bool? RegisterJsonSerializers, string? UseMethod, bool? GenerateFactories, System.Collections.Generic.IReadOnlyList Parameters, System.Collections.Generic.IReadOnlyList PropertyInfoModels, System.Collections.Generic.IReadOnlyList AttributeModels, System.Collections.Generic.IReadOnlyList AdditionalModules, System.Collections.Generic.IReadOnlyList Features) { }
public System.Collections.Generic.IReadOnlyList AdditionalModules { get; init; }
public System.Collections.Generic.IReadOnlyList AttributeModels { get; init; }
public CSharpAuthor.ITypeDefinition ClassType { get; }
@@ -1440,6 +1447,7 @@ namespace DependencyModules.SourceGenerator.Impl.Models
public string FileLocation { get; init; }
public bool? GenerateAttribute { get; init; }
public bool? GenerateFactories { get; init; }
+ public DependencyModules.SourceGenerator.Impl.Models.LocationModel Location { get; init; }
public DependencyModules.SourceGenerator.Impl.Models.ModuleEntryPointFeatures ModuleFeatures { get; init; }
public System.Collections.Generic.IReadOnlyList Parameters { get; init; }
public System.Collections.Generic.IReadOnlyList PropertyInfoModels { get; init; }
@@ -1518,13 +1526,14 @@ namespace DependencyModules.SourceGenerator.Impl.Models
public class ServiceModel : System.IEquatable
{
public static DependencyModules.SourceGenerator.Impl.Models.ServiceModel Ignore;
- public ServiceModel(CSharpAuthor.ITypeDefinition ImplementationType, DependencyModules.SourceGenerator.Impl.Models.ConstructorInfoModel? Constructor, DependencyModules.SourceGenerator.Impl.Models.ServiceFactoryModel? Factory, DependencyModules.SourceGenerator.Impl.Models.FactoryOutputDelegate? FactoryOutput, System.Collections.Generic.IReadOnlyList Registrations, DependencyModules.SourceGenerator.Impl.Models.RegistrationFeature Features, System.Collections.Generic.IReadOnlyList? Conditions = null) { }
+ public ServiceModel(CSharpAuthor.ITypeDefinition ImplementationType, DependencyModules.SourceGenerator.Impl.Models.ConstructorInfoModel? Constructor, DependencyModules.SourceGenerator.Impl.Models.ServiceFactoryModel? Factory, DependencyModules.SourceGenerator.Impl.Models.FactoryOutputDelegate? FactoryOutput, System.Collections.Generic.IReadOnlyList Registrations, DependencyModules.SourceGenerator.Impl.Models.RegistrationFeature Features, System.Collections.Generic.IReadOnlyList? Conditions = null, DependencyModules.SourceGenerator.Impl.Models.LocationModel? Location = null) { }
public System.Collections.Generic.IReadOnlyList? Conditions { get; init; }
public DependencyModules.SourceGenerator.Impl.Models.ConstructorInfoModel? Constructor { get; init; }
public DependencyModules.SourceGenerator.Impl.Models.ServiceFactoryModel? Factory { get; init; }
public DependencyModules.SourceGenerator.Impl.Models.FactoryOutputDelegate? FactoryOutput { get; init; }
public DependencyModules.SourceGenerator.Impl.Models.RegistrationFeature Features { get; init; }
public CSharpAuthor.ITypeDefinition ImplementationType { get; init; }
+ public DependencyModules.SourceGenerator.Impl.Models.LocationModel? Location { get; init; }
public System.Collections.Generic.IReadOnlyList Registrations { get; init; }
}
public class ServiceModelComparer : System.Collections.Generic.IEqualityComparer