Skip to content
Draft
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
53 changes: 53 additions & 0 deletions .global.editorconfig.ini
Original file line number Diff line number Diff line change
@@ -1,8 +1,59 @@
is_global = true

## misc. C# compiler/language warnings

# Obsolete member 'memberA' overrides non-obsolete member 'memberB' (this simply doesn't work, see https://learn.microsoft.com/en-us/dotnet/csharp/misc/cs0809#methods-recognized-as-obsolete)
dotnet_diagnostic.CS0809.severity = error

## CLS compliance (F#/VB.NET compatibility) warnings

# `__arglist` in CLS-compliant context
dotnet_diagnostic.CS3000.severity = error
# illegal parameter type (e.g. `uint`) in CLS-compliant context
dotnet_diagnostic.CS3001.severity = error
# illegal member return type (e.g. `uint`) in CLS-compliant context
dotnet_diagnostic.CS3002.severity = error
# illegal field(?) type (e.g. `uint`) in CLS-compliant context
dotnet_diagnostic.CS3003.severity = error
# type and member identifiers in CLS-compliant context can only contain certain characters(?)
dotnet_diagnostic.CS3004.severity = error
# type or member identifier collision in CLS-compliant context (case-insensitive comparison)
dotnet_diagnostic.CS3005.severity = error
# type or member identifier may not begin with '_' in CLS-compliant context
dotnet_diagnostic.CS3008.severity = error
# type with non-CLS-compliant supertype cannot be CLS-compliant
dotnet_diagnostic.CS3009.severity = error
# CLS-compliant interface may not have non-CLS-compliant members
dotnet_diagnostic.CS3010.severity = error
# member of CLS-compliant type must be CLS-compliant and/or concrete (non-`abstract`)
dotnet_diagnostic.CS3011.severity = error
# `[module:CLSCompliant]` compiled without `/target:module`
dotnet_diagnostic.CS3012.severity = error
# `[module:CLSCompliant]` missing when `[assembly:CLSCompliant]` present
dotnet_diagnostic.CS3013.severity = error
# `[CLSCompliant]` applied to member without `[assembly:CLSCompliant]` present
dotnet_diagnostic.CS3014.severity = error
# CLS-compliant `Attribute` subclass declares a constructor with illegal parameter types (e.g. `int[]`)
dotnet_diagnostic.CS3015.severity = error
# `[module:CLSCompliant]` doesn't match `[assembly:CLSCompliant]`
dotnet_diagnostic.CS3017.severity = error
# inner type with non-CLS-compliant outer type cannot be CLS-compliant
dotnet_diagnostic.CS3018.severity = error
# `[CLSCompliant]` applied to `internal` type or member
dotnet_diagnostic.CS3019.severity = error
# `[CLSCompliant]` applied to type without `[assembly:CLSCompliant]` present
dotnet_diagnostic.CS3021.severity = error
# `[CLSCompliant]` applied to parameter
dotnet_diagnostic.CS3022.severity = error
# `[CLSCompliant]` applied to member return type
dotnet_diagnostic.CS3023.severity = error
# type with non-CLS-compliant type parameter constraint cannot be CLS-compliant
dotnet_diagnostic.CS3024.severity = error
# `volatile` in CLS-compliant context
dotnet_diagnostic.CS3026.severity = error
# interface with non-CLS-compliant superinterface cannot be CLS-compliant
dotnet_diagnostic.CS3027.severity = error

## BizHawk internal rules

# Do not use anonymous delegates
Expand Down Expand Up @@ -73,6 +124,8 @@ dotnet_diagnostic.BHI3300.severity = error

# Do not declare static members on generic types
dotnet_diagnostic.CA1000.severity = error
# Mark assemblies with CLSCompliantAttribute
#dotnet_diagnostic.CA1014.severity = warning
# Properties should not be write only
dotnet_diagnostic.CA1044.severity = error
# Do not raise exceptions in unexpected locations
Expand Down
13 changes: 13 additions & 0 deletions ExternalProjects/AnalyzersCommon/DiagnosticDescriptorExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace BizHawk.Analyzers;
using MI = System.Runtime.CompilerServices.MethodImplAttribute;
using OAC = Microsoft.CodeAnalysis.Diagnostics.OperationAnalysisContext;
using SNAC = Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext;
using SPC = Microsoft.CodeAnalysis.SourceProductionContext;

