Browse Source

Split analyzers and code fixes into separate projects (#20312)

* Split analyzers and code fixes

* Split C# and VB analyzers

* Merge all analyzer packages

* Add Analyzers.VisualBasic to .slnf
pull/20428/head
Julien Lebosquain 1 month ago
committed by GitHub
parent
commit
4b071000a4
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 4
      Avalonia.Desktop.slnf
  2. 16
      Avalonia.sln
  3. 3
      build/AnalyzerProject.targets
  4. 12
      build/DevAnalyzers.props
  5. 2
      nukebuild/Build.cs
  6. 12
      nukebuild/numerge.json
  7. 12
      packages/Avalonia/Avalonia.csproj
  8. 9
      src/tools/Avalonia.Analyzers.CSharp/Avalonia.Analyzers.CSharp.csproj
  9. 16
      src/tools/Avalonia.Analyzers.CSharp/AvaloniaAnalysisException.cs
  10. 37
      src/tools/Avalonia.Analyzers.CSharp/AvaloniaPropertyAnalyzer.CSharp.cs
  11. 15
      src/tools/Avalonia.Analyzers.CSharp/AvaloniaPropertyAnalyzer.CompileAnalyzer.cs
  12. 43
      src/tools/Avalonia.Analyzers.CSharp/AvaloniaPropertyAnalyzer.cs
  13. 3
      src/tools/Avalonia.Analyzers.CSharp/BitmapAnalyzer.cs
  14. 7
      src/tools/Avalonia.Analyzers.CSharp/DiagnosticIds.cs
  15. 4
      src/tools/Avalonia.Analyzers.CSharp/OnPropertyChangedOverrideAnalyzer.cs
  16. 28
      src/tools/Avalonia.Analyzers.CodeFixes.CSharp/Avalonia.Analyzers.CodeFixes.CSharp.csproj
  17. 8
      src/tools/Avalonia.Analyzers.CodeFixes.CSharp/BitmapAnalyzerCodeFixProvider.cs
  18. 30
      src/tools/Avalonia.Analyzers.VisualBasic/Avalonia.Analyzers.VisualBasic.csproj
  19. 24
      src/tools/Avalonia.Analyzers.VisualBasic/AvaloniaPropertyAnalyzer.VisualBasic.cs
  20. 8
      src/tools/Avalonia.Analyzers/GlobalSuppressions.cs
  21. 4
      src/tools/Avalonia.Generators/Avalonia.Generators.csproj
  22. 5
      src/tools/DevAnalyzers/DevAnalyzers.csproj
  23. 4
      src/tools/DevGenerators/DevGenerators.csproj

4
Avalonia.Desktop.slnf

@ -42,7 +42,9 @@
"src\\Markup\\Avalonia.Markup.Xaml\\Avalonia.Markup.Xaml.csproj", "src\\Markup\\Avalonia.Markup.Xaml\\Avalonia.Markup.Xaml.csproj",
"src\\Markup\\Avalonia.Markup\\Avalonia.Markup.csproj", "src\\Markup\\Avalonia.Markup\\Avalonia.Markup.csproj",
"src\\Skia\\Avalonia.Skia\\Avalonia.Skia.csproj", "src\\Skia\\Avalonia.Skia\\Avalonia.Skia.csproj",
"src\\tools\\Avalonia.Analyzers\\Avalonia.Analyzers.csproj", "src\\tools\\Avalonia.Analyzers.CodeFixes.CSharp\\Avalonia.Analyzers.CodeFixes.CSharp.csproj",
"src\\tools\\Avalonia.Analyzers.CSharp\\Avalonia.Analyzers.CSharp.csproj",
"src\\tools\\Avalonia.Analyzers.VisualBasic\\Avalonia.Analyzers.VisualBasic.csproj",
"src\\tools\\Avalonia.Generators\\Avalonia.Generators.csproj", "src\\tools\\Avalonia.Generators\\Avalonia.Generators.csproj",
"src\\tools\\DevAnalyzers\\DevAnalyzers.csproj", "src\\tools\\DevAnalyzers\\DevAnalyzers.csproj",
"src\\tools\\DevGenerators\\DevGenerators.csproj", "src\\tools\\DevGenerators\\DevGenerators.csproj",

16
Avalonia.sln

@ -204,7 +204,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Browser", "s
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GpuInterop", "samples\GpuInterop\GpuInterop.csproj", "{C810060E-3809-4B74-A125-F11533AF9C1B}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GpuInterop", "samples\GpuInterop\GpuInterop.csproj", "{C810060E-3809-4B74-A125-F11533AF9C1B}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Analyzers", "src\tools\Avalonia.Analyzers\Avalonia.Analyzers.csproj", "{C692FE73-43DB-49CE-87FC-F03ED61F25C9}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Analyzers.CSharp", "src\tools\Avalonia.Analyzers.CSharp\Avalonia.Analyzers.CSharp.csproj", "{C692FE73-43DB-49CE-87FC-F03ED61F25C9}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{176582E8-46AF-416A-85C1-13A5C6744497}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{176582E8-46AF-416A-85C1-13A5C6744497}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
@ -285,6 +285,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.XUnit.Per
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.IntegrationTests.Win32", "tests\Avalonia.IntegrationTests.Win32\Avalonia.IntegrationTests.Win32.csproj", "{11522B0D-BF31-42D5-8FC5-41E58F319AF9}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.IntegrationTests.Win32", "tests\Avalonia.IntegrationTests.Win32\Avalonia.IntegrationTests.Win32.csproj", "{11522B0D-BF31-42D5-8FC5-41E58F319AF9}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Analyzers.CodeFixes.CSharp", "src\tools\Avalonia.Analyzers.CodeFixes.CSharp\Avalonia.Analyzers.CodeFixes.CSharp.csproj", "{FDFB9C25-552D-420B-9D4A-DB0BB6472239}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Analyzers.VisualBasic", "src\tools\Avalonia.Analyzers.VisualBasic\Avalonia.Analyzers.VisualBasic.csproj", "{A7644C3B-B843-44F1-9940-560D56CB0936}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -659,6 +663,14 @@ Global
{11522B0D-BF31-42D5-8FC5-41E58F319AF9}.Debug|Any CPU.Build.0 = Debug|Any CPU {11522B0D-BF31-42D5-8FC5-41E58F319AF9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{11522B0D-BF31-42D5-8FC5-41E58F319AF9}.Release|Any CPU.ActiveCfg = Release|Any CPU {11522B0D-BF31-42D5-8FC5-41E58F319AF9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{11522B0D-BF31-42D5-8FC5-41E58F319AF9}.Release|Any CPU.Build.0 = Release|Any CPU {11522B0D-BF31-42D5-8FC5-41E58F319AF9}.Release|Any CPU.Build.0 = Release|Any CPU
{A7644C3B-B843-44F1-9940-560D56CB0936}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A7644C3B-B843-44F1-9940-560D56CB0936}.Release|Any CPU.Build.0 = Release|Any CPU
{A7644C3B-B843-44F1-9940-560D56CB0936}.Debug|Any CPU.ActiveCfg = Release|Any CPU
{A7644C3B-B843-44F1-9940-560D56CB0936}.Debug|Any CPU.Build.0 = Release|Any CPU
{FDFB9C25-552D-420B-9D4A-DB0BB6472239}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FDFB9C25-552D-420B-9D4A-DB0BB6472239}.Release|Any CPU.Build.0 = Release|Any CPU
{FDFB9C25-552D-420B-9D4A-DB0BB6472239}.Debug|Any CPU.ActiveCfg = Release|Any CPU
{FDFB9C25-552D-420B-9D4A-DB0BB6472239}.Debug|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -743,6 +755,8 @@ Global
{342D2657-2F84-493C-B74B-9D2CAE5D9DAB} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {342D2657-2F84-493C-B74B-9D2CAE5D9DAB} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{26918642-829D-4FA2-B60A-BE8D83F4E063} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {26918642-829D-4FA2-B60A-BE8D83F4E063} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{11522B0D-BF31-42D5-8FC5-41E58F319AF9} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {11522B0D-BF31-42D5-8FC5-41E58F319AF9} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{A7644C3B-B843-44F1-9940-560D56CB0936} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
{FDFB9C25-552D-420B-9D4A-DB0BB6472239} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A} SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}

3
build/AnalyzerProject.targets

@ -3,12 +3,11 @@
<PropertyGroup> <PropertyGroup>
<EnforceExtendedAnalyzerRules Condition="'$(EnforceExtendedAnalyzerRules)' == ''">true</EnforceExtendedAnalyzerRules> <EnforceExtendedAnalyzerRules Condition="'$(EnforceExtendedAnalyzerRules)' == ''">true</EnforceExtendedAnalyzerRules>
<IsRoslynComponent Condition="'$(IsRoslynComponent)' == ''">true</IsRoslynComponent> <IsRoslynComponent Condition="'$(IsRoslynComponent)' == ''">true</IsRoslynComponent>
<NoWarn>$(NoWarn);RS2008</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" PrivateAssets="all"/> <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" PrivateAssets="all"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.5.0" PrivateAssets="all" />
</ItemGroup> </ItemGroup>
</Project> </Project>

12
build/DevAnalyzers.props

@ -1,12 +1,20 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<ProjectReference Include="$(MSBuildThisFileDirectory)..\src\tools\DevAnalyzers\DevAnalyzers.csproj" <ProjectReference Include="$(MSBuildThisFileDirectory)../src/tools/DevAnalyzers/DevAnalyzers.csproj"
PrivateAssets="all" PrivateAssets="all"
ReferenceOutputAssembly="false" ReferenceOutputAssembly="false"
OutputItemType="Analyzer" /> OutputItemType="Analyzer" />
<ProjectReference Include="$(MSBuildThisFileDirectory)..\src\tools\Avalonia.Analyzers\Avalonia.Analyzers.csproj" <ProjectReference Include="$(MSBuildThisFileDirectory)../src/tools/Avalonia.Analyzers.CSharp/Avalonia.Analyzers.CSharp.csproj"
PrivateAssets="all" PrivateAssets="all"
ReferenceOutputAssembly="false" ReferenceOutputAssembly="false"
OutputItemType="Analyzer" /> OutputItemType="Analyzer" />
<ProjectReference Include="$(MSBuildThisFileDirectory)../src/tools/Avalonia.Analyzers.CodeFixes.CSharp/Avalonia.Analyzers.CodeFixes.CSharp.csproj"
PrivateAssets="all"
ReferenceOutputAssembly="false"
OutputItemType="Analyzer" />
<ProjectReference Include="$(MSBuildThisFileDirectory)../src/tools/Avalonia.Analyzers.VisualBasic/Avalonia.Analyzers.VisualBasic.csproj"
ReferenceOutputAssembly="false"
PrivateAssets="all"
OutputItemType="Analyzer" />
</ItemGroup> </ItemGroup>
</Project> </Project>

2
nukebuild/Build.cs

@ -322,7 +322,7 @@ partial class Build : NukeBuild
BuildTasksPatcher.PatchBuildTasksInPackage(Parameters.NugetIntermediateRoot / "Avalonia.Build.Tasks." + BuildTasksPatcher.PatchBuildTasksInPackage(Parameters.NugetIntermediateRoot / "Avalonia.Build.Tasks." +
Parameters.Version + ".nupkg", Parameters.Version + ".nupkg",
IlRepackTool); IlRepackTool);
var config = Numerge.MergeConfiguration.LoadFile(RootDirectory / "nukebuild" / "numerge.config"); var config = Numerge.MergeConfiguration.LoadFile(RootDirectory / "nukebuild" / "numerge.json");
Parameters.NugetRoot.CreateOrCleanDirectory(); Parameters.NugetRoot.CreateOrCleanDirectory();
if(!Numerge.NugetPackageMerger.Merge(Parameters.NugetIntermediateRoot, Parameters.NugetRoot, config, if(!Numerge.NugetPackageMerger.Merge(Parameters.NugetIntermediateRoot, Parameters.NugetRoot, config,
new NumergeNukeLogger())) new NumergeNukeLogger()))

12
nukebuild/numerge.config → nukebuild/numerge.json

@ -18,7 +18,17 @@
"DoNotMergeDependencies": true "DoNotMergeDependencies": true
}, },
{ {
"Id": "Avalonia.Analyzers", "Id": "Avalonia.Analyzers.CSharp",
"IgnoreMissingFrameworkBinaries": true,
"DoNotMergeDependencies": true
},
{
"Id": "Avalonia.Analyzers.VisualBasic",
"IgnoreMissingFrameworkBinaries": true,
"DoNotMergeDependencies": true
},
{
"Id": "Avalonia.Analyzers.CodeFixes.CSharp",
"IgnoreMissingFrameworkBinaries": true, "IgnoreMissingFrameworkBinaries": true,
"DoNotMergeDependencies": true "DoNotMergeDependencies": true
} }

12
packages/Avalonia/Avalonia.csproj

@ -9,11 +9,19 @@
<ProjectReference Include="../../src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj" /> <ProjectReference Include="../../src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj" />
<ProjectReference Include="../../src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj" <ProjectReference Include="../../src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj"
PrivateAssets="all" /> PrivateAssets="all" />
<ProjectReference Include="..\..\src\tools\Avalonia.Generators\Avalonia.Generators.csproj" <ProjectReference Include="../../src/tools/Avalonia.Generators/Avalonia.Generators.csproj"
ReferenceOutputAssembly="false" ReferenceOutputAssembly="false"
PrivateAssets="all" PrivateAssets="all"
OutputItemType="Analyzer" /> OutputItemType="Analyzer" />
<ProjectReference Include="..\..\src\tools\Avalonia.Analyzers\Avalonia.Analyzers.csproj" <ProjectReference Include="../../src/tools/Avalonia.Analyzers.CSharp/Avalonia.Analyzers.CSharp.csproj"
ReferenceOutputAssembly="false"
PrivateAssets="all"
OutputItemType="Analyzer" />
<ProjectReference Include="../../src/tools/Avalonia.Analyzers.CodeFixes.CSharp/Avalonia.Analyzers.CodeFixes.CSharp.csproj"
ReferenceOutputAssembly="false"
PrivateAssets="all"
OutputItemType="Analyzer" />
<ProjectReference Include="../../src/tools/Avalonia.Analyzers.VisualBasic/Avalonia.Analyzers.VisualBasic.csproj"
ReferenceOutputAssembly="false" ReferenceOutputAssembly="false"
PrivateAssets="all" PrivateAssets="all"
OutputItemType="Analyzer" /> OutputItemType="Analyzer" />

9
src/tools/Avalonia.Analyzers/Avalonia.Analyzers.csproj → src/tools/Avalonia.Analyzers.CSharp/Avalonia.Analyzers.CSharp.csproj

@ -1,17 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<IncludeBuildOutput>false</IncludeBuildOutput> <IncludeBuildOutput>false</IncludeBuildOutput>
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
<IsPackable>true</IsPackable> <IsPackable>true</IsPackable>
<IncludeSymbols>false</IncludeSymbols> <IncludeSymbols>false</IncludeSymbols>
<RootNamespace>Avalonia.Analyzers</RootNamespace>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" /> <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<None Include="$(OutputPath)/$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
</ItemGroup> </ItemGroup>
<Import Project="../../../build/TrimmingEnable.props" /> <Import Project="../../../build/TrimmingEnable.props" />
<Import Project="../../../build/NullableEnable.props" /> <Import Project="../../../build/NullableEnable.props" />
<Import Project="../../../build/AnalyzerProject.targets" /> <Import Project="../../../build/AnalyzerProject.targets" />
</Project> </Project>

16
src/tools/Avalonia.Analyzers.CSharp/AvaloniaAnalysisException.cs

@ -0,0 +1,16 @@
using System;
using System.Runtime.Serialization;
namespace Avalonia.Analyzers;
[Serializable]
public class AvaloniaAnalysisException : Exception
{
public AvaloniaAnalysisException(string message, Exception? innerException = null) : base(message, innerException)
{
}
protected AvaloniaAnalysisException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}

37
src/tools/Avalonia.Analyzers.CSharp/AvaloniaPropertyAnalyzer.CSharp.cs

@ -0,0 +1,37 @@
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
namespace Avalonia.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public partial class AvaloniaPropertyAnalyzer
{
private static partial TypeReference TypeReferenceFromInvocationTypeParameter(IInvocationOperation invocation, ITypeParameterSymbol typeParameter)
{
var argument = invocation.TargetMethod.TypeArguments[typeParameter.Ordinal];
var typeArgumentSyntax = invocation.Syntax;
// type arguments do not appear in the invocation, so search the code for them
try
{
typeArgumentSyntax = invocation.Syntax.DescendantNodes()
.First(n => n.IsKind(SyntaxKind.TypeArgumentList))
.DescendantNodes().ElementAt(typeParameter.Ordinal);
}
catch
{
// ignore, this is just a nicety
}
return new TypeReference(argument, typeArgumentSyntax.GetLocation());
}
private static partial bool IsSimpleAssignmentNode(SyntaxNode node)
=> node.IsKind(SyntaxKind.SimpleAssignmentExpression);
private static partial bool IsInvocationNode(SyntaxNode node)
=> node.IsKind(SyntaxKind.InvocationExpression);
}

15
src/tools/Avalonia.Analyzers/AvaloniaPropertyAnalyzer.CompileAnalyzer.cs → src/tools/Avalonia.Analyzers.CSharp/AvaloniaPropertyAnalyzer.CompileAnalyzer.cs

@ -7,7 +7,6 @@ using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Operations;
@ -168,7 +167,7 @@ public partial class AvaloniaPropertyAnalyzer
{ {
var (node, model) = GetNodeAndModel(syntaxRef); var (node, model) = GetNodeAndModel(syntaxRef);
foreach (var descendant in node.DescendantNodes().Where(n => n.IsKind(SyntaxKind.SimpleAssignmentExpression))) foreach (var descendant in node.DescendantNodes().Where(IsSimpleAssignmentNode))
{ {
if (model.GetOperation(descendant, cancellationToken) is IAssignmentOperation assignmentOperation && if (model.GetOperation(descendant, cancellationToken) is IAssignmentOperation assignmentOperation &&
@ -309,7 +308,7 @@ public partial class AvaloniaPropertyAnalyzer
if (_ownerTypeParams.TryGetValue(originalMethod, out var ownerTypeParam)) if (_ownerTypeParams.TryGetValue(originalMethod, out var ownerTypeParam))
{ {
ownerTypeRef = TypeReference.FromInvocationTypeParameter(invocation, ownerTypeParam); ownerTypeRef = TypeReferenceFromInvocationTypeParameter(invocation, ownerTypeParam);
} }
else if (_ownerParams.TryGetValue(originalMethod, out var ownerParam) && // try extracting the runtime argument else if (_ownerParams.TryGetValue(originalMethod, out var ownerParam) && // try extracting the runtime argument
ResolveOperationSource(invocation.Arguments[ownerParam.Ordinal].Value, cancellationToken) is ITypeOfOperation { Type: ITypeSymbol type } typeOf) ResolveOperationSource(invocation.Arguments[ownerParam.Ordinal].Value, cancellationToken) is ITypeOfOperation { Type: ITypeSymbol type } typeOf)
@ -324,7 +323,7 @@ public partial class AvaloniaPropertyAnalyzer
TypeReference valueTypeRef; TypeReference valueTypeRef;
if (_valueTypeParams.TryGetValue(originalMethod, out var valueTypeParam)) if (_valueTypeParams.TryGetValue(originalMethod, out var valueTypeParam))
{ {
valueTypeRef = TypeReference.FromInvocationTypeParameter(invocation, valueTypeParam); valueTypeRef = TypeReferenceFromInvocationTypeParameter(invocation, valueTypeParam);
} }
else else
{ {
@ -360,7 +359,7 @@ public partial class AvaloniaPropertyAnalyzer
{ {
if (_hostTypeParams.TryGetValue(originalMethod, out var hostTypeParam)) if (_hostTypeParams.TryGetValue(originalMethod, out var hostTypeParam))
{ {
hostTypeRef = TypeReference.FromInvocationTypeParameter(invocation, hostTypeParam); hostTypeRef = TypeReferenceFromInvocationTypeParameter(invocation, hostTypeParam);
} }
else else
{ {
@ -418,7 +417,7 @@ public partial class AvaloniaPropertyAnalyzer
return result; return result;
}); });
var ownerTypeRef = TypeReference.FromInvocationTypeParameter(invocation, ownerTypeParam); var ownerTypeRef = TypeReferenceFromInvocationTypeParameter(invocation, ownerTypeParam);
description.SetAssignment(target, ownerTypeRef); description.SetAssignment(target, ownerTypeRef);
description.AddOwner(ownerTypeRef); description.AddOwner(ownerTypeRef);
} }
@ -611,7 +610,7 @@ public partial class AvaloniaPropertyAnalyzer
{ {
if (newOwnerType is INamedTypeSymbol { IsGenericType: true }) if (newOwnerType is INamedTypeSymbol { IsGenericType: true })
{ {
context.ReportDiagnostic(Diagnostic.Create(PropertyOwnedByGenericType, TypeReference.FromInvocationTypeParameter(invocation, typeParam).Location)); context.ReportDiagnostic(Diagnostic.Create(PropertyOwnedByGenericType, TypeReferenceFromInvocationTypeParameter(invocation, typeParam).Location));
} }
if (_avaloniaPropertyAddOwnerMethods.Contains(originalMethod) && GetReferencedProperty(invocation.Instance!, context.CancellationToken) is { } refProp) if (_avaloniaPropertyAddOwnerMethods.Contains(originalMethod) && GetReferencedProperty(invocation.Instance!, context.CancellationToken) is { } refProp)
@ -765,7 +764,7 @@ public partial class AvaloniaPropertyAnalyzer
var bodyNode = context.CodeBlock.ChildNodes().Single(); var bodyNode = context.CodeBlock.ChildNodes().Single();
var operation = bodyNode.DescendantNodes() var operation = bodyNode.DescendantNodes()
.Where(n => n.IsKind(SyntaxKind.InvocationExpression)) // this line is specific to C# .Where(IsInvocationNode)
.Select(n => (IInvocationOperation)context.SemanticModel.GetOperation(n)!) .Select(n => (IInvocationOperation)context.SemanticModel.GetOperation(n)!)
.FirstOrDefault(); .FirstOrDefault();

43
src/tools/Avalonia.Analyzers/AvaloniaPropertyAnalyzer.cs → src/tools/Avalonia.Analyzers.CSharp/AvaloniaPropertyAnalyzer.cs

@ -5,16 +5,13 @@ using System.Collections.Immutable;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Runtime.Serialization;
using System.Threading; using System.Threading;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Operations;
namespace Avalonia.Analyzers; namespace Avalonia.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
public partial class AvaloniaPropertyAnalyzer : DiagnosticAnalyzer public partial class AvaloniaPropertyAnalyzer : DiagnosticAnalyzer
{ {
private const string Category = "AvaloniaProperty"; private const string Category = "AvaloniaProperty";
@ -333,6 +330,12 @@ public partial class AvaloniaPropertyAnalyzer : DiagnosticAnalyzer
return SymbolEqualityComparer.Default.Equals(x, y); return SymbolEqualityComparer.Default.Equals(x, y);
} }
private static partial TypeReference TypeReferenceFromInvocationTypeParameter(IInvocationOperation invocation, ITypeParameterSymbol typeParameter);
private static partial bool IsSimpleAssignmentNode(SyntaxNode node);
private static partial bool IsInvocationNode(SyntaxNode node);
private class AvaloniaPropertyDescription private class AvaloniaPropertyDescription
{ {
/// <summary> /// <summary>
@ -450,28 +453,6 @@ public partial class AvaloniaPropertyAnalyzer : DiagnosticAnalyzer
Type = type; Type = type;
Location = location; Location = location;
} }
public static TypeReference FromInvocationTypeParameter(IInvocationOperation invocation, ITypeParameterSymbol typeParameter)
{
var argument = invocation.TargetMethod.TypeArguments[typeParameter.Ordinal];
var typeArgumentSyntax = invocation.Syntax;
if (invocation.Language == LanguageNames.CSharp) // type arguments do not appear in the invocation, so search the code for them
{
try
{
typeArgumentSyntax = invocation.Syntax.DescendantNodes()
.First(n => n.IsKind(SyntaxKind.TypeArgumentList))
.DescendantNodes().ElementAt(typeParameter.Ordinal);
}
catch
{
// ignore, this is just a nicety
}
}
return new TypeReference(argument, typeArgumentSyntax.GetLocation());
}
} }
private class SymbolEqualityComparer<T> : IEqualityComparer<T> where T : ISymbol private class SymbolEqualityComparer<T> : IEqualityComparer<T> where T : ISymbol
@ -482,15 +463,3 @@ public partial class AvaloniaPropertyAnalyzer : DiagnosticAnalyzer
public static SymbolEqualityComparer<T> Default { get; } = new(); public static SymbolEqualityComparer<T> Default { get; } = new();
} }
} }
[Serializable]
public class AvaloniaAnalysisException : Exception
{
public AvaloniaAnalysisException(string message, Exception? innerException = null) : base(message, innerException)
{
}
protected AvaloniaAnalysisException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}

3
src/tools/Avalonia.Analyzers/BitmapAnalyzer.cs → src/tools/Avalonia.Analyzers.CSharp/BitmapAnalyzer.cs

@ -15,14 +15,13 @@ namespace Avalonia.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class BitmapAnalyzer: DiagnosticAnalyzer public class BitmapAnalyzer: DiagnosticAnalyzer
{ {
public const string DiagnosticId = "AVA2002";
private const string Title = "Cannot initialize Bitmap from \"avares\" scheme"; private const string Title = "Cannot initialize Bitmap from \"avares\" scheme";
private const string MessageFormat = "Cannot initialize Bitmap from \"avares\" scheme directly"; private const string MessageFormat = "Cannot initialize Bitmap from \"avares\" scheme directly";
private const string Description = "Cannot initialize Bitmap from \"avares\" scheme, use AssetLoader to open assets as stream first."; private const string Description = "Cannot initialize Bitmap from \"avares\" scheme, use AssetLoader to open assets as stream first.";
private const string Category = "Usage"; private const string Category = "Usage";
private static readonly DiagnosticDescriptor _rule = new( private static readonly DiagnosticDescriptor _rule = new(
DiagnosticId, DiagnosticIds.Bitmap,
Title, Title,
MessageFormat, MessageFormat,
Category, Category,

7
src/tools/Avalonia.Analyzers.CSharp/DiagnosticIds.cs

@ -0,0 +1,7 @@
namespace Avalonia.Analyzers;
public static class DiagnosticIds
{
public const string OnPropertyChangedOverride = "AVA2001";
public const string Bitmap = "AVA2002";
}

4
src/tools/Avalonia.Analyzers/OnPropertyChangedOverrideAnalyzer.cs → src/tools/Avalonia.Analyzers.CSharp/OnPropertyChangedOverrideAnalyzer.cs

@ -10,10 +10,8 @@ namespace Avalonia.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class OnPropertyChangedOverrideAnalyzer : DiagnosticAnalyzer public class OnPropertyChangedOverrideAnalyzer : DiagnosticAnalyzer
{ {
public const string DiagnosticId = "AVA2001";
private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(
DiagnosticId, DiagnosticIds.OnPropertyChangedOverride,
"Missing invoke base.OnPropertyChanged", "Missing invoke base.OnPropertyChanged",
"Method '{0}' do not invoke base.{0}", "Method '{0}' do not invoke base.{0}",
"Potential issue", "Potential issue",

28
src/tools/Avalonia.Analyzers.CodeFixes.CSharp/Avalonia.Analyzers.CodeFixes.CSharp.csproj

@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<IncludeBuildOutput>false</IncludeBuildOutput>
<DebugType>embedded</DebugType>
<IsPackable>true</IsPackable>
<IncludeSymbols>false</IncludeSymbols>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.5.0" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<None Include="$(OutputPath)/$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
</ItemGroup>
<ItemGroup>
<Compile Include="../Avalonia.Analyzers.CSharp/DiagnosticIds.cs" />
</ItemGroup>
<Import Project="../../../build/TrimmingEnable.props" />
<Import Project="../../../build/NullableEnable.props" />
<Import Project="../../../build/AnalyzerProject.targets" />
</Project>

8
src/tools/Avalonia.Analyzers/BitmapAnalyzerCSCodeFixProvider.cs → src/tools/Avalonia.Analyzers.CodeFixes.CSharp/BitmapAnalyzerCodeFixProvider.cs

@ -10,21 +10,21 @@ using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Avalonia.Analyzers; namespace Avalonia.Analyzers.CodeFixes.CSharp;
/// <summary> /// <summary>
/// Provides a code fix for the BitmapAnalyzer diagnostic, which replaces "avares://" string arguments /// Provides a code fix for the BitmapAnalyzer diagnostic, which replaces "avares://" string arguments
/// with a call to AssetLoader.Open(new Uri("avares://...")). /// with a call to AssetLoader.Open(new Uri("avares://...")).
/// </summary> /// </summary>
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(BitmapAnalyzerCSCodeFixProvider))] [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(BitmapAnalyzerCodeFixProvider))]
[Shared] [Shared]
public class BitmapAnalyzerCSCodeFixProvider : CodeFixProvider public class BitmapAnalyzerCodeFixProvider : CodeFixProvider
{ {
private const string _title = "Use AssetLoader to open assets as stream first"; private const string _title = "Use AssetLoader to open assets as stream first";
/// <inheritdoc /> /// <inheritdoc />
public override ImmutableArray<string> FixableDiagnosticIds { get; } = public override ImmutableArray<string> FixableDiagnosticIds { get; } =
ImmutableArray.Create(BitmapAnalyzer.DiagnosticId); ImmutableArray.Create(DiagnosticIds.Bitmap);
/// <inheritdoc /> /// <inheritdoc />
public override FixAllProvider? GetFixAllProvider() public override FixAllProvider? GetFixAllProvider()

30
src/tools/Avalonia.Analyzers.VisualBasic/Avalonia.Analyzers.VisualBasic.csproj

@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<IncludeBuildOutput>false</IncludeBuildOutput>
<DebugType>embedded</DebugType>
<IsPackable>true</IsPackable>
<IncludeSymbols>false</IncludeSymbols>
<RootNamespace>Avalonia.Analyzers</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.VisualBasic" Version="4.5.0" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<None Include="$(OutputPath)/$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
</ItemGroup>
<ItemGroup>
<Compile Include="../Avalonia.Analyzers.CSharp/AvaloniaAnalysisException.cs" />
<Compile Include="../Avalonia.Analyzers.CSharp/AvaloniaPropertyAnalyzer.CompileAnalyzer.cs" />
<Compile Include="../Avalonia.Analyzers.CSharp/AvaloniaPropertyAnalyzer.cs" />
</ItemGroup>
<Import Project="../../../build/TrimmingEnable.props" />
<Import Project="../../../build/NullableEnable.props" />
<Import Project="../../../build/AnalyzerProject.targets" />
</Project>

24
src/tools/Avalonia.Analyzers.VisualBasic/AvaloniaPropertyAnalyzer.VisualBasic.cs

@ -0,0 +1,24 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.VisualBasic;
namespace Avalonia.Analyzers;
[DiagnosticAnalyzer(LanguageNames.VisualBasic)]
public partial class AvaloniaPropertyAnalyzer
{
private static partial TypeReference TypeReferenceFromInvocationTypeParameter(IInvocationOperation invocation, ITypeParameterSymbol typeParameter)
{
var argument = invocation.TargetMethod.TypeArguments[typeParameter.Ordinal];
var typeArgumentSyntax = invocation.Syntax;
return new TypeReference(argument, typeArgumentSyntax.GetLocation());
}
private static partial bool IsSimpleAssignmentNode(SyntaxNode node)
=> node.IsKind(SyntaxKind.SimpleAssignmentStatement);
private static partial bool IsInvocationNode(SyntaxNode node)
=> node.IsKind(SyntaxKind.InvocationExpression);
}

8
src/tools/Avalonia.Analyzers/GlobalSuppressions.cs

@ -1,8 +0,0 @@
// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.
using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("MicrosoftCodeAnalysisReleaseTracking", "RS2008:Enable analyzer release tracking")]

4
src/tools/Avalonia.Generators/Avalonia.Generators.csproj

@ -10,6 +10,10 @@
<XamlXSourcePath>../../../external/XamlX/src/XamlX</XamlXSourcePath> <XamlXSourcePath>../../../external/XamlX/src/XamlX</XamlXSourcePath>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" PrivateAssets="all" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="$(XamlXSourcePath)/**/*.cs" <Compile Include="$(XamlXSourcePath)/**/*.cs"
Exclude="$(XamlXSourcePath)/obj/**/*.cs;$(XamlXSourcePath)/IL/SreTypeSystem.cs" Exclude="$(XamlXSourcePath)/obj/**/*.cs;$(XamlXSourcePath)/IL/SreTypeSystem.cs"

5
src/tools/DevAnalyzers/DevAnalyzers.csproj

@ -5,5 +5,10 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" PrivateAssets="all" />
</ItemGroup>
<Import Project="../../../build/AnalyzerProject.targets" /> <Import Project="../../../build/AnalyzerProject.targets" />
</Project> </Project>

4
src/tools/DevGenerators/DevGenerators.csproj

@ -6,6 +6,10 @@
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" PrivateAssets="all" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\..\Shared\SourceGeneratorAttributes.cs" /> <Compile Include="..\..\Shared\SourceGeneratorAttributes.cs" />
<Compile Include="..\..\Shared\IsExternalInit.cs" Link="IsExternalInit.cs" /> <Compile Include="..\..\Shared\IsExternalInit.cs" Link="IsExternalInit.cs" />

Loading…
Cancel
Save