Skip to content

Commit

Permalink
Takes into account Nullable context settings when creating Pure.DI API
Browse files Browse the repository at this point in the history
  • Loading branch information
NikolayPianikov committed May 6, 2024
1 parent 5088988 commit 52a4c37
Show file tree
Hide file tree
Showing 28 changed files with 100 additions and 66 deletions.
4 changes: 2 additions & 2 deletions .run/Pack.run.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Pack" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/build/bin/roslyn4.3/Debug/net8.0/build.exe" />
<option name="PROGRAM_PARAMETERS" value="-p:version=2.1.0-dev pack" />
<option name="EXE_PATH" value="$PROJECT_DIR$/build/bin/Debug/net8.0/build.exe" />
<option name="PROGRAM_PARAMETERS" value="pack" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
Expand Down
2 changes: 1 addition & 1 deletion src/Pure.DI.Core/Core/Code/ArgFieldsBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public CompositionCode Build(CompositionCode composition)
var membersCounter = composition.MembersCount;
foreach (var arg in classArgs)
{
code.AppendLine($"private readonly {typeResolver.Resolve(arg.InstanceType)} {arg.VariableName};");
code.AppendLine($"private readonly {typeResolver.Resolve(arg.InstanceType)} {arg.VariableDeclarationName};");
membersCounter++;
}

Expand Down
37 changes: 15 additions & 22 deletions src/Pure.DI.Core/Core/Code/BlockCodeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,6 @@ variable.Node.Lifetime is Lifetime.Singleton or Lifetime.Scoped
// The "per resolve" instance should be created without checks if it is the only one in the composition
|| (variable.Node.Lifetime == Lifetime.PerResolve && variable.Info.RefCount > 1);

var parent = "";
if (variable.Node.Lifetime == Lifetime.Singleton)
{
parent = $"{Names.RootFieldName}.";
}

var uniqueAccumulators = ctx.Accumulators
.Where(accumulator => !accumulator.IsDeclared)
.GroupBy(i => i.Name)
Expand All @@ -58,8 +52,8 @@ variable.Node.Lifetime is Lifetime.Singleton or Lifetime.Scoped
if (toCheckExistence)
{
var checkExpression = variable.InstanceType.IsValueType
? $"!{parent}{variable.VariableName}Created"
: $"{parent}{variable.VariableName} == null";
? $"!{variable.VariableName}Created"
: $"{variable.VariableName} == null";

if (lockIsRequired)
{
Expand Down Expand Up @@ -90,19 +84,19 @@ variable.Node.Lifetime is Lifetime.Singleton or Lifetime.Scoped
}
}

if (variable.Node.Lifetime is Lifetime.Singleton)
{
code.AppendLine($"{parent}{variable.VariableName} = {variable.VariableName};");
variable.VariableCode = $"{parent}{variable.VariableName}";
}

if (!toCheckExistence)
{
return;
}

if (variable.Node.Lifetime is Lifetime.Singleton or Lifetime.Scoped && nodeInfo.IsDisposable(variable.Node))
{
var parent = "";
if (variable.Node.Lifetime == Lifetime.Singleton)
{
parent = $"{Names.RootFieldName}.";
}

code.AppendLine($"{parent}{Names.DisposablesFieldName}[{parent}{Names.DisposeIndexFieldName}++] = {variable.VariableName};");
}

Expand All @@ -113,7 +107,7 @@ variable.Node.Lifetime is Lifetime.Singleton or Lifetime.Scoped
code.AppendLine($"{Names.SystemNamespace}Threading.Thread.MemoryBarrier();");
}

code.AppendLine($"{parent}{variable.VariableName}Created = true;");
code.AppendLine($"{variable.VariableName}Created = true;");
}