public static class DDExtensions
{
Expand Down Expand Up @@ -91,6 +92,10 @@ public static void ReportAt(this DD diag, Location location, OAC ctx, object?[]?
public static void ReportAt(this DD diag, Location location, SNAC ctx, object?[]? messageArgs = null)
=> ctx.ReportDiagnostic(Diagnostic.Create(diag, location, messageArgs: messageArgs));

[MI(AggressiveInlining)]
public static void ReportAt(this DD diag, Location location, SPC ctx, object?[]? messageArgs = null)
=> ctx.ReportDiagnostic(Diagnostic.Create(diag, location, messageArgs: messageArgs));

[MI(AggressiveInlining)]
public static void ReportAt(this DD diag, Location location, OAC ctx, string message)
=> diag.ReportAt(location, ctx, [ message ]);
Expand Down Expand Up @@ -167,6 +172,10 @@ public static void ReportAt(this DD diag, SyntaxNode location, OAC ctx, object?[
public static void ReportAt(this DD diag, SyntaxNode location, SNAC ctx, object?[]? messageArgs = null)
=> diag.ReportAt(location.GetLocation(), ctx, messageArgs);

[MI(AggressiveInlining)]
public static void ReportAt(this DD diag, SyntaxNode location, SPC ctx, object?[]? messageArgs = null)
=> diag.ReportAt(location.GetLocation(), ctx, messageArgs);

[MI(AggressiveInlining)]
public static void ReportAt(this DD diag, SyntaxNode location, OAC ctx, string message)
=> diag.ReportAt(location, ctx, [ message ]);
Expand All @@ -175,6 +184,10 @@ public static void ReportAt(this DD diag, SyntaxNode location, OAC ctx, string m
public static void ReportAt(this DD diag, SyntaxNode location, SNAC ctx, string message)
=> diag.ReportAt(location, ctx, [ message ]);

[MI(AggressiveInlining)]
public static void ReportAt(this DD diag, SyntaxNode location, SPC ctx, string message)
=> diag.ReportAt(location, ctx, [ message ]);

[MI(AggressiveInlining)]
public static void ReportAt(
this DD diag,
Expand Down
8 changes: 5 additions & 3 deletions ExternalProjects/AnalyzersCommon/RoslynUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,7 @@ public static IEnumerable<ITypeSymbol> AllEnclosingTypes(this ITypeSymbol? typeS

public static bool? GetIsCLSCompliant(this ITypeSymbol typeSym, ISymbol clsCompliantAttrSym)
=> typeSym.AllEnclosingTypes().Prepend(typeSym)
.Select(typeSym1 => typeSym1.GetAttributes()
.FirstOrDefault(ad => clsCompliantAttrSym.Matches(ad.AttributeClass))
?.ConstructorArguments[0].Value as bool?)
.Select(typeSym1 => typeSym1.GetOwnCLSCompliantValue(clsCompliantAttrSym))
.FirstOrDefault(static tristate => tristate is not null);

public static string GetMethodName(this ConversionOperatorDeclarationSyntax cods)
Expand Down Expand Up @@ -112,6 +110,10 @@ public static string GetMethodName(this OperatorDeclarationSyntax ods)
_ => throw new ArgumentException(paramName: nameof(ods), message: "pretend this is a BHI6660 unexpected token in AST (in this case, a new kind of operator was added to C#)"),
};

public static bool? GetOwnCLSCompliantValue(this ITypeSymbol typeSym, ISymbol clsCompliantAttrSym)
=> typeSym.GetAttributes().FirstOrDefault(ad => clsCompliantAttrSym.Matches(ad.AttributeClass))
?.ConstructorArguments[0].Value as bool?;

private static ITypeSymbol? GetThrownExceptionType(this SemanticModel model, ExpressionSyntax exprSyn)
=> exprSyn is ObjectCreationExpressionSyntax
? model.GetTypeInfo(exprSyn).Type
Expand Down
2 changes: 2 additions & 0 deletions ExternalProjects/BizHawk.SrcGen.CLSCompliance/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/bin
/obj
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#pragma warning disable
#nullable enable // for when this file is embedded

using System;

namespace BizHawk.SrcGen.CLSCompliance
{
[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.Class)]
public sealed class AutoderiveCLSComplianceAttribute : Attribute {}
}
#pragma warning restore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<Import Project="../AnalyzersCommon.props" />
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
namespace BizHawk.SrcGen.CLSCompliance;

using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;

using BizHawk.Analyzers;

[Generator]
public sealed class CLSComplianceGenerator : IIncrementalGenerator
{
private static readonly DiagnosticDescriptor DiagTest = new(
id: "BHI1337",
title: "test",
messageFormat: "{0}",
category: "Usage",
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);

public void Initialize(IncrementalGeneratorInitializationContext context)
{
var declSyns0 = context.SyntaxProvider.CreateSyntaxProvider(
predicate: static (syntaxNode, _) => syntaxNode is TypeDeclarationSyntax, // excludes enums because they can't be `partial`; you'll have to add the attribute explicitly
transform: static (ctx, _) => (TypeDeclarationSyntax) ctx.Node);
context.RegisterSourceOutput(context.CompilationProvider.Combine(declSyns0.Collect()), Execute);
}

private static void Execute(
SourceProductionContext context,
(Compilation Compilation, ImmutableArray<TypeDeclarationSyntax> DeclSyns) receiver)
{
var clsCompliantAttrSym = receiver.Compilation.GetTypeByMetadataName(typeof(CLSCompliantAttribute).FullName)!;
bool? InheritedCLSCompliant(ITypeSymbol typeSym)
=> typeSym.AllBaseTypes().Select(typeSym1 => typeSym1.GetOwnCLSCompliantValue(clsCompliantAttrSym))
.FirstOrDefault(static tristate => tristate is not null);
string? NodesToNamespaceBlock(
IGrouping<string, (TypeDeclarationSyntax Syn, INamedTypeSymbol Sym, string Namespace)> group)
{
StringBuilder sb = new();
HashSet<string> seen = new(); // `DistinctBy` only exists in .NET Core >:(
foreach (var (syn, sym, _) in group)
{
if (!seen.Add(sym.Name)) continue; // dedup partials
if (sym.GetIsCLSCompliant(clsCompliantAttrSym) is not null) continue; // already set explicitly
var accessMod = "internal";
var hasPartial = false;
foreach (var mod in syn.Modifiers)
{
if (mod.Text is "public" or "internal" or "protected" or "private")
{
accessMod = mod.Text;
if (accessMod is not "public") break;
}
else if (mod.Text is "partial") hasPartial = true; // initially set this up for `static class`, `readonly struct`, etc. but it turns out you don't need to repeat them
}
if (accessMod is not "public") continue;
if (!hasPartial)
{
// DiagTest.ReportAt(syn, context, "make this type partial");
continue;
}
sb.AppendFormat(
"\t[CLSCompliant({0})]\n\t{1} partial {2} {3}{4} {{}}\n\n",
(InheritedCLSCompliant(sym) ?? true).ToString().ToLowerInvariant(),
accessMod,
syn switch
{
ClassDeclarationSyntax => "class",
InterfaceDeclarationSyntax => "interface",
RecordDeclarationSyntax rds => $"record {rds.ClassOrStructKeyword.Text}",
StructDeclarationSyntax => "struct",
_ => throw new InvalidOperationException("pretend this is a BHI6660 unexpected node in AST (in this case, a new kind of type declaration was added to C#)"),
},
syn.Identifier.Text,
syn.TypeParameterList?.ToString() ?? string.Empty);
}
return sb.Length is 0 ? null : $"\n\nnamespace {group.Key}\n{{\n{sb}}}";
}

var compilation = receiver.Compilation;
var clsCompliantSym = compilation.GetTypeByMetadataName(typeof(CLSCompliantAttribute).FullName)!;
List<(TypeDeclarationSyntax Syn, INamedTypeSymbol Sym, string Namespace)> typeDecls = new();
foreach (var tuple in receiver.DeclSyns
.Select(tds => (Syn: tds, Sym: compilation.GetSemanticModel(tds.SyntaxTree)!.GetDeclaredSymbol(tds, context.CancellationToken)!))
.Where(tuple => tuple.Sym.GetIsCLSCompliant(clsCompliantSym) is null))
{
var parentSyn = tuple.Syn.Parent;
var nds = parentSyn as NamespaceDeclarationSyntax;
if (nds is not null)
{
typeDecls.Add((
tuple.Syn,
tuple.Sym,
nds.Name.ToMetadataNameStr() ?? string.Empty));
}
else if (parentSyn is not null) // must be a type decl. with this as its inner class/struct
{
//TODO warn
}
}
var src = $"#pragma warning disable MA0104{string.Concat(
typeDecls.GroupBy(static tuple => tuple.Namespace)
.Select(NodesToNamespaceBlock)
.Where(static s => s is not null))}";
context.AddSource("CLSCompliance.cs", SourceText.From(src, Encoding.UTF8));
}
}
Binary file added References/BizHawk.SrcGen.CLSCompliance.dll
Binary file not shown.
1 change: 1 addition & 0 deletions src/BizHawk.Bizware.Graphics/BitmapBuffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,7 @@ private void LoadInternal(Stream stream, Bitmap bitmap, BitmapLoadOptions option
/// <summary>
/// Loads the BitmapBuffer from a source buffer, which is expected to be the right pixel format
/// </summary>
[CLSCompliant(false)]
public void LoadFrom(int width, int stride, int height, byte* data, BitmapLoadOptions options)
{
var cleanup = options.CleanupAlpha0;
Expand Down
1 change: 1 addition & 0 deletions src/BizHawk.Bizware.Input/OSTailoredKeyInputAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace BizHawk.Bizware.Input
public abstract class OSTailoredKeyMouseInputAdapter : IHostInputAdapter
{
private IKeyMouseInput? _keyMouseInput;
[CLSCompliant(false)] //TODO just needs renaming
protected Func<bool>? _getHandleAlternateKeyboardLayouts;

public abstract string Desc { get; }
Expand Down
4 changes: 4 additions & 0 deletions src/BizHawk.Client.Common/Api/ApiContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public ICommApi Comm
public IEmuClientApi EmuClient
=> Get<IEmuClientApi>();

[CLSCompliant(false)]
public IEmulationApi Emulation
=> Get<IEmulationApi>(); // requires IEmulator

Expand All @@ -26,15 +27,18 @@ public IInputApi Input
public IJoypadApi Joypad
=> Get<IJoypadApi>();

[CLSCompliant(false)]
public IMemoryApi Memory
=> Get<IMemoryApi>(); // requires IEmulator

[CLSCompliant(false)]
public IMemoryEventsApi? MemoryEvents
=> TryGet<IMemoryEventsApi>(); // requires IDebuggable

public IMemorySaveStateApi? MemorySaveState
=> TryGet<IMemorySaveStateApi>(); // requires IStatable

[CLSCompliant(false)]
public IMovieApi Movie
=> Get<IMovieApi>();

Expand Down
1 change: 1 addition & 0 deletions src/BizHawk.Client.Common/Api/Classes/EmulationApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

namespace BizHawk.Client.Common
{
[CLSCompliant(false)]
[Description("A library for interacting with the currently loaded emulator core")]
public sealed class EmulationApi : IEmulationApi
{
Expand Down
1 change: 1 addition & 0 deletions src/BizHawk.Client.Common/Api/Classes/JoypadApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public sealed class JoypadApi : IJoypadApi

private readonly Action<string> LogCallback;

[CLSCompliant(MovieSession.CLS_IMOVIESESSION)]
public JoypadApi(Action<string> logCallback, InputManager inputManager, IMovieSession movieSession)
{
LogCallback = logCallback;
Expand Down
1 change: 1 addition & 0 deletions src/BizHawk.Client.Common/Api/Classes/MemoryApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

namespace BizHawk.Client.Common
{
[CLSCompliant(false)]
public sealed class MemoryApi : IMemoryApi
{
[RequiredService]
Expand Down
1 change: 1 addition & 0 deletions src/BizHawk.Client.Common/Api/Classes/MemoryEventsApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace BizHawk.Client.Common
{
[CLSCompliant(false)]
public sealed class MemoryEventsApi : IMemoryEventsApi
{
[RequiredService]
Expand Down
1 change: 1 addition & 0 deletions src/BizHawk.Client.Common/Api/Classes/MovieApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

namespace BizHawk.Client.Common
{
[CLSCompliant(false)]
public sealed class MovieApi : IMovieApi
{
private readonly IMainFormForApi _mainForm;
Expand Down
1 change: 1 addition & 0 deletions src/BizHawk.Client.Common/Api/Classes/UserDataApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public KeyCollectionType Keys
}
}

[CLSCompliant(MovieSession.CLS_IMOVIESESSION)]
public UserDataApi(IMovieSession movieSession) => _movieSession = movieSession;

/// <exception cref="InvalidOperationException">type of <paramref name="value"/> cannot be used in userdata</exception>
Expand Down
1 change: 1 addition & 0 deletions src/BizHawk.Client.Common/Api/ExternalToolAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public override bool NotApplicableTo(string romHash, string? sysID)
}

[AttributeUsage(AttributeTargets.Class)]
[CLSCompliant(false)] //TODO make [ExternalToolApplicability.SingleRom] multi-application
public sealed class RomList : ExternalToolApplicabilityAttributeBase
{
private readonly IList<string> _romHashes;
Expand Down
1 change: 1 addition & 0 deletions src/BizHawk.Client.Common/Api/Interfaces/IEmulationApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

namespace BizHawk.Client.Common
{
[CLSCompliant(false)]
public interface IEmulationApi : IExternalApi
{
void DisplayVsync(bool enabled);
Expand Down
1 change: 1 addition & 0 deletions src/BizHawk.Client.Common/Api/Interfaces/IMemoryApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace BizHawk.Client.Common
{
[CLSCompliant(false)]
public interface IMemoryApi : IExternalApi
{
string MainMemoryName { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace BizHawk.Client.Common
{
[CLSCompliant(false)]
public interface IMemoryEventsApi : IExternalApi
{
void AddReadCallback(MemoryCallbackDelegate cb, uint? address, string domain);
Expand Down
1 change: 1 addition & 0 deletions src/BizHawk.Client.Common/Api/Interfaces/IMovieApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace BizHawk.Client.Common
{
[CLSCompliant(false)]
public interface IMovieApi : IExternalApi
{
bool StartsFromSavestate();
Expand Down
Loading
Loading