22// ReactiveUI and Contributors licenses this file to you under the MIT license.
33// See the LICENSE file in the project root for full license information.
44
5- using System ;
65using System . Collections . Immutable ;
76using System . Runtime . CompilerServices ;
87
@@ -18,6 +17,11 @@ namespace Splat.DependencyInjection.SourceGenerator.CodeGeneration;
1817/// others share the one closure the compiler creates for <c>resolver</c>.
1918/// </para>
2019/// <para>
20+ /// Dependencies are resolved without the registration's contract. A contract names one registration of the type
21+ /// being registered; a constructor parameter or injected property names no contract, so it gets the default
22+ /// registration of its type.
23+ /// </para>
24+ /// <para>
2125/// A dependency the resolver does not have throws through a helper written once per file. The factory then holds a
2226/// call rather than the construction and concatenation of an exception, which keeps the code the factory runs on
2327/// every resolution small.
@@ -31,7 +35,7 @@ internal static class CodeGenerator
3135 /// <summary>The characters a registration typically takes.</summary>
3236 internal const int RegistrationCapacity = 384 ;
3337
34- /// <summary>The name of the helpers that throw for an unregistered dependency.</summary>
38+ /// <summary>The name of the helper that throws for an unregistered dependency.</summary>
3539 internal const string ThrowNotRegistered = "ThrowNotRegistered" ;
3640
3741 /// <summary>The start of a registration on the resolver, before the registered type.</summary>
@@ -74,7 +78,7 @@ internal static partial class SplatRegistrations
7478 static partial void SetupIOCInternal(Splat.IDependencyResolver resolver)
7579 """ ;
7680
77- /// <summary>The helper that throws for a dependency registered without a contract .</summary>
81+ /// <summary>The helper that throws for an unregistered dependency .</summary>
7882 private const string ThrowNotRegisteredMethod = $$ """
7983 /// <summary>Throws for a dependency the resolver has no registration for.</summary>
8084 /// <typeparam name="T">The type of the dependency.</typeparam>
@@ -86,33 +90,6 @@ static partial void SetupIOCInternal(Splat.IDependencyResolver resolver)
8690 }
8791 """ ;
8892
89- /// <summary>The helper that throws for a dependency registered under a contract.</summary>
90- private const string ThrowNotRegisteredWithContractMethod = $$ """
91- /// <summary>Throws for a dependency the resolver has no registration for under a contract.</summary>
92- /// <typeparam name="T">The type of the dependency.</typeparam>
93- /// <param name="typeName">The name of the dependency's type.</param>
94- /// <param name="contract">The contract the dependency was asked for under.</param>
95- /// <returns>Never returns.</returns>
96- private static T {{ ThrowNotRegistered }} <T>(string typeName, string contract)
97- {
98- throw new global::System.InvalidOperationException("Dependency '" + typeName + "' with contract " + contract + " not registered with Splat resolver.");
99- }
100- """ ;
101-
102- /// <summary>Which throw helpers the written registrations call.</summary>
103- [ Flags ]
104- internal enum ThrowHelpers
105- {
106- /// <summary>No helper is called.</summary>
107- None = 0 ,
108-
109- /// <summary>The helper for a dependency without a contract is called.</summary>
110- WithoutContract = 1 << 0 ,
111-
112- /// <summary>The helper for a dependency under a contract is called.</summary>
113- WithContract = 1 << 1 ,
114- }
115-
11693 /// <summary>Writes the registrations file.</summary>
11794 /// <param name="registrations">The registrations, in source order.</param>
11895 /// <returns>The source.</returns>
@@ -131,7 +108,7 @@ internal static string Generate(ImmutableArray<RegistrationInfo> registrations)
131108 . Lines ( MethodDeclaration )
132109 . OpenBlock ( ) ;
133110
134- var used = ThrowHelpers . None ;
111+ var usesThrowHelper = false ;
135112 var written = 0 ;
136113 var lazySingletons = 0 ;
137114 foreach ( var registration in registrations )
@@ -143,7 +120,7 @@ internal static string Generate(ImmutableArray<RegistrationInfo> registrations)
143120
144121 SeparateRegistration ( writer , written ) ;
145122 written ++ ;
146- AppendTransient ( writer , registration , ref used ) ;
123+ AppendTransient ( writer , registration , ref usesThrowHelper ) ;
147124 }
148125
149126 foreach ( var registration in registrations )
@@ -155,42 +132,42 @@ internal static string Generate(ImmutableArray<RegistrationInfo> registrations)
155132
156133 SeparateRegistration ( writer , written ) ;
157134 written ++ ;
158- AppendLazySingleton ( writer , registration , lazySingletons , ref used ) ;
135+ AppendLazySingleton ( writer , registration , lazySingletons , ref usesThrowHelper ) ;
159136 lazySingletons ++ ;
160137 }
161138
162139 _ = writer . CloseBlock ( ) ;
163- AppendThrowHelpers ( writer , used ) ;
140+ AppendThrowHelper ( writer , usesThrowHelper ) ;
164141 return writer . CloseBlock ( ) . CloseBlock ( ) . ToStringAndReturn ( ) ;
165142 }
166143
167144 /// <summary>Writes a transient registration.</summary>
168145 /// <param name="writer">The writer, at the method body's level.</param>
169146 /// <param name="registration">The registration.</param>
170- /// <param name="used">The throw helpers called so far, updated with those this registration calls.</param>
147+ /// <param name="usesThrowHelper">Set when this registration calls the throw helper .</param>
171148 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
172- internal static void AppendTransient ( SourceWriter writer , RegistrationInfo registration , ref ThrowHelpers used ) =>
149+ internal static void AppendTransient ( SourceWriter writer , RegistrationInfo registration , ref bool usesThrowHelper ) =>
173150 AppendFactoryCall (
174151 writer . Append ( RegisterOpen ) . Append ( registration . InterfaceTypeFullName ) . Append ( ">(" ) ,
175152 registration ,
176153 registration . ContractValue ,
177- ref used ) ;
154+ ref usesThrowHelper ) ;
178155
179156 /// <summary>Writes a lazy singleton registration: the lazy value, and the lazy and its value registered.</summary>
180157 /// <param name="writer">The writer, at the method body's level.</param>
181158 /// <param name="registration">The registration.</param>
182159 /// <param name="index">The number of lazy singletons written before this one, which names its local.</param>
183- /// <param name="used">The throw helpers called so far, updated with those this registration calls.</param>
160+ /// <param name="usesThrowHelper">Set when this registration calls the throw helper .</param>
184161 /// <remarks>
185162 /// Each lazy is a local of the method itself, named by its position, rather than of a block of its own. The
186163 /// compiler keeps every captured local of one scope in one closure object, and <c>resolver</c> already needs one,
187164 /// so the lambdas that read the lazy add no closure; a block per registration would add one each.
188165 /// </remarks>
189- internal static void AppendLazySingleton ( SourceWriter writer , RegistrationInfo registration , int index , ref ThrowHelpers used )
166+ internal static void AppendLazySingleton ( SourceWriter writer , RegistrationInfo registration , int index , ref bool usesThrowHelper )
190167 {
191168 var serviceType = registration . InterfaceTypeFullName ;
192169 _ = writer . Append ( "var " ) . Append ( LazyLocal ) . Append ( index ) . Append ( " = new " ) . Append ( LazyTypeOpen ) . Append ( serviceType ) . Append ( ">(" ) ;
193- AppendFactoryCall ( writer , registration , registration . LazyThreadSafetyMode , ref used ) ;
170+ AppendFactoryCall ( writer , registration , registration . LazyThreadSafetyMode , ref usesThrowHelper ) ;
194171
195172 _ = writer . Append ( RegisterOpen ) . Append ( LazyTypeOpen ) . Append ( serviceType ) . Append ( ">>(() => " ) . Append ( LazyLocal ) . Append ( index ) ;
196173 _ = AppendTrailingArgument ( writer , registration . ContractValue ) . Line ( ");" ) ;
@@ -203,12 +180,12 @@ internal static void AppendLazySingleton(SourceWriter writer, RegistrationInfo r
203180 /// <param name="writer">The writer, after the call's opening parenthesis.</param>
204181 /// <param name="registration">The registration.</param>
205182 /// <param name="lastArgument">The argument after the factory, as C# source; <see langword="null"/> for none.</param>
206- /// <param name="used">The throw helpers called so far, updated with those the factory calls .</param>
183+ /// <param name="usesThrowHelper">Set when the factory calls the throw helper .</param>
207184 /// <remarks>
208185 /// A factory with nothing to resolve stays on the call's line. Otherwise each argument takes its own line, one
209186 /// level deeper, and the call closes after the last of them.
210187 /// </remarks>
211- internal static void AppendFactoryCall ( SourceWriter writer , RegistrationInfo registration , string ? lastArgument , ref ThrowHelpers used )
188+ internal static void AppendFactoryCall ( SourceWriter writer , RegistrationInfo registration , string ? lastArgument , ref bool usesThrowHelper )
212189 {
213190 if ( registration . ConstructorParameters . Length == 0 && registration . PropertyInjections . Length == 0 )
214191 {
@@ -217,7 +194,7 @@ internal static void AppendFactoryCall(SourceWriter writer, RegistrationInfo reg
217194 return ;
218195 }
219196
220- AppendFactory ( writer . OpenContinuation ( ) , registration , ref used ) ;
197+ AppendFactory ( writer . OpenContinuation ( ) , registration , ref usesThrowHelper ) ;
221198 if ( lastArgument is not null )
222199 {
223200 _ = writer . ArgumentSeparator ( ) . Append ( lastArgument ) ;
@@ -229,11 +206,10 @@ internal static void AppendFactoryCall(SourceWriter writer, RegistrationInfo reg
229206 /// <summary>Writes the lambda that constructs a registration's type, with its dependencies one per line.</summary>
230207 /// <param name="writer">The writer, at the start of a line.</param>
231208 /// <param name="registration">The registration.</param>
232- /// <param name="used">The throw helpers called so far, updated with those the factory calls .</param>
209+ /// <param name="usesThrowHelper">Set when the factory calls the throw helper .</param>
233210 /// <remarks>Leaves the writer on the lambda's last line, for the caller to finish.</remarks>
234- internal static void AppendFactory ( SourceWriter writer , RegistrationInfo registration , ref ThrowHelpers used )
211+ internal static void AppendFactory ( SourceWriter writer , RegistrationInfo registration , ref bool usesThrowHelper )
235212 {
236- var contract = registration . ContractValue ;
237213 _ = writer . Append ( "() => new " ) . Append ( registration . ConcreteTypeFullName ) . Append ( '(' ) ;
238214
239215 var parameters = registration . ConstructorParameters ;
@@ -247,7 +223,7 @@ internal static void AppendFactory(SourceWriter writer, RegistrationInfo registr
247223 _ = writer . ArgumentSeparator ( ) ;
248224 }
249225
250- AppendParameter ( writer , parameters [ i ] , contract , ref used ) ;
226+ AppendParameter ( writer , parameters [ i ] , ref usesThrowHelper ) ;
251227 }
252228
253229 _ = writer . Outdent ( ) ;
@@ -266,7 +242,7 @@ internal static void AppendFactory(SourceWriter writer, RegistrationInfo registr
266242 {
267243 var property = properties [ i ] ;
268244 _ = writer . Append ( property . PropertyName ) . Append ( " = " ) ;
269- AppendService ( writer , property . TypeFullName , contract , ref used ) ;
245+ AppendService ( writer , property . TypeFullName , ref usesThrowHelper ) ;
270246 _ = writer . ArgumentSeparator ( ) ;
271247 }
272248
@@ -276,55 +252,39 @@ internal static void AppendFactory(SourceWriter writer, RegistrationInfo registr
276252 /// <summary>Writes the expression that resolves a constructor parameter.</summary>
277253 /// <param name="writer">The writer.</param>
278254 /// <param name="parameter">The parameter.</param>
279- /// <param name="contract">The registration's contract, as C# source; <see langword="null"/> for none.</param>
280- /// <param name="used">The throw helpers called so far, updated with the one this parameter calls.</param>
255+ /// <param name="usesThrowHelper">Set when the parameter calls the throw helper.</param>
281256 /// <remarks>A collection resolves to every registration, which may be none, so it never throws.</remarks>
282- internal static void AppendParameter ( SourceWriter writer , in ConstructorParameter parameter , string ? contract , ref ThrowHelpers used )
257+ internal static void AppendParameter ( SourceWriter writer , in ConstructorParameter parameter , ref bool usesThrowHelper )
283258 {
284259 if ( parameter . Kind != DependencyKind . Collection )
285260 {
286- AppendService ( writer , parameter . TypeFullName , contract , ref used ) ;
261+ AppendService ( writer , parameter . TypeFullName , ref usesThrowHelper ) ;
287262 return ;
288263 }
289264
290- _ = writer . Append ( "resolver.GetServices<" ) . Append ( parameter . InnerTypeFullName ! ) . Append ( ">(" ) ;
291- _ = contract is null ? writer . Append ( ')' ) : writer . Append ( contract ) . Append ( ')' ) ;
265+ _ = writer . Append ( "resolver.GetServices<" ) . Append ( parameter . InnerTypeFullName ! ) . Append ( ">()" ) ;
292266 }
293267
294268 /// <summary>Writes the expression that resolves one service, throwing when it is not registered.</summary>
295269 /// <param name="writer">The writer.</param>
296270 /// <param name="typeFullName">The fully qualified type of the service.</param>
297- /// <param name="contract">The registration's contract, as C# source; <see langword="null"/> for none.</param>
298- /// <param name="used">The throw helpers called so far, updated with the one this service calls.</param>
299- internal static void AppendService ( SourceWriter writer , string typeFullName , string ? contract , ref ThrowHelpers used )
271+ /// <param name="usesThrowHelper">Set, as the expression calls the throw helper.</param>
272+ internal static void AppendService ( SourceWriter writer , string typeFullName , ref bool usesThrowHelper )
300273 {
301- _ = writer . Append ( "resolver.GetService<" ) . Append ( typeFullName ) . Append ( ">(" ) ;
302- if ( contract is null )
303- {
304- used |= ThrowHelpers . WithoutContract ;
305- _ = writer . Append ( ") ?? " ) . Append ( ThrowNotRegistered ) . Append ( '<' ) . Append ( typeFullName ) . Append ( ">(\" " ) . Append ( typeFullName ) . Append ( "\" )" ) ;
306- return ;
307- }
308-
309- used |= ThrowHelpers . WithContract ;
310- _ = writer . Append ( contract ) . Append ( ") ?? " ) . Append ( ThrowNotRegistered ) . Append ( '<' ) . Append ( typeFullName ) . Append ( ">(\" " )
311- . Append ( typeFullName ) . Append ( "\" , " ) . Append ( contract ) . Append ( ')' ) ;
274+ usesThrowHelper = true ;
275+ _ = writer . Append ( "resolver.GetService<" ) . Append ( typeFullName ) . Append ( ">() ?? " ) . Append ( ThrowNotRegistered )
276+ . Append ( '<' ) . Append ( typeFullName ) . Append ( ">(\" " ) . Append ( typeFullName ) . Append ( "\" )" ) ;
312277 }
313278
314- /// <summary>Writes the throw helpers the registrations call, after the registration method .</summary>
279+ /// <summary>Writes the throw helper after the registration method, when a registration calls it .</summary>
315280 /// <param name="writer">The writer, at the class body's level.</param>
316- /// <param name="used">The helpers the registrations call .</param>
317- internal static void AppendThrowHelpers ( SourceWriter writer , ThrowHelpers used )
281+ /// <param name="usesThrowHelper">Whether a registration calls the helper .</param>
282+ internal static void AppendThrowHelper ( SourceWriter writer , bool usesThrowHelper )
318283 {
319- if ( ( used & ThrowHelpers . WithoutContract ) != 0 )
284+ if ( usesThrowHelper )
320285 {
321286 _ = writer . BlankLine ( ) . Lines ( ThrowNotRegisteredMethod ) ;
322287 }
323-
324- if ( ( used & ThrowHelpers . WithContract ) != 0 )
325- {
326- _ = writer . BlankLine ( ) . Lines ( ThrowNotRegisteredWithContractMethod ) ;
327- }
328288 }
329289
330290 /// <summary>Writes a trailing argument when there is one.</summary>
0 commit comments