code.DecIndent();
Expand All @@ -134,10 +128,10 @@ variable.Node.Lifetime is Lifetime.Singleton or Lifetime.Scoped
info.HasCode = true;
// ctx.Code.AppendLines(info.Code.Lines);
if (block.Parent is not null
&& info is { PerBlockRefCount: > 2, Code.Lines.Count: > 3 })
&& info is { PerBlockRefCount: > 1, Code.Lines.Count: > 3 })
{
var localFunctionsCode = ctx.LocalFunctionsCode;
var localMethodName = $"{Names.LocalMethodPrefix}{variable.VariableName}{Names.EnsureExistsMethodNamePostfix}";
var localMethodName = $"{Names.LocalMethodPrefix}{variable.VariableDeclarationName}{Names.EnsureExistsMethodNamePostfix}";
if (variable.Node.Binding.SemanticModel.Compilation.GetLanguageVersion() >= LanguageVersion.CSharp9)
{
localFunctionsCode.AppendLine($"[{Names.MethodImplAttribute}(({Names.MethodImplOptions})0x100)]");
Expand All @@ -151,12 +145,11 @@ variable.Node.Lifetime is Lifetime.Singleton or Lifetime.Scoped
}

localFunctionsCode.AppendLine("}");
ctx.Code.AppendLine($"{localMethodName}();");
}
else
{
ctx.Code.AppendLines(info.Code.Lines);
info.Code = new LinesBuilder();
info.Code.AppendLine($"{localMethodName}();");
}

ctx.Code.AppendLines(info.Code.Lines);
}
}

Expand Down
23 changes: 20 additions & 3 deletions src/Pure.DI.Core/Core/Code/BuildTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,26 @@ public string GetDeclaration(Variable variable) =>

public string OnInjected(BuildContext ctx, Variable variable)
{
var variableCode = variable.VariableCode;
if (variableCode == variable.VariableName)
{
var skipNotNullCheck = (variable.HasCycle || variable.Info.HasCode)
&& variable.InstanceType.IsReferenceType
&& ctx.DependencyGraph.Source.SemanticModel.Compilation.Options.NullableContextOptions != NullableContextOptions.Disable;

if (skipNotNullCheck)
{
variableCode = variable.Node.Lifetime switch
{
Lifetime.Singleton or Lifetime.PerResolve or Lifetime.Scoped => $"{variableCode}!",
_ => variableCode
};
}
}

if (!ctx.DependencyGraph.Source.Hints.IsOnDependencyInjectionEnabled)
{
return variable.VariableCode;
return variableCode;
}

if (!filter.IsMeetRegularExpression(
Expand All @@ -32,11 +49,11 @@ public string OnInjected(BuildContext ctx, Variable variable)
(Hint.OnDependencyInjectionTagRegularExpression, variable.Injection.Tag.ValueToString()),
(Hint.OnDependencyInjectionLifetimeRegularExpression, variable.Node.Lifetime.ValueToString())))
{
return variable.VariableCode;
return variableCode;
}

var tag = GetTag(ctx, variable);
return $"{Names.OnDependencyInjectionMethodName}<{variable.ContractType}>({variable.VariableCode}, {tag.ValueToString()}, {variable.Node.Lifetime.ValueToString()})";
return $"{Names.OnDependencyInjectionMethodName}<{variable.ContractType}>({variableCode}, {tag.ValueToString()}, {variable.Node.Lifetime.ValueToString()})";
}

public IEnumerable<Line> OnCreated(BuildContext ctx, Variable variable)
Expand Down
10 changes: 7 additions & 3 deletions src/Pure.DI.Core/Core/Code/ClassBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,13 @@ public CompositionCode Build(CompositionCode composition)
var code = composition.Code;
code.AppendLine("// <auto-generated/>");
code.AppendLine($"// by {information.Description}");
code.AppendLine("#nullable enable");
code.AppendLine("#pragma warning disable");
if (composition.Source.Source.SemanticModel.Compilation.Options.NullableContextOptions != NullableContextOptions.Disable)
{
code.AppendLine("#nullable enable");
}

code.AppendLine("#pragma warning disable CS0436");

code.AppendLine();

composition = usingDeclarationsBuilder.Build(composition);
Expand Down Expand Up @@ -98,7 +103,6 @@ public CompositionCode Build(CompositionCode composition)
code.AppendLine("}");
}

