diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fe08fbf5b..4565da072 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,6 +75,7 @@ jobs: dotnet pack src/bunit/ -c Release --output ${{ env.NUGET_DIRECTORY }} -p:ContinuousIntegrationBuild=true -p:publicrelease=true dotnet pack src/bunit.template/ -c Release --output ${{ env.NUGET_DIRECTORY }} -p:ContinuousIntegrationBuild=true -p:publicrelease=true dotnet pack src/bunit.web.query/ -c release --output ${{ env.NUGET_DIRECTORY }} -p:ContinuousIntegrationBuild=true -p:publicrelease=true + dotnet pack src/bunit.generators/ -c release --output ${{ env.NUGET_DIRECTORY }} -p:ContinuousIntegrationBuild=true -p:publicrelease=true # Publish the NuGet package as an artifact, so they can be used in the following jobs - uses: actions/upload-artifact@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d164e7c3c..09a1ac5c9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -99,6 +99,7 @@ jobs: dotnet pack src/bunit/ -c Release --property:PackageOutputPath=${GITHUB_WORKSPACE}/packages -p:ContinuousIntegrationBuild=true -p:publicrelease=true dotnet pack src/bunit.template/ -c Release --property:PackageOutputPath=${GITHUB_WORKSPACE}/packages -p:ContinuousIntegrationBuild=true -p:publicrelease=true dotnet pack src/bunit.web.query/ -c Release --property:PackageOutputPath=${GITHUB_WORKSPACE}/packages -p:ContinuousIntegrationBuild=true -p:publicrelease=true + dotnet pack src/bunit.generators/ -c Release --property:PackageOutputPath=${GITHUB_WORKSPACE}/packages -p:ContinuousIntegrationBuild=true -p:publicrelease=true - name: 🛠️ Upload library to NuGet.org repository run: | diff --git a/Directory.Packages.props b/Directory.Packages.props index ea2bd036a..fe9b04b1f 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -101,10 +101,10 @@ - - - - + + + + diff --git a/bunit.sln b/bunit.sln index 6561aef97..e5b5294d4 100644 --- a/bunit.sln +++ b/bunit.sln @@ -60,6 +60,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "bunit.generators", "src\bun EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "bunit.tests", "tests\bunit.tests\bunit.tests.csproj", "{56889DE7-5E66-4E9C-815B-CBCFC9961612}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "bunit.generators.tests", "tests\bunit.generators.tests\bunit.generators.tests.csproj", "{D08F7F1D-74B1-4A76-86A2-94918863740C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -96,6 +98,10 @@ Global {56889DE7-5E66-4E9C-815B-CBCFC9961612}.Debug|Any CPU.Build.0 = Debug|Any CPU {56889DE7-5E66-4E9C-815B-CBCFC9961612}.Release|Any CPU.ActiveCfg = Release|Any CPU {56889DE7-5E66-4E9C-815B-CBCFC9961612}.Release|Any CPU.Build.0 = Release|Any CPU + {D08F7F1D-74B1-4A76-86A2-94918863740C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D08F7F1D-74B1-4A76-86A2-94918863740C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D08F7F1D-74B1-4A76-86A2-94918863740C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D08F7F1D-74B1-4A76-86A2-94918863740C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -109,6 +115,7 @@ Global {AE3DFB52-2BF4-4806-AD82-7FB7B38AC17F} = {9A2B3B34-D41C-43E8-BC7D-246BEBE48D59} {A7C6A2AA-FF8F-4ED1-8590-5324FC566059} = {9A2B3B34-D41C-43E8-BC7D-246BEBE48D59} {56889DE7-5E66-4E9C-815B-CBCFC9961612} = {6EA09ED4-B714-4E6F-B0E1-4D987F8AE520} + {D08F7F1D-74B1-4A76-86A2-94918863740C} = {6EA09ED4-B714-4E6F-B0E1-4D987F8AE520} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {24106918-1C86-4769-BDA6-9C80E64CD260} diff --git a/src/bunit.generators/Web.Stubs/AddStubMethodStubGenerator/AddStubGenerator.cs b/src/bunit.generators/Web.Stubs/AddStubMethodStubGenerator/AddStubGenerator.cs index c7fadff33..a96d88980 100644 --- a/src/bunit.generators/Web.Stubs/AddStubMethodStubGenerator/AddStubGenerator.cs +++ b/src/bunit.generators/Web.Stubs/AddStubMethodStubGenerator/AddStubGenerator.cs @@ -2,6 +2,7 @@ using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp; // For GetInterceptableLocation API and InterceptableLocation type namespace Bunit.Web.Stubs.AddStubMethodStubGenerator; @@ -50,10 +51,11 @@ public void Initialize(IncrementalGeneratorInitializationContext context) return null; } - var path = GetInterceptorFilePath(context.Node.SyntaxTree, context.SemanticModel.Compilation); - var lineSpan = context.SemanticModel.SyntaxTree.GetLineSpan(context.Node.Span); - var line = lineSpan.StartLinePosition.Line + 1; - var column = lineSpan.Span.Start.Character + context.Node.ToString().IndexOf("AddStub", StringComparison.Ordinal) + 1; + var interceptableLocation = context.SemanticModel.GetInterceptableLocation(invocation); + if (interceptableLocation is null) + { + return null; + } var properties = symbol.GetAllMembersRecursively() .OfType() @@ -67,9 +69,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) TargetTypeNamespace = symbol.ContainingNamespace.ToDisplayString(), TargetTypeName = symbol.ToDisplayString(), Properties = properties, - Path = path, - Line = line, - Column = column, + Location = interceptableLocation, }; static bool IsComponentFactoryStubMethod(InvocationExpressionSyntax invocation, SemanticModel semanticModel) @@ -88,11 +88,6 @@ static bool IsComponentFactoryStubMethod(InvocationExpressionSyntax invocation, return semanticModel.GetSymbolInfo(invocation).Symbol is IMethodSymbol { IsExtensionMethod: true, ReceiverType.Name: "ComponentFactoryCollection" }; } - static string GetInterceptorFilePath(SyntaxTree tree, Compilation compilation) - { - return compilation.Options.SourceReferenceResolver?.NormalizePath(tree.FilePath, baseFilePath: null) ?? tree.FilePath; - } - static bool IsParameterOrCascadingParameter(ISymbol member) { return member.GetAttributes().Any(SupportedAttributes.IsSupportedAttribute); @@ -124,27 +119,21 @@ private static void Execute(ImmutableArray classInfos, SourceP private static void GenerateInterceptorCode(AddStubClassInfo stubbedComponentGroup, IEnumerable stubClassGrouped, SourceProductionContext context) { - // Generate the attribute - const string attribute = """ - namespace System.Runtime.CompilerServices - { - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - sealed file class InterceptsLocationAttribute : Attribute - { - public InterceptsLocationAttribute(string filePath, int line, int column) - { - _ = filePath; - _ = line; - _ = column; - } - } - } - """; - // Generate the interceptor var interceptorSource = new StringBuilder(1000); interceptorSource.AppendLine(HeaderProvider.Header); - interceptorSource.AppendLine(attribute); + interceptorSource.AppendLine("namespace System.Runtime.CompilerServices"); + interceptorSource.AppendLine("{"); + interceptorSource.AppendLine("\t[global::System.AttributeUsage(global::System.AttributeTargets.Method, AllowMultiple = true)]"); + interceptorSource.AppendLine("\tsealed file class InterceptsLocationAttribute : global::System.Attribute"); + interceptorSource.AppendLine("\t{"); + interceptorSource.AppendLine("\t\tpublic InterceptsLocationAttribute(int version, string data)"); + interceptorSource.AppendLine("\t\t{"); + interceptorSource.AppendLine("\t\t\t_ = version;"); + interceptorSource.AppendLine("\t\t\t_ = data;"); + interceptorSource.AppendLine("\t\t}"); + interceptorSource.AppendLine("\t}"); + interceptorSource.AppendLine("}"); interceptorSource.AppendLine(); interceptorSource.AppendLine("namespace Bunit"); interceptorSource.AppendLine("{"); @@ -153,8 +142,7 @@ public InterceptsLocationAttribute(string filePath, int line, int column) foreach (var hit in stubClassGrouped) { - interceptorSource.AppendLine( - $"\t\t[global::System.Runtime.CompilerServices.InterceptsLocationAttribute(@\"{hit.Path}\", {hit.Line}, {hit.Column})]"); + interceptorSource.AppendLine($"\t\t{hit.Location.GetInterceptsLocationAttributeSyntax()}"); } interceptorSource.AppendLine( @@ -213,9 +201,7 @@ internal sealed record AddStubClassInfo public required string TargetTypeName { get; set; } public string UniqueQualifier => $"{TargetTypeNamespace}.{StubClassName}"; public ImmutableArray Properties { get; set; } = ImmutableArray.Empty; - public required string Path { get; set; } - public int Line { get; set; } - public int Column { get; set; } + public required InterceptableLocation Location { get; set; } } internal sealed record StubPropertyInfo