Skip to content

Commit 2c72c2f

Browse files
committed
Emit compiler warning and remarks depending on sponsoring status
This follows the mechanism in ThisAssembly. Then generating code inside an editor, we'll emit the APIs with `[Obsolete]` if status is unknown or expired. A grace-period remark will be added too as appropriate.
1 parent d401d36 commit 2c72c2f

File tree

3 files changed

+118
-19
lines changed

3 files changed

+118
-19
lines changed

src/DependencyInjection/DependencyInjection.csproj

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,30 @@
1212
<PackFolder>analyzers/dotnet</PackFolder>
1313
<IsRoslynComponent>true</IsRoslynComponent>
1414
<DevelopmentDependency>true</DevelopmentDependency>
15-
<DefineConstants>$(DefineConstants);DDI_ADDSERVICE</DefineConstants>
15+
<DefineConstants>$(DefineConstants);DDI_ADDSERVICE;DDI_ADDSERVICES</DefineConstants>
1616
<ImplicitUsings>false</ImplicitUsings>
1717
</PropertyGroup>
1818

1919
<ItemGroup>
20-
<None Update="Devlooped.Extensions.DependencyInjection.props" CopyToOutputDirectory="PreserveNewest" PackFolder="buildTransitive" />
21-
<None Update="Devlooped.Extensions.DependencyInjection.targets" CopyToOutputDirectory="PreserveNewest" PackFolder="buildTransitive" />
22-
<!--
23-
<Compile Update="AddServicesNoReflectionExtension.cs" Pack="true" />
24-
<Compile Update="ServiceAttribute*.cs" Pack="true" />
25-
-->
20+
<PackageReference Include="NuGetizer" Version="1.2.1" />
21+
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.2.0" Pack="false" />
22+
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
23+
<PackageReference Include="PolySharp" Version="1.14.1" PrivateAssets="all" />
24+
<PackageReference Include="ThisAssembly.Resources" Version="2.0.8" PrivateAssets="all" />
25+
</ItemGroup>
26+
27+
<ItemGroup>
28+
<None Update="Devlooped.Extensions.DependencyInjection.props" CopyToOutputDirectory="PreserveNewest" PackFolder="build" />
29+
<None Update="Devlooped.Extensions.DependencyInjection.targets" CopyToOutputDirectory="PreserveNewest" PackFolder="build" />
2630
<EmbeddedCode Include="ServiceAttribute*.cs;AddServicesNoReflectionExtension.cs" />
2731
</ItemGroup>
2832

33+
<PropertyGroup Label="SponsorLink">
34+
<CustomAfterMicrosoftCSharpTargets>$(MSBuildThisFileDirectory)..\SponsorLink\SponsorLink.Analyzer.targets</CustomAfterMicrosoftCSharpTargets>
35+
<!-- We also bring in the additional packages that just customize ThisAssembly.Constants -->
36+
<FundingPackageId>$(PackageId)</FundingPackageId>
37+
</PropertyGroup>
38+
2939
<Target Name="CopyEmbeddedCode" Inputs="@(EmbeddedCode)" Outputs="@(EmbeddedCode -> '$(IntermediateOutputPath)%(Filename).txt')">
3040
<Copy SourceFiles="@(EmbeddedCode)" DestinationFiles="@(EmbeddedCode -> '$(IntermediateOutputPath)%(Filename).txt')" SkipUnchangedFiles="true" />
3141
</Target>
@@ -36,14 +46,6 @@
3646
</ItemGroup>
3747
</Target>
3848

39-
<ItemGroup>
40-
<PackageReference Include="NuGetizer" Version="1.2.1" />
41-
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.2.0" Pack="false" />
42-
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
43-
<PackageReference Include="PolySharp" Version="1.14.1" PrivateAssets="all" />
44-
<PackageReference Include="ThisAssembly.Resources" Version="2.0.8" PrivateAssets="all" />
45-
</ItemGroup>
46-
4749
<Target Name="PokePackageVersion" BeforeTargets="GetPackageContents" DependsOnTargets="CopyFilesToOutputDirectory" Condition="'$(dotnet-nugetize)' == '' and Exists('$(OutputPath)\Devlooped.Extensions.DependencyInjection.props')">
4850
<XmlPoke XmlInputPath="$(OutputPath)\Devlooped.Extensions.DependencyInjection.props" Query="/Project/PropertyGroup/DevloopedExtensionsDependencyInjectionVersion" Value="$(PackageVersion)" />
4951
</Target>

src/DependencyInjection/Devlooped.Extensions.DependencyInjection.targets

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<Project>
2+
<Import Project="..\..\buildTransitive\Devlooped.Sponsors.targets" Condition="Exists('..\..\buildTransitive\Devlooped.Sponsors.targets')"/>
23

34
<PropertyGroup>
45
<!-- Backwards compatiblity -->
@@ -7,6 +8,11 @@
78
<DefineConstants Condition="'$(Language)' == 'C#' and '$(AddServicesExtension)' == 'true'">$(DefineConstants);DDI_ADDSERVICES</DefineConstants>
89
</PropertyGroup>
910