code.AppendLine("#pragma warning restore");
return composition;
}
}
2 changes: 1 addition & 1 deletion src/Pure.DI.Core/Core/Code/ClassCommenter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ IReadOnlyCollection<string> CreateRootDescriptions(Root root) =>
code.AppendLine("/// <example>");
code.AppendLine($"/// This shows how to get an instance of type {formatter.FormatRef(root.Node.Type)} using the composition root {formatter.FormatRef(root)}:");
code.AppendLine("/// <code>");
code.AppendLine($"/// {(composition.TotalDisposablesCount == 0 ? "" : "using ")}var composition = new {composition.Source.Source.Name.ClassName}({string.Join(", ", composition.Args.Where(i => i.Node.Arg?.Source.Kind == ArgKind.Class).Select(arg => arg.VariableName))});");
code.AppendLine($"/// {(composition.TotalDisposablesCount == 0 ? "" : "using ")}var composition = new {composition.Source.Source.Name.ClassName}({string.Join(", ", composition.Args.Where(i => i.Node.Arg?.Source.Kind == ArgKind.Class).Select(arg => arg.VariableDeclarationName))});");
code.AppendLine($"/// var instance = composition.{formatter.Format(root)};");
code.AppendLine("/// </code>");
code.AppendLine("/// </example>");
Expand Down
2 changes: 1 addition & 1 deletion src/Pure.DI.Core/Core/Code/ClassDiagramBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ private string FormatRoot(Root root)
var rootArgsStr = "";
if (root.IsMethod)
{
rootArgsStr = $"({string.Join(", ", root.Args.Select(arg => $"{typeResolver.Resolve(arg.InstanceType)} {arg.VariableName}"))})";
rootArgsStr = $"({string.Join(", ", root.Args.Select(arg => $"{typeResolver.Resolve(arg.InstanceType)} {arg.VariableDeclarationName}"))})";
}

