Skip to content

Commit

Permalink
Add UseMauiCommunityToolkit() Analyzers (#486)
Browse files Browse the repository at this point in the history
* Rename `Source Generators` folder -> `Analyzers`

* Add Analyzer

* Update Analyzer

* Add Code Fix + Unit Test

* Port Unit Test to Xunit

* Add Tests, Update CodeFix

* Fix Typo

* Update Messaging

* Add Using Directive CodeFix

* Update Unit Tests

* Update BaseHandlerTest.cs

* Fixed typo

* Created a helper method to grab the InvocationExpressionSyntax

* improved the code

remove some comments; make SyntaxFactory static, pass the cancellationToken

* Remove UseCommunityToolkitAnalyzerTests

* Added a verification to check other methods in the class

* Fixed typo

* Added more checks to make sure we find all cases

* Update UseCommunityToolkitInitializationAnalyzer.cs

* Update RESX

* Update azure-pipelines.yml

* Update azure-pipelines.yml

* Create UseCommunityToolkitInitializationAnalyzerTests.cs

* Update azure-pipelines.yml

* Update azure-pipelines.yml

Co-authored-by: pictos <[email protected]>
  • Loading branch information
brminnick and pictos authored Jul 14, 2022
1 parent 38ec66f commit b3b2b9a
Show file tree
Hide file tree
Showing 20 changed files with 585 additions and 17 deletions.
50 changes: 48 additions & 2 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ variables:
PathToCommunityToolkitCoreCsproj: 'src/CommunityToolkit.Maui.Core/CommunityToolkit.Maui.Core.csproj'
PathToCommunityToolkitSampleCsproj: 'samples/CommunityToolkit.Maui.Sample/CommunityToolkit.Maui.Sample.csproj'
PathToCommunityToolkitUnitTestCsproj: 'src/CommunityToolkit.Maui.UnitTests/CommunityToolkit.Maui.UnitTests.csproj'
PathToCommunityToolkitAnalyzersCsproj: 'src/CommunityToolkit.Maui.Analyzers/CommunityToolkit.Maui.Analyzers.csproj'
PathToCommunityToolkitSourceGeneratorsCsproj: 'src/CommunityToolkit.Maui.SourceGenerators/CommunityToolkit.Maui.SourceGenerators.csproj'
PathToCommunityToolkitAnalyzersCodeFixCsproj: 'src/CommunityToolkit.Maui.Analyzers.CodeFixes/CommunityToolkit.Maui.Analyzers.CodeFixes.csproj'
PathToCommunityToolkitAnalyzersUnitTestCsproj: 'src/CommunityToolkit.Maui.Analyzers.UnitTests/CommunityToolkit.Maui.Analyzers.UnitTests.csproj'
XcodeVersion: '13.3.1'
RollbackFile: '6.0.312.json'

Expand Down Expand Up @@ -66,14 +70,40 @@ jobs:
Write-Host "##vso[build.updatebuildnumber]$fullVersionString"
displayName: Set NuGet Version to PR Version
condition: and(succeeded(), eq(variables['build.reason'], 'PullRequest'))
# build analyzers
- task: VSBuild@1
displayName: 'Build CommunityToolkit.Maui.Analyzers'
inputs:
solution: '$(PathToCommunityToolkitAnalyzersCsproj)'
configuration: 'Release'
msbuildArgs: '/restore'
- task: VSBuild@1
displayName: 'Build CommunityToolkit.Maui.Analyzers.CodeFixes'
inputs:
solution: '$(PathToCommunityToolkitAnalyzersCodeFixCsproj)'
configuration: 'Release'
msbuildArgs: '/restore'
- task: VSBuild@1
displayName: 'Build CommunityToolkit.Maui.SourceGenerators'
inputs:
solution: '$(PathToCommunityToolkitSourceGeneratorsCsproj)'
configuration: 'Release'
msbuildArgs: '/restore'
# test
- task: DotNetCoreCLI@2
displayName: 'Run Unit Tests'
displayName: 'Run CommunityToolkit.Maui Unit Tests'
inputs:
command: 'test'
projects: '$(PathToCommunityToolkitUnitTestCsproj)'
arguments: '--configuration Release --settings ".runsettings" --collect "XPlat code coverage" --logger trx --results-directory $(Agent.TempDirectory)'
publishTestResults: false
- task: DotNetCoreCLI@2
displayName: 'Run CommunityToolkit.Maui.Analyzers Unit Tests'
inputs:
command: 'test'
projects: '$(PathToCommunityToolkitAnalyzersUnitTestCsproj)'
arguments: '--configuration Release'
publishTestResults: false
- task: PublishTestResults@2
displayName: 'Publish Test Results'
inputs:
Expand Down Expand Up @@ -181,7 +211,23 @@ jobs:
inputs:
script: dotnet workload install maui --from-rollback-file $(RollbackFile) --source https://api.nuget.org/v3/index.json
- task: CmdLine@2
displayName: 'Run Unit Tests'
displayName: 'Build CommunityToolkit.Maui.Analyzers'
inputs:
script: 'dotnet build -c Release $(PathToCommunityToolkitAnalyzersCsproj)'
- task: CmdLine@2
displayName: 'Build CommunityToolkit.Maui.Analyzers.CodeFixes'
inputs:
script: 'dotnet build -c Release $(PathToCommunityToolkitAnalyzersCodeFixCsproj)'
- task: CmdLine@2
displayName: 'Build CommunityToolkit.Maui.SourceGenerators'
inputs:
script: 'dotnet build -c Release $(PathToCommunityToolkitSourceGeneratorsCsproj)'
- task: CmdLine@2
displayName: 'Run CommunityToolkit.Maui.Analyzers.UnitTests'
inputs:
script: 'dotnet test $(PathToCommunityToolkitAnalyzersUnitTestCsproj) -c Release'
- task: CmdLine@2
displayName: 'Run CommunityToolkit.Maui.UnitTests'
inputs:
script: 'dotnet test $(PathToCommunityToolkitUnitTestCsproj) -c Release'
- task: CmdLine@2
Expand Down
28 changes: 26 additions & 2 deletions samples/CommunityToolkit.Maui.Sample.sln
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,17 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Maui.Core"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Maui", "..\src\CommunityToolkit.Maui\CommunityToolkit.Maui.csproj", "{1BA2F867-25C1-4D7E-AE51-5466929D467A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Maui.SourceGenerators", "..\src\CommunityToolkit.Maui.SourceGenerators\CommunityToolkit.Maui.SourceGenerators.csproj", "{98B61BE8-4A47-4A56-82C1-40498DF12214}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Maui.SourceGenerators", "..\src\CommunityToolkit.Maui.SourceGenerators\CommunityToolkit.Maui.SourceGenerators.csproj", "{98B61BE8-4A47-4A56-82C1-40498DF12214}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source Generators", "Source Generators", "{9BFC4026-BC8F-43E2-BAA9-5BC2D764D37D}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Analyzers", "Analyzers", "{9BFC4026-BC8F-43E2-BAA9-5BC2D764D37D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Maui.Analyzers", "..\src\CommunityToolkit.Maui.Analyzers\CommunityToolkit.Maui.Analyzers.csproj", "{BB7A6C2F-B8F1-49FF-8E67-C436EF062A22}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Maui.Analyzers.CodeFixes", "..\src\CommunityToolkit.Maui.Analyzers.CodeFixes\CommunityToolkit.Maui.Analyzers.CodeFixes.csproj", "{20792D38-690A-4760-82FD-94E9510F86C9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{9F7D54C0-EA17-409A-804F-B2E8D7F4A7F3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Maui.Analyzers.UnitTests", "..\src\CommunityToolkit.Maui.Analyzers.UnitTests\CommunityToolkit.Maui.Analyzers.UnitTests.csproj", "{60B976B2-F3FA-494E-A28B-7BED2EAE990E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -54,13 +62,29 @@ Global
{98B61BE8-4A47-4A56-82C1-40498DF12214}.Debug|Any CPU.Build.0 = Debug|Any CPU
{98B61BE8-4A47-4A56-82C1-40498DF12214}.Release|Any CPU.ActiveCfg = Release|Any CPU
{98B61BE8-4A47-4A56-82C1-40498DF12214}.Release|Any CPU.Build.0 = Release|Any CPU
{BB7A6C2F-B8F1-49FF-8E67-C436EF062A22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BB7A6C2F-B8F1-49FF-8E67-C436EF062A22}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BB7A6C2F-B8F1-49FF-8E67-C436EF062A22}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BB7A6C2F-B8F1-49FF-8E67-C436EF062A22}.Release|Any CPU.Build.0 = Release|Any CPU
{20792D38-690A-4760-82FD-94E9510F86C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{20792D38-690A-4760-82FD-94E9510F86C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{20792D38-690A-4760-82FD-94E9510F86C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{20792D38-690A-4760-82FD-94E9510F86C9}.Release|Any CPU.Build.0 = Release|Any CPU
{60B976B2-F3FA-494E-A28B-7BED2EAE990E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{60B976B2-F3FA-494E-A28B-7BED2EAE990E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{60B976B2-F3FA-494E-A28B-7BED2EAE990E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{60B976B2-F3FA-494E-A28B-7BED2EAE990E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{7D49CC16-93CF-471B-B1FA-0BA44DECC15D} = {9F7D54C0-EA17-409A-804F-B2E8D7F4A7F3}
{3A795407-604F-4502-8C76-281BE5575A84} = {AE67B86A-46F2-4078-BF02-0614B7E0E3F5}
{98B61BE8-4A47-4A56-82C1-40498DF12214} = {9BFC4026-BC8F-43E2-BAA9-5BC2D764D37D}
{BB7A6C2F-B8F1-49FF-8E67-C436EF062A22} = {9BFC4026-BC8F-43E2-BAA9-5BC2D764D37D}
{20792D38-690A-4760-82FD-94E9510F86C9} = {9BFC4026-BC8F-43E2-BAA9-5BC2D764D37D}
{60B976B2-F3FA-494E-A28B-7BED2EAE990E} = {9F7D54C0-EA17-409A-804F-B2E8D7F4A7F3}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1E9E61C1-5CB7-4C8E-87BA-6C1D38238679}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
<ItemGroup>
<ProjectReference Include="..\..\src\CommunityToolkit.Maui\CommunityToolkit.Maui.csproj" />
<ProjectReference Include="..\..\src\CommunityToolkit.Maui.SourceGenerators\CommunityToolkit.Maui.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="..\..\src\CommunityToolkit.Maui.Analyzers\CommunityToolkit.Maui.Analyzers.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="..\..\src\CommunityToolkit.Maui.Analyzers.CodeFixes\CommunityToolkit.Maui.Analyzers.CodeFixes.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>

<PropertyGroup Condition="$(TargetFramework.Contains('-android'))">
Expand Down
4 changes: 2 additions & 2 deletions samples/CommunityToolkit.Maui.Sample/MauiProgram.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ public static class MauiProgram
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder()
.UseMauiApp<App>()
.UseMauiCommunityToolkit()
.UseMauiCommunityToolkitMarkup();
.UseMauiCommunityToolkitMarkup()
.UseMauiApp<App>();

builder.Services.AddHttpClient<ByteArrayToImageSourceConverterViewModel>();
builder.Services.AddSingleton<PopupSizeConstants>();
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Initialize .NET MAUI Community Toolkit Before UseMauiApp" xml:space="preserve">
<value>Add `.UseMauiCommunityToolkit()`</value>
<comment>Add `.UseMauiCommunityToolkit()`</comment>
</data>
</root>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>false</IsPackable>
<IsRoslynComponent>true</IsRoslynComponent>
<RootNamespace>CommunityToolkit.Maui.Analyzers</RootNamespace>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.2.0" PrivateAssets="all" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\CommunityToolkit.Maui.Analyzers\CommunityToolkit.Maui.Analyzers.csproj" />
</ItemGroup>

<ItemGroup>
<Compile Update="CodeFixResources.Designer.cs" DesignTime="True" AutoGen="True" DependentUpon="CodeFixResources.resx" />
<EmbeddedResource Update="CodeFixResources.resx" Generator="ResXFileCodeGenerator" LastGenOutput="CodeFixResources.Designer.cs" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"profiles": {
"CommunityToolkit.Maui.Sample": {
"commandName": "DebugRoslynComponent",
"targetProject": "..\\..\\samples\\CommunityToolkit.Maui.Sample\\CommunityToolkit.Maui.Sample.csproj"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using System.Collections.Immutable;
using System.Composition;
using System.Linq.Expressions;
using System.Reflection.Metadata.Ecma335;
using System.Runtime.InteropServices.ComTypes;
using CommunityToolkit.Maui.Analyzers;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Rename;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

namespace CommunityToolkit.Maui.Analyzers;

[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(UseCommunityToolkitInitializationAnalyzerCodeFixProvider)), Shared]
public class UseCommunityToolkitInitializationAnalyzerCodeFixProvider : CodeFixProvider
{
public sealed override ImmutableArray<string> FixableDiagnosticIds { get; } = ImmutableArray.Create(UseCommunityToolkitInitializationAnalyzer.DiagnosticId);

public sealed override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;

public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);

var diagnostic = context.Diagnostics.First();
var diagnosticSpan = diagnostic.Location.SourceSpan;

// Find the type declaration identified by the diagnostic.
var declaration = root?.FindToken(diagnosticSpan.Start).Parent?.AncestorsAndSelf().OfType<InvocationExpressionSyntax>().Last() ?? throw new InvalidOperationException();

// Register a code action that will invoke the fix.
context.RegisterCodeFix(
CodeAction.Create(
title: CodeFixResources.Initialize__NET_MAUI_Community_Toolkit_Before_UseMauiApp,
createChangedDocument: c => AddUseCommunityToolkit(context.Document, declaration, c),
equivalenceKey: nameof(CodeFixResources.Initialize__NET_MAUI_Community_Toolkit_Before_UseMauiApp)),
diagnostic);
}

async Task<Document> AddUseCommunityToolkit(Document document, InvocationExpressionSyntax invocationExpression, CancellationToken cancellationToken)
{
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
if (root is null)
{
return document;
}

var updatedInvocationExpression =
InvocationExpression(
MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression, invocationExpression, IdentifierName("UseMauiCommunityToolkit")));

var mauiCommunityToolkitUsingStatement =
UsingDirective(
QualifiedName(
IdentifierName("CommunityToolkit"),
IdentifierName("Maui")));

var newRoot = root.ReplaceNode(invocationExpression, updatedInvocationExpression);

if (newRoot is CompilationUnitSyntax compilationSyntax)
{
newRoot = compilationSyntax.AddUsings(mauiCommunityToolkitUsingStatement).NormalizeWhitespace();
}

var newDocument = document.WithSyntaxRoot(newRoot);

return newDocument;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable>
<UseMaui>true</UseMaui>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GF</CompilerGeneratedFilesOutputPath>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.7.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" Version="1.1.1" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5" PrivateAssets="All" />
<PackageReference Include="coverlet.collector" Version="3.1.2" PrivateAssets="All" />
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.2.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\CommunityToolkit.Maui.Analyzers.CodeFixes\CommunityToolkit.Maui.Analyzers.CodeFixes.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;

namespace CommunityToolkit.Maui.Analyzers.UnitTests;

public class UseCommunityToolkitInitializationAnalyzerTests
{
[Fact]
public void UseCommunityToolkitInitializationAnalyzerId()
{
Assert.Equal("MCT001", UseCommunityToolkitInitializationAnalyzer.DiagnosticId);
}
}
Loading

0 comments on commit b3b2b9a

Please sign in to comment.