+
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ jobs:
shell: bash

- name: Verify code format
run: dotnet format GirCore.sln --no-restore --verify-no-changes --exclude *.Generated.cs
run: dotnet format GirCore.sln --no-restore --verify-no-changes --exclude *.Generated.cs --exclude-diagnostics GirCore1001 GirCore1002 GirCore1003
working-directory: './src'

- name: Run integration tests
Expand Down
30 changes: 30 additions & 0 deletions docs/docs/integration/diagnostic/1001.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# GirCore1001

- Title: GObject subclass constructor with parameters must call 'this()' constructor
- Message: GObject subclass constructor with parameters must call a generated constructor like 'this()'

Not calling the `this` results in the class being represented by its parent type instead of its actual type. Call `this()` to ensure the correct type of the instance.

Invalid code:
```csharp
[Subclass<Gtk.Widget>]
public partial class MyWidget
{
public MyWidget(string data)
{
...
}
}
```

Solution:
```csharp
[Subclass<Gtk.Widget>]
public partial class MyWidget
{
public MyWidget(string data) : this()
{
...
}
}
```
30 changes: 30 additions & 0 deletions docs/docs/integration/diagnostic/1002.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# GirCore1002

- Title: GObject subclass constructor without parameters must use partial 'Initialize' method
- Message: GObject subclass constructor without parameters must use 'partial void Initialize()' method instead of a constructor

Using a custom constructor instead of the `Initialize()` method does not initialize the instance in all cases. There are generated constructors which can get called if a new instance is created by native code. Those constructors call the `Initialize()` method.

Invalid code:
```csharp
[Subclass<Gtk.Widget>]
public partial class MyWidget
{
public MyWidget()
{
...
}
}
```

Solution:
```csharp
[Subclass<Gtk.Widget>]
public partial class MyWidget
{
partial void Initialize()
{
...
}
}
```
30 changes: 30 additions & 0 deletions docs/docs/integration/diagnostic/1003.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# GirCore1001

- Title: GObject subclass constructor must not call base constructor
- Message: GObject subclass constructor must call a generated constructor like 'this()' instead of 'base()'

An explicit call to the constructor of a base class results in an instance which has the type of its parent instead of its own type. Call `this()` to ensure the correct type of the instance.

Invalid code:
```csharp
[Subclass<Gtk.Widget>]
public partial class MyWidget
{
public MyWidget(string text) : base()
{
...
}
}
```

Solution:
```csharp
[Subclass<Gtk.Widget>]
public partial class MyWidget
{
public MyWidget(string text) : this()
{
...
}
}
```
11 changes: 11 additions & 0 deletions docs/docs/integration/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Integration packages

The GirCore project provides integration nuget packages which can generate boilerplate code to allow easy integration of dotnet with the GObject type system.

The following nuget packages are available:
- [GObject-2.0.Integration](https://www.nuget.org/packages/GirCore.GObject-2.0.Integration): Makes it easy to create a subclass of a GObject

## Diagnostic messages
- [GirCore1001](diagnostic/1001.md)
- [GirCore1002](diagnostic/1002.md)
- [GirCore1003](diagnostic/1003.md)
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using System.Reflection;

namespace GObject.Integration;

internal static class GeneratedCodeAttribute
{
private static string Version { get; } = GetVersion();
private static string Name { get; } = GetName();

public static string Render()
{
return $"""[System.CodeDom.Compiler.GeneratedCode("{Name}", "{Version}")]""";
}

private static string GetName()
{
var name = Assembly.GetExecutingAssembly().GetName().Name;
if (name is null)
throw new NotSupportedException("Could not get assembly version");

return $"GirCore.{name}";
}

private static string GetVersion()
{
var version = Assembly.GetExecutingAssembly().GetName().Version;
if (version is null)
throw new NotSupportedException("Could not get assembly version");

return $"{version.Major}.{version.Minor}.{version.Build}";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Microsoft.CodeAnalysis;

namespace GObject.Integration;

internal static class HandleAttribute
{
private const string FullyQualifiedDisplayName = "global::GObject.HandleAttribute<T>";

public static bool IsHandleAttribute(this AttributeData data)
{
var displayString = data.AttributeClass?.OriginalDefinition.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
return displayString == FullyQualifiedDisplayName;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Microsoft.CodeAnalysis;

namespace GObject.Integration;

internal static class SubclassAttribute
{
public const string MetadataName = "GObject.SubclassAttribute`1";
public const string FullyQualifiedDisplayName = "global::GObject.SubclassAttribute<T>";

public static bool IsSubclassAttribute(this AttributeData data)
{
var displayString = data.AttributeClass?.OriginalDefinition.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
return displayString == FullyQualifiedDisplayName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<ItemGroup>
<!-- Reference 4.8 as it is the first version which supports net8.0 -->
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.8.0"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0"/>
</ItemGroup>

<ItemGroup>
Expand Down
31 changes: 31 additions & 0 deletions src/Extensions/GObject-2.0.Integration/SourceAnalyzer/Analyzer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;

namespace GObject.Integration.SourceAnalyzer;

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class Analyzer : DiagnosticAnalyzer
{

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = [
GirCore1001.DiagnosticDescriptor,
GirCore1002.DiagnosticDescriptor,
GirCore1003.DiagnosticDescriptor
];

public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();

RegisterDiagnosticRule<GirCore1001>(context);
RegisterDiagnosticRule<GirCore1002>(context);
RegisterDiagnosticRule<GirCore1003>(context);
}

private static void RegisterDiagnosticRule<T>(AnalysisContext context) where T : Rule
{
context.RegisterSyntaxNodeAction(T.Analyze, T.SyntaxKind);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace GObject.Integration.SourceAnalyzer;

internal static class DiagnosticLink
{
public static string Create(int id)
{
return $"https://gircore.github.io/docs/integration/diagnostic/{id}.html";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace GObject.Integration.SourceAnalyzer;

internal sealed class GirCore1001 : Rule
{
public static SyntaxKind SyntaxKind => SyntaxKind.ConstructorDeclaration;

public static DiagnosticDescriptor DiagnosticDescriptor { get; } = new(
id: "GirCore1001",
title: "GObject subclass constructor with parameters must call 'this()' constructor",
messageFormat: "GObject subclass constructor with parameters must call a generated constructor like 'this()'",
category: "Usage",
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true,
helpLinkUri: DiagnosticLink.Create(1001)
);

public static void Analyze(SyntaxNodeAnalysisContext context)
{
var constructorSyntax = (ConstructorDeclarationSyntax) context.Node;

if (!constructorSyntax.ParameterList.Parameters.Any())
return;

var initializerSyntax = constructorSyntax.Initializer;

if (initializerSyntax is not null)
return;

if (constructorSyntax.Parent is not ClassDeclarationSyntax classSyntax)
return;

var classSymbol = context.SemanticModel.GetDeclaredSymbol(classSyntax);
if (classSymbol is null || !classSymbol.GetAttributes().Any(x => x.IsSubclassAttribute()))
return;

var diagnostic = Diagnostic.Create(DiagnosticDescriptor, constructorSyntax.Identifier.GetLocation());
context.ReportDiagnostic(diagnostic);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace GObject.Integration.SourceAnalyzer;

internal sealed class GirCore1002 : Rule
{
public static SyntaxKind SyntaxKind => SyntaxKind.ConstructorDeclaration;

public static DiagnosticDescriptor DiagnosticDescriptor { get; } = new(
id: "GirCore1002",
title: "GObject subclass constructor without parameters must use partial 'Initialize' method",
messageFormat: "GObject subclass constructor without parameters must use 'partial void Initialize()' method instead of a constructor",
category: "Usage",
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true,
helpLinkUri: DiagnosticLink.Create(1002)
);

public static void Analyze(SyntaxNodeAnalysisContext context)
{
var constructorSyntax = (ConstructorDeclarationSyntax) context.Node;

if (constructorSyntax.ParameterList.Parameters.Any())
return;

if (constructorSyntax.Parent is not ClassDeclarationSyntax classSyntax)
return;

var classSymbol = context.SemanticModel.GetDeclaredSymbol(classSyntax);
if (classSymbol is null || !classSymbol.GetAttributes().Any(x => x.IsSubclassAttribute()))
return;

var diagnostic = Diagnostic.Create(DiagnosticDescriptor, constructorSyntax.Identifier.GetLocation());
context.ReportDiagnostic(diagnostic);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace GObject.Integration.SourceAnalyzer;

internal sealed class GirCore1003 : Rule
{
public static SyntaxKind SyntaxKind => SyntaxKind.ConstructorDeclaration;

public static DiagnosticDescriptor DiagnosticDescriptor { get; } = new(
id: "GirCore1003",
title: "GObject subclass constructor must not call base constructor",
messageFormat: "GObject subclass constructor must call a generated constructor like 'this()' instead of 'base()'",
category: "Usage",
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true,
helpLinkUri: DiagnosticLink.Create(1003)
);

public static void Analyze(SyntaxNodeAnalysisContext context)
{
var constructorSyntax = (ConstructorDeclarationSyntax) context.Node;
var initializerSyntax = constructorSyntax.Initializer;

if (initializerSyntax is null || initializerSyntax.Kind() != SyntaxKind.BaseConstructorInitializer)
return;

if (constructorSyntax.Parent is not ClassDeclarationSyntax classSyntax)
return;

var classSymbol = context.SemanticModel.GetDeclaredSymbol(classSyntax);
if (classSymbol is null || !classSymbol.GetAttributes().Any(x => x.IsSubclassAttribute()))
return;

var diagnostic = Diagnostic.Create(DiagnosticDescriptor, initializerSyntax.GetLocation());
context.ReportDiagnostic(diagnostic);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;

namespace GObject.Integration.SourceAnalyzer;

internal interface Rule
{
static abstract SyntaxKind SyntaxKind { get; }
static abstract void Analyze(SyntaxNodeAnalysisContext context);
}
Loading
Loading
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载