var displayName = root.IsPublic ? root.DisplayName : "_";
Expand Down
15 changes: 11 additions & 4 deletions src/Pure.DI.Core/Core/Code/ConstructCodeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ private void BuildEnumerable(BuildContext ctx, in DpConstruct enumerable, string
var variable = ctx.Variable;
var code = ctx.Code;
var level = ctx.Level + 1;
var localFuncName = $"{Names.LocalMethodPrefix}{variable.VariableName}";
var localFuncName = $"{Names.LocalMethodPrefix}{variable.VariableDeclarationName}";
if (enumerable.Source.SemanticModel.Compilation.GetLanguageVersion() >= LanguageVersion.CSharp9)
{
code.AppendLine($"[{Names.MethodImplAttribute}(({Names.MethodImplOptions})0x200)]");
Expand All @@ -67,10 +67,17 @@ private void BuildEnumerable(BuildContext ctx, in DpConstruct enumerable, string
code.AppendLine($"yield return {ctx.BuildTools.OnInjected(ctx, statement.Current)};");
hasYieldReturn = true;
}

if (!hasYieldReturn)

if (methodPrefix == "async ")
{
code.AppendLine("await Task.CompletedTask;");
}
else
{
code.AppendLine("yield break;");
if (!hasYieldReturn)
{
code.AppendLine("yield break;");
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/Pure.DI.Core/Core/Code/DisposeMethodBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,8 @@ private static void AddSyncPart(CompositionCode composition, LinesBuilder code)
{
code.AppendLine(
singletonField.InstanceType.IsValueType
? $"{singletonField.VariableName}Created = false;"
: $"{singletonField.VariableName} = null;");
? $"{singletonField.VariableDeclarationName}Created = false;"
: $"{singletonField.VariableDeclarationName} = null;");
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/Pure.DI.Core/Core/Code/FactoryCodeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public void Build(BuildContext ctx, in DpFactory factory)
}

// Rewrites syntax tree
var finishLabel = $"{variable.VariableName}Finish";
var finishLabel = $"{variable.VariableDeclarationName}Finish";
var injections = new List<FactoryRewriter.Injection>();
var localVariableRenamingRewriter = new LocalVariableRenamingRewriter(idGenerator, factory.Source.SemanticModel);
var factoryExpression = localVariableRenamingRewriter.Rewrite(factory.Source.Factory);
Expand Down
7 changes: 4 additions & 3 deletions src/Pure.DI.Core/Core/Code/FieldsBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public CompositionCode Build(CompositionCode composition)
{
var code = composition.Code;
var membersCounter = composition.MembersCount;
var nullable = composition.Source.Source.SemanticModel.Compilation.Options.NullableContextOptions == NullableContextOptions.Disable ? "" : "?";

// _parent filed
code.AppendLine($"private readonly {composition.Source.Source.Name.ClassName} {Names.RootFieldName};");
Expand Down Expand Up @@ -36,15 +37,15 @@ public CompositionCode Build(CompositionCode composition)
{
if (singletonField.InstanceType.IsValueType)
{
code.AppendLine($"private {typeResolver.Resolve(singletonField.InstanceType)} {singletonField.VariableName};");
code.AppendLine($"private {typeResolver.Resolve(singletonField.InstanceType)} {singletonField.VariableDeclarationName};");
membersCounter++;

code.AppendLine($"private bool {singletonField.VariableName}Created;");
code.AppendLine($"private bool {singletonField.VariableDeclarationName}Created;");
membersCounter++;
}
else
{
code.AppendLine($"private {typeResolver.Resolve(singletonField.InstanceType)} {singletonField.VariableName};");
code.AppendLine($"private {typeResolver.Resolve(singletonField.InstanceType)}{nullable} {singletonField.VariableDeclarationName};");
membersCounter++;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Pure.DI.Core/Core/Code/Formatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public string Format(Root root)
}

sb.Append('(');
sb.Append(string.Join(", ", root.Args.Select(i => i.VariableName)));
sb.Append(string.Join(", ", root.Args.Select(i => i.VariableDeclarationName)));
sb.Append(')');
return sb.ToString();
}
Expand Down
4 changes: 2 additions & 2 deletions src/Pure.DI.Core/Core/Code/ImplementationCodeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ public void Build(BuildContext ctx, in DpImplementation implementation)

if (tempVariableInit)
{
ctx = ctx with { Variable = variable with { NameOverride = variable.VariableName + "Temp" } };
ctx.Code.AppendLine($"{typeResolver.Resolve(ctx.Variable.InstanceType)} {ctx.Variable.VariableName};");
ctx = ctx with { Variable = variable with { NameOverride = variable.VariableDeclarationName + "Temp" } };
ctx.Code.AppendLine($"{typeResolver.Resolve(ctx.Variable.InstanceType)} {ctx.Variable.VariableDeclarationName};");
if (onCreatedStatements.Any())
{
onCreatedStatements = ctx.BuildTools.OnCreated(ctx, ctx.Variable).ToArray();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public CompositionCode Build(CompositionCode composition)

foreach (var arg in classArgs)
{
code.AppendLine($"{arg.VariableName} = {arg.Node.Arg?.Source.ArgName};");
code.AppendLine($"{arg.VariableDeclarationName} = {arg.Node.Arg?.Source.ArgName};");
}

if (composition.TotalDisposablesCount > 0)
Expand Down
2 changes: 1 addition & 1 deletion src/Pure.DI.Core/Core/Code/RootMethodsBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ private void BuildRoot(CompositionCode composition, Root root)
var rootArgsStr = "";
if (root.IsMethod)
{
rootArgsStr = $"({string.Join(", ", root.Args.Select(arg => $"{typeResolver.Resolve(arg.InstanceType)} {arg.VariableName}"))})";
rootArgsStr = $"({string.Join(", ", root.Args.Select(arg => $"{typeResolver.Resolve(arg.InstanceType)} {arg.VariableDeclarationName}"))})";
buildTools.AddPureHeader(code);
}

Expand Down
2 changes: 1 addition & 1 deletion src/Pure.DI.Core/Core/Code/RootMethodsCommenter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public void AddComments(CompositionCode composition, Root root)
code.AppendLine("/// <example>");
code.AppendLine($"/// This shows how to get an instance of type {formatter.FormatRef(root.Node.Type)}:");
code.AppendLine("/// <code>");
code.AppendLine($"/// {(composition.TotalDisposablesCount == 0 ? "" : "using ")}var composition = new {composition.Source.Source.Name.ClassName}({string.Join(", ", composition.Args.Where(i => i.Node.Arg?.Source.Kind == ArgKind.Class).Select(arg => arg.VariableName))});");
code.AppendLine($"/// {(composition.TotalDisposablesCount == 0 ? "" : "using ")}var composition = new {composition.Source.Source.Name.ClassName}({string.Join(", ", composition.Args.Where(i => i.Node.Arg?.Source.Kind == ArgKind.Class).Select(arg => arg.VariableDeclarationName))});");
code.AppendLine($"/// var instance = composition.{formatter.Format(root)};");
code.AppendLine("/// </code>");
code.AppendLine("/// </example>");
Expand Down
9 changes: 8 additions & 1 deletion src/Pure.DI.Core/Core/Code/ScopeConstructorBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,20 @@ public CompositionCode Build(CompositionCode composition)
{
code.AppendLine($"{Names.DisposablesFieldName} = new object[{composition.DisposablesScopedCount.ToString()}];");
}
else
{
if (composition.TotalDisposablesCount > 0)
{
code.AppendLine($"{Names.DisposablesFieldName} = {Names.ParentScopeArgName}.{Names.DisposablesFieldName};");
}
}

var classArgs = composition.Args.GetArgsOfKind(ArgKind.Class).ToArray();
if (classArgs.Any())
{
foreach (var argsField in classArgs)
{
code.AppendLine($"{argsField.VariableName} = {Names.ParentScopeArgName}.{argsField.VariableName};");
code.AppendLine($"{argsField.VariableDeclarationName} = {Names.ParentScopeArgName}.{argsField.VariableDeclarationName};");
}
}
}
Expand Down
9 changes: 8 additions & 1 deletion src/Pure.DI.Core/Core/Code/Variable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,15 @@ internal record Variable(
public Variable Current => this;

public bool IsDeclared { get; } = Node.Lifetime is not Lifetime.Transient and not Lifetime.PerBlock || Node.Arg is not null;

public string VariableDeclarationName =>
string.IsNullOrEmpty(NameOverride)
? Node.GetVariableName(PerLifetimeId)
: NameOverride;

public string VariableName => string.IsNullOrEmpty(NameOverride) ? Node.GetVariableName(PerLifetimeId) : NameOverride;
public string VariableName => string.IsNullOrEmpty(NameOverride)
? (Node.Lifetime == Lifetime.Singleton ? $"{Names.RootFieldName}." : "") + Node.GetVariableName(PerLifetimeId)
: NameOverride;

public string VariableCode
{
Expand Down
2 changes: 1 addition & 1 deletion src/Pure.DI.Core/Core/Code/VariableInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ internal class VariableInfo

public int RefCount { get; private set; } = 1;

public int PerBlockRefCount => _perBlockRefCounts.Count;
public int PerBlockRefCount => _perBlockRefCounts.Count + 1;

public void AddRef(Block parentBlock)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Pure.DI.Core/Core/RootValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ private static string Format(Root root)
}

sb.Append('(');
sb.Append(string.Join(", ", root.Args.Select(i => i.VariableName)));
sb.Append(string.Join(", ", root.Args.Select(i => i.VariableDeclarationName)));
sb.Append(')');
return sb.ToString();
}
Expand Down
3 changes: 1 addition & 2 deletions tests/Pure.DI.IntegrationTests/AccumulatorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -309,8 +309,7 @@ public static void Main()
""".RunAsync(new Options
{
LanguageVersion = LanguageVersion.CSharp8,
NullableContextOptions = NullableContextOptions.Disable,
PreprocessorSymbols = ["NET", "NET6_0_OR_GREATER"]
NullableContextOptions = NullableContextOptions.Disable
} );

// Then
Expand Down
2 changes: 1 addition & 1 deletion tests/Pure.DI.IntegrationTests/FactoryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1157,6 +1157,6 @@ public static void Main()

// Then
result.Success.ShouldBeTrue(result);
result.GeneratedCode.Split(Environment.NewLine).Count(i => i.Contains(" = new Sample.Dependency2();")).ShouldBe(2);
result.GeneratedCode.Split(Environment.NewLine).Count(i => i.Contains(" = new Sample.Dependency2();")).ShouldBe(1);
}
}
Loading

0 comments on commit 52a4c37

Please sign in to comment.