Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
7385f20
strip version metadata for BepInEx 5
Hamunii Apr 7, 2025
437436f
fix not setting version if not BepInEx 5
Hamunii Apr 7, 2025
9438783
make it incremental
Hamunii Apr 23, 2025
a6c5eb1
add .g.cs to BepInAutoPluginAttribute source
Hamunii Apr 23, 2025
517f349
target oldest roslyn version possible for maximum support
Hamunii Apr 23, 2025
5d8e3db
remove probably stupid code
Hamunii Apr 23, 2025
74bc4ad
resolve most issues
Hamunii Apr 24, 2025
f079d14
upgrade to latest CodeAnalysis references
Hamunii Apr 24, 2025
1f5b443
don't generate for invalid classes
Hamunii Apr 24, 2025
3f6098f
introduce analyzer for missing partial modifier
Hamunii Apr 24, 2025
d7e3b44
more proper nullable handling
Hamunii Apr 24, 2025
94ab940
change ToPluginClass nullability handling
Hamunii Apr 24, 2025
0fbea64
make record structs readonly
Hamunii Apr 24, 2025
a0d9942
downgrade Microsoft.CodeAnalysis.CSharp to oldest supported version
Hamunii Apr 25, 2025
09d2f61
this needs to be changed due to the downgraded version, whoops
Hamunii Apr 25, 2025
1ce922b
use ds5678's method for analyzer
Hamunii Apr 25, 2025
872ad75
cleanup
Hamunii Apr 25, 2025
e62cd4b
update readme to be more comprehensive
Hamunii Apr 25, 2025
211dfb0
use Title for project name with Product as a fallback
Hamunii Apr 26, 2025
808b719
take info from assembly attributes instead & reintroduce BepInAutoPlu…
Hamunii Apr 26, 2025
1793b01
update readme to be up to date again
Hamunii Apr 26, 2025
d7961dc
switch from using FAWMN for getting attribute arguments
Hamunii Apr 29, 2025
a7e309c
ignore all warnings in generated file
Hamunii Jun 24, 2025
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
artifacts/
bin/
obj/
/packages/
Expand Down
10 changes: 10 additions & 0 deletions BepInEx.AutoPlugin.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@

Microsoft Visual Studio Solution File, Format Version 12.00
#
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BepInEx.AutoPlugin", "BepInEx.AutoPlugin\BepInEx.AutoPlugin.csproj", "{DF01D949-82F0-4CCA-A094-903C8F30E691}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestPlugin", "TestPlugin\TestPlugin.csproj", "{6C291C8C-CC69-45BE-9235-421F7B12B0B2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -12,5 +15,12 @@ Global
{DF01D949-82F0-4CCA-A094-903C8F30E691}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DF01D949-82F0-4CCA-A094-903C8F30E691}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DF01D949-82F0-4CCA-A094-903C8F30E691}.Release|Any CPU.Build.0 = Release|Any CPU
{6C291C8C-CC69-45BE-9235-421F7B12B0B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6C291C8C-CC69-45BE-9235-421F7B12B0B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6C291C8C-CC69-45BE-9235-421F7B12B0B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6C291C8C-CC69-45BE-9235-421F7B12B0B2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
3 changes: 3 additions & 0 deletions BepInEx.AutoPlugin/AnalyzerReleases.Shipped.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
; Shipped analyzer releases
; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md

8 changes: 8 additions & 0 deletions BepInEx.AutoPlugin/AnalyzerReleases.Unshipped.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
; Unshipped analyzer release
; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md

### New Rules

Rule ID | Category | Severity | Notes
--------|----------|----------|-------
AutoPlugin0001 | BepInEx.AutoPlugin | Warning | AutoPluginAnalyzer
104 changes: 104 additions & 0 deletions BepInEx.AutoPlugin/Analyzers/PluginClassAnalyzer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace BepInEx.AutoPlugin.Analyzers;

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class PluginClassAnalyzer : DiagnosticAnalyzer
{
const string Category = "BepInEx.AutoPlugin";

public static readonly DiagnosticDescriptor PluginClassMustBeMarkedPartial = new(
"AutoPlugin0001",
"Plugin class must be marked partial",
"Plugin class '{0}' must be marked partial for use with AutoPlugin",
Category,
DiagnosticSeverity.Warning,
isEnabledByDefault: true
);

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } =
ImmutableArray.Create(PluginClassMustBeMarkedPartial);

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

context.RegisterSymbolAction(AnalyzeClassSymbol, SymbolKind.NamedType);
}

static void AnalyzeClassSymbol(SymbolAnalysisContext context)
{
var typeSymbol = (INamedTypeSymbol)context.Symbol;

bool isNotClassOrIsPartialClass = typeSymbol.DeclaringSyntaxReferences.All(
static syntaxReference =>
syntaxReference.GetSyntax() is not ClassDeclarationSyntax classSyntax
|| classSyntax.Modifiers.Any(SyntaxKind.PartialKeyword)
);

if (isNotClassOrIsPartialClass)
{
return;
}

foreach (AttributeData attribute in typeSymbol.GetAttributes())
{
var attributeClass = attribute.AttributeClass;
if (attributeClass is null)
{
continue;
}

switch (attributeClass.Name)
{
case AutoPluginGenerator.BepInAutoPluginAttributeName:
string attributeFullName = attributeClass.ToDisplayString();
if (attributeFullName != AutoPluginGenerator.BepInAutoPluginAttributeFullName)
{
continue;
}
break;

case AutoPluginGenerator.PatcherAutoPluginAttributeName:
attributeFullName = attributeClass.ToDisplayString();
if (attributeFullName != AutoPluginGenerator.PatcherAutoPluginAttributeFullName)
{
continue;
}
break;

default:
continue;
}

if (
attribute.ApplicationSyntaxReference?.GetSyntax()
is not AttributeSyntax attributeSyntax
)
{
continue;
}

if (
attributeSyntax.Parent is not AttributeListSyntax attributeList
|| attributeList.Parent is not ClassDeclarationSyntax classDeclaration
)
{
continue;
}

var diagnostic = Diagnostic.Create(
PluginClassMustBeMarkedPartial,
classDeclaration.Identifier.GetLocation(),
classDeclaration.Identifier.ToString()
);

context.ReportDiagnostic(diagnostic);
}
}
}
Loading