11+
<ItemGroup>
12+
<!-- Brings in the analyzer file to report installation time -->
13+
<FundingPackageId Include="Devlooped.Extensions.DependencyInjection" />
14+
</ItemGroup>
15+
1016
<Target Name="_AddDDI_Constant" BeforeTargets="CoreCompile">
1117
<PropertyGroup>
1218
<DefineConstants Condition="'$(Language)' == 'C#' and '$(AddServiceAttribute)' == 'true' and !$(DefineConstants.Contains('DDI_ADDSERVICE'))">$(DefineConstants);DDI_ADDSERVICE</DefineConstants>

src/DependencyInjection/StaticGenerator.cs

Lines changed: 95 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1-
using Microsoft.CodeAnalysis;
1+
using System.Collections.Generic;
2+
using System.Globalization;
3+
using System.IO;
4+
using System.Text;
5+
using Devlooped.Sponsors;
6+
using Microsoft.CodeAnalysis;
27
using Microsoft.Extensions.DependencyInjection;
8+
using static Devlooped.Sponsors.SponsorLink;
39

410
namespace Devlooped.Extensions.DependencyInjection;
511

@@ -30,8 +36,93 @@ public void Execute(GeneratorExecutionContext context)
3036
var className = context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.AddServicesClassName", out value) && !string.IsNullOrEmpty(value) ?
3137
value : DefaultAddServicesClass;
3238

33-
context.AddSource(DefaultAddServicesClass + ".g", ThisAssembly.Resources.AddServicesNoReflectionExtension.Text
34-
.Replace("namespace " + DefaultNamespace, "namespace " + rootNs)
35-
.Replace(DefaultAddServicesClass, className));
39+
var code = ThisAssembly.Resources.AddServicesNoReflectionExtension.Text
40+
.Replace("namespace " + DefaultNamespace, "namespace " + rootNs)
41+
.Replace(DefaultAddServicesClass, className);
42+
43+
if (IsEditor)
44+
{
45+
var status = Diagnostics.GetOrSetStatus(context.GetStatusOptions());
46+
string? remarks = default;
47+
string? warn = default;
48+
49+
if (status == SponsorStatus.Unknown || status == SponsorStatus.Expired)
50+
{
51+
warn =
52+
$"""
53+
[Obsolete("{string.Format(CultureInfo.CurrentCulture, Resources.Editor_Disabled, Funding.Product, Funding.HelpUrl)}", false
54+
#if NET6_0_OR_GREATER
55+
, UrlFormat = "{Funding.HelpUrl}"
56+
#endif
57+
)]
58+
""";
59+
60+
remarks = Resources.Editor_DisabledRemarks;
61+
}
62+
else if (status == SponsorStatus.Grace && Diagnostics.TryGet() is { } grace && grace.Properties.TryGetValue(nameof(SponsorStatus.Grace), out var days))
63+
{
64+
remarks = string.Format(CultureInfo.CurrentCulture, Resources.Editor_GraceRemarks, days);
65+
}
66+
67+
if (remarks != null)
68+
{
69+
// Remove /// <remarks> and /// </remarks> LINES from the remarks string
70+
var builder = new StringBuilder();
71+
foreach (var line in ReadLines(remarks))
72+
{
73+
if (line.EndsWith("/// <remarks>") || line.EndsWith("/// </remarks>"))
74+
continue;
75+
if (line.TrimStart() is { Length: > 0 } trimmed && trimmed.StartsWith("///"))
76+
builder.AppendLine(trimmed);
77+
}
78+
remarks = builder.AppendLine("///").ToString();
79+
}
80+
81+
if (remarks != null || warn != null)
82+
{
83+
var builder = new StringBuilder();
84+
foreach (var line in ReadLines(code))
85+
{
86+
if (remarks != null && line.EndsWith("/// <remarks>"))
87+
{
88+
builder.AppendLine(line);
89+
// trim the remarks line to remove leading spaces and
90+
// replace them with the indenting from the target code line
91+
var indent = line.IndexOf("/// <remarks>");
92+
foreach (var rline in ReadLines(remarks))
93+
{
94+
builder.Append(new string(' ', indent)).AppendLine(rline);
95+
}
96+
}
97+
else if (warn != null && line.EndsWith("[DDIAddServices]"))
98+
{
99+
builder.AppendLine(line);
100+
// trim the remarks line to remove leading spaces and
101+
// replace them with the indenting from the target code line
102+
var indent = line.IndexOf("[DDIAddServices]");
103+
// append indentation and the warning, also splitting lines and trimming start
104+
foreach (var wline in ReadLines(warn))
105+
{
106+
builder.Append(new string(' ', indent)).AppendLine(wline.TrimStart());
107+
}
108+
}
109+
else
110+
{
111+
builder.AppendLine(line);
112+
}
113+
}
114+
code = builder.ToString();
115+
}
116+
}
117+
118+
context.AddSource(DefaultAddServicesClass + ".g", code);
119+
}
120+
121+
static IEnumerable<string> ReadLines(string text)
122+
{
123+
using var reader = new StringReader(text);
124+
string? line;
125+
while ((line = reader.ReadLine()) != null)
126+
yield return line;
36127
}
37128
}

0 commit comments

Comments
 (0)