Skip to content

Commit

Permalink
Implement the analyzer for smart enums.
Browse files Browse the repository at this point in the history
  • Loading branch information
mandel-macaque committed Oct 18, 2024
1 parent 0a66ce0 commit fa0c0ee
Show file tree
Hide file tree
Showing 33 changed files with 773 additions and 117 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<Compile Include="..\..\..\tools\common\ApplePlatform.cs">
<Link>external\ApplePlatform.cs</Link>
</Compile>
<Compile Include="..\..\..\tools\common\SdkVersions.cs">
<Link>external\SdkVersions.cs</Link>
</Compile>
<Compile Include="..\..\..\tools\common\TargetFramework.cs">
<Link>external\TargetFramework.cs</Link>
</Compile>
<Compile Include="../../../tools/common/Frameworks.cs" >
<Link>external\Frameworks.cs</Link>
</Compile>
<Compile Include="..\..\bgen\PlatformName.cs" >
<Link>external\PlatformName.cs</Link>
</Compile>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ namespace Microsoft.Macios.Bindings.Analyzer;
public class BindingTypeCodeFixProvider : CodeFixProvider {
// Specify the diagnostic IDs of analyzers that are expected to be linked.
public sealed override ImmutableArray<string> FixableDiagnosticIds { get; } =
ImmutableArray.Create (BindingTypeSemanticAnalyzer.DiagnosticId);
ImmutableArray.Create (BindingTypeSemanticAnalyzer.RBI0001.Id);

// If you don't need the 'fix all' behaviour, return null.
public override FixAllProvider? GetFixAllProvider () => null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.Macios.Bindings.Analyzer.Extensions;

namespace Microsoft.Macios.Bindings.Analyzer;

Expand All @@ -12,23 +13,20 @@ namespace Microsoft.Macios.Bindings.Analyzer;
/// pattern.
/// </summary>
[DiagnosticAnalyzer (LanguageNames.CSharp)]
public class BindingTypeSemanticAnalyzer : DiagnosticAnalyzer {
internal const string DiagnosticId = "RBI0001";
static readonly LocalizableString Title = new LocalizableResourceString (nameof (Resources.RBI0001Title),
Resources.ResourceManager, typeof (Resources));
static readonly LocalizableString MessageFormat =
new LocalizableResourceString (nameof (Resources.RBI0001MessageFormat), Resources.ResourceManager,
typeof (Resources));
static readonly LocalizableString Description =
new LocalizableResourceString (nameof (Resources.RBI0001Description), Resources.ResourceManager,
typeof (Resources));
const string Category = "Usage";
public class BindingTypeSemanticAnalyzer : DiagnosticAnalyzer, IBindingTypeAnalyzer<ClassDeclarationSyntax> {

static readonly DiagnosticDescriptor RBI0001 = new (DiagnosticId, Title, MessageFormat, Category,
DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description);

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } =
ImmutableArray.Create (RBI0001);
internal static readonly DiagnosticDescriptor RBI0001 = new (
"RBI0001",
new LocalizableResourceString (nameof (Resources.RBI0001Title), Resources.ResourceManager, typeof (Resources)),
new LocalizableResourceString (nameof (Resources.RBI0001MessageFormat), Resources.ResourceManager, typeof (Resources)),
"Usage",
DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: new LocalizableResourceString (nameof (Resources.RBI0001Description), Resources.ResourceManager, typeof (Resources))
);

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = [RBI0001];

public override void Initialize (AnalysisContext context)
{
Expand All @@ -38,35 +36,17 @@ public override void Initialize (AnalysisContext context)
}

void AnalysisContext (SyntaxNodeAnalysisContext context)
{
// only care about classes
if (context.Node is not ClassDeclarationSyntax classDeclarationNode)
return;
=> this.AnalyzeBindingType (context);

var classSymbol = context.SemanticModel.GetDeclaredSymbol (classDeclarationNode);
if (classSymbol is null)
return;
public ImmutableArray<Diagnostic> Analyze (PlatformName _, ClassDeclarationSyntax declarationNode, INamedTypeSymbol symbol)
{
if (declarationNode.Modifiers.Any (x => x.IsKind (SyntaxKind.PartialKeyword)))
return [];

var boundAttributes = classSymbol.GetAttributes ();
if (boundAttributes.Length == 0) {
return;
}
var diagnostic = Diagnostic.Create (RBI0001,
declarationNode.Identifier.GetLocation (), // point to where the 'class' keyword is used
symbol.ToDisplayString ());
return [diagnostic];

// the c# syntax is a a list of lists of attributes. That is why we need to iterate through the list of lists
foreach (var attributeData in boundAttributes) {
// based on the type use the correct parser to retrieve the data
var attributeType = attributeData.AttributeClass?.ToDisplayString ();
switch (attributeType) {
case "ObjCBindings.BindingTypeAttribute":
// validate that the class is partial, else we need to report an error
if (!classDeclarationNode.Modifiers.Any (x => x.IsKind (SyntaxKind.PartialKeyword))) {
var diagnostic = Diagnostic.Create (RBI0001,
classDeclarationNode.Identifier.GetLocation (), // point to where the 'class' keyword is used
classSymbol.ToDisplayString ());
context.ReportDiagnostic (diagnostic);
}
break;
}
}
}
}
17 changes: 17 additions & 0 deletions src/rgen/Microsoft.Macios.Bindings.Analyzer/DiagnosticInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Microsoft.CodeAnalysis;

namespace Microsoft.Macios.Bindings.Analyzer;

public struct DiagnosticInfo (
string id,
LocalizableResourceString title,
LocalizableResourceString messageFormat,
LocalizableResourceString description,
string category) {

public string Id { get; } = id;
public LocalizableResourceString Title { get; } = title;
public LocalizableResourceString MessageFormat { get; } = messageFormat;
public LocalizableResourceString Description { get; } = description;
public string Category { get; } = category;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.Macios.Generator;
using Microsoft.Macios.Generator.Extensions;
using Diagnostic = Microsoft.CodeAnalysis.Diagnostic;

namespace Microsoft.Macios.Bindings.Analyzer.Extensions;

public static class BindingTypeAnalyzerExtensions {
public static void AnalyzeBindingType<T> (this IBindingTypeAnalyzer<T> self, SyntaxNodeAnalysisContext context) where T : BaseTypeDeclarationSyntax
{
// calculate the current compilation platform name
if (context.Node is not T declarationNode)
return;

var declaredSymbol = context.SemanticModel.GetDeclaredSymbol (declarationNode);
if (declaredSymbol is null)
return;

var boundAttributes = declaredSymbol.GetAttributes ();
if (boundAttributes.Length == 0) {
// do nothing since our generator only cares about declared types with the BindingType attribute
return;
}

// the c# syntax is a a list of lists of attributes. That is why we need to iterate through the list of lists
foreach (var attributeData in boundAttributes) {
// based on the type use the correct parser to retrieve the data
var attributeType = attributeData.AttributeClass?.ToDisplayString ();
switch (attributeType) {
case AttributesNames.BindingAttribute:
// validate that the class is partial, else we need to report an error
var diagnostics= self.Analyze (context.Compilation.GetCurrentPlatform (),
declarationNode, declaredSymbol);
foreach (var diagnostic in diagnostics)
context.ReportDiagnostic (diagnostic);
break;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Xamarin.Utils;

namespace Microsoft.Macios.Bindings.Analyzer.Extensions;

public static class PlatformNameExtensions {

public static ApplePlatform ToApplePlatform (this PlatformName platformName)
=> platformName switch {
PlatformName.iOS => ApplePlatform.iOS,
PlatformName.MacCatalyst => ApplePlatform.MacCatalyst,
PlatformName.MacOSX => ApplePlatform.MacOSX,
PlatformName.TvOS => ApplePlatform.TVOS,
_ => ApplePlatform.None,
};

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Microsoft.Macios.Bindings.Analyzer;

/// <summary>
/// Interface to be implemented by those analyzer that will be looking at BindingTypes.
/// </summary>
public interface IBindingTypeAnalyzer<T> where T : BaseTypeDeclarationSyntax {
ImmutableArray<Diagnostic> Analyze (PlatformName platformName, T declarationNode, INamedTypeSymbol symbol);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.9.2" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.9.2"/>
</ItemGroup>
Expand All @@ -43,4 +44,23 @@
</Compile>
</ItemGroup>

<ItemGroup>
<Compile Include="../Microsoft.Macios.Generator/AttributesNames.cs" >
<Link>external\AttributesNames.cs</Link>
</Compile>
<Compile Include="../Microsoft.Macios.Generator/Attributes/FieldData.cs" >
<Link>external\FieldData.cs</Link>
</Compile>
<Compile Include="../Microsoft.Macios.Generator/Extensions/CompilationExtensions.cs" >
<Link>Extensions\CompilationExtensions.cs</Link>
</Compile>
<Compile Include="../Microsoft.Macios.Generator/Extensions/TypeSymbolExtensions.cs" >
<Link>Extensions\TypeSymbolExtensions.cs</Link>
</Compile>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Microsoft.Macios.Binding.Common\Microsoft.Macios.Binding.Common.csproj" />
</ItemGroup>

</Project>
80 changes: 64 additions & 16 deletions src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.Designer.cs

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

Loading

0 comments on commit fa0c0ee

Please sign in to comment.