mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-04-18 13:29:22 -04:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
186 lines
6.1 KiB
C#
186 lines
6.1 KiB
C#
using System.Collections.Generic;
|
|
|
|
namespace Sandbox.CodeUpgrader;
|
|
|
|
[DiagnosticAnalyzer( LanguageNames.CSharp )]
|
|
public partial class HostSyncAttributeAnalyzer : Analyzer
|
|
{
|
|
public override DiagnosticDescriptor Rule => Diagnostics.HostSyncAttribute;
|
|
|
|
public override void Init( AnalysisContext context )
|
|
{
|
|
context.RegisterSyntaxNodeAction( AnalyzeNode, SyntaxKind.Attribute );
|
|
}
|
|
|
|
void AnalyzeNode( SyntaxNodeAnalysisContext context )
|
|
{
|
|
var attributeSyntax = (AttributeSyntax)context.Node;
|
|
|
|
// Check if the attribute name is "HostSync"
|
|
if ( attributeSyntax.Name.ToString() == "HostSync" || attributeSyntax.Name.ToString() == "Sandbox.HostSync" )
|
|
{
|
|
var diagnostic = Diagnostic.Create( Rule, attributeSyntax.GetLocation() );
|
|
context.ReportDiagnostic( diagnostic );
|
|
}
|
|
}
|
|
|
|
public override async Task RunTests( IAnalyzerTest tester )
|
|
{
|
|
await tester.TestWithMarkup( """
|
|
using Sandbox;
|
|
|
|
public class MyClass
|
|
{
|
|
[[|HostSync|]]
|
|
public int MySyncProperty { get; set; }
|
|
}
|
|
""" );
|
|
}
|
|
}
|
|
|
|
|
|
[ExportCodeFixProvider( LanguageNames.CSharp ), Shared]
|
|
public class HostSyncAttributeFix : Fixer<HostSyncAttributeAnalyzer>
|
|
{
|
|
public override async Task RegisterCodeFixesAsync( CodeFixContext context )
|
|
{
|
|
var root = await context.Document.GetSyntaxRootAsync( context.CancellationToken ).ConfigureAwait( false );
|
|
|
|
// Locate the diagnostic in the source code
|
|
var diagnostic = context.Diagnostics.First();
|
|
var diagnosticSpan = diagnostic.Location.SourceSpan;
|
|
|
|
// Find the attribute syntax node
|
|
var attributeNode = root.FindToken( diagnosticSpan.Start ).Parent.AncestorsAndSelf()
|
|
.OfType<AttributeSyntax>()
|
|
.FirstOrDefault();
|
|
|
|
if ( attributeNode == null ) return;
|
|
|
|
var action = CodeAction.Create(
|
|
title: "Use SyncFlags.FromHost",
|
|
createChangedDocument: c => ChangeAttributeAsync( context.Document, attributeNode ),
|
|
equivalenceKey: "ChangeHostSyncToFlag",
|
|
priority: CodeActionPriority.High );
|
|
|
|
// Register a code action that will invoke the fix
|
|
context.RegisterCodeFix( action, diagnostic );
|
|
}
|
|
|
|
private async Task<Document> ChangeAttributeAsync( Document document, AttributeSyntax attributeNode )
|
|
{
|
|
var root = await document.GetSyntaxRootAsync();
|
|
var siblingSync = attributeNode.Parent
|
|
.ChildNodes()
|
|
.FirstOrDefault( n => n is AttributeSyntax asn && asn.Name.ToString() is "Sync" or "Sandbox.Sync" ) as AttributeSyntax;
|
|
|
|
// Do we already have a Sync node as well - update that one because we shouldn't have duplicates
|
|
if ( siblingSync is not null )
|
|
{
|
|
var arguments = siblingSync.ArgumentList?.Arguments.ToList() ?? new List<AttributeArgumentSyntax>();
|
|
var syncFlagsArgument = arguments.FirstOrDefault( arg => arg.Expression.ToString().StartsWith( "SyncFlags" ) );
|
|
|
|
// Create the new SyncFlags.FromHost expression
|
|
var fromHostExpression = SyntaxFactory.ParseExpression( "SyncFlags.FromHost" );
|
|
|
|
if ( syncFlagsArgument != null )
|
|
{
|
|
// Combine existing SyncFlags with SyncFlags.FromHost using a bitwise OR
|
|
var combinedExpression = SyntaxFactory.BinaryExpression(
|
|
SyntaxKind.BitwiseOrExpression,
|
|
syncFlagsArgument.Expression,
|
|
fromHostExpression );
|
|
|
|
// Replace the old SyncFlags argument
|
|
syncFlagsArgument = syncFlagsArgument.WithExpression( combinedExpression );
|
|
}
|
|
else
|
|
{
|
|
// Add a new SyncFlags argument
|
|
syncFlagsArgument = SyntaxFactory.AttributeArgument( fromHostExpression );
|
|
}
|
|
|
|
var reorderedArguments = new List<AttributeArgumentSyntax>();
|
|
if ( syncFlagsArgument is not null )
|
|
reorderedArguments.Add( syncFlagsArgument );
|
|
|
|
reorderedArguments.AddRange( arguments.Where( arg => arg.NameEquals != null ) );
|
|
|
|
// Create a new AttributeArgumentList
|
|
var updatedArgumentList = SyntaxFactory.AttributeArgumentList( SyntaxFactory.SeparatedList( reorderedArguments ) );
|
|
var newAttribute = siblingSync.WithArgumentList( updatedArgumentList );
|
|
|
|
var newRoot = root.TrackNodes( attributeNode, siblingSync );
|
|
newRoot = newRoot.RemoveNode( newRoot.GetCurrentNode( attributeNode ), SyntaxRemoveOptions.KeepNoTrivia );
|
|
newRoot = newRoot.ReplaceNode( newRoot.GetCurrentNode( siblingSync ), newAttribute );
|
|
root = newRoot;
|
|
}
|
|
else
|
|
{
|
|
var arguments = attributeNode.ArgumentList?.Arguments.ToList() ?? new List<AttributeArgumentSyntax>();
|
|
var syncFlagsArgument = arguments.FirstOrDefault( arg => arg.Expression.ToString().StartsWith( "SyncFlags" ) );
|
|
|
|
// Create the new SyncFlags.FromHost expression
|
|
var fromHostExpression = SyntaxFactory.ParseExpression( "SyncFlags.FromHost" );
|
|
|
|
if ( syncFlagsArgument != null )
|
|
{
|
|
// Combine existing SyncFlags with SyncFlags.FromHost using a bitwise OR
|
|
var combinedExpression = SyntaxFactory.BinaryExpression(
|
|
SyntaxKind.BitwiseOrExpression,
|
|
syncFlagsArgument.Expression,
|
|
fromHostExpression );
|
|
|
|
// Replace the old SyncFlags argument
|
|
syncFlagsArgument = syncFlagsArgument.WithExpression( combinedExpression );
|
|
}
|
|
else
|
|
{
|
|
// Add a new SyncFlags argument
|
|
syncFlagsArgument = SyntaxFactory.AttributeArgument( fromHostExpression );
|
|
}
|
|
|
|
var reorderedArguments = new List<AttributeArgumentSyntax>();
|
|
if ( syncFlagsArgument is not null )
|
|
reorderedArguments.Add( syncFlagsArgument );
|
|
|
|
reorderedArguments.AddRange( arguments.Where( arg => arg.NameEquals != null ) );
|
|
|
|
// Create a new AttributeArgumentList
|
|
var updatedArgumentList = SyntaxFactory.AttributeArgumentList( SyntaxFactory.SeparatedList( reorderedArguments ) );
|
|
|
|
// Create the new Sync attribute
|
|
var newAttribute = SyntaxFactory.Attribute( SyntaxFactory.ParseName( "Sync" ) )
|
|
.WithArgumentList( updatedArgumentList );
|
|
|
|
// Replace the old HostSync attribute with the new Sync attribute
|
|
root = root.ReplaceNode( attributeNode, newAttribute );
|
|
}
|
|
|
|
// Return the updated document
|
|
return document.WithSyntaxRoot( root );
|
|
}
|
|
|
|
public override async Task RunTests( IFixerTest tester )
|
|
{
|
|
await tester.Test( """
|
|
using Sandbox;
|
|
|
|
public class MyClass
|
|
{
|
|
[|HostSync|]
|
|
public int MySyncProperty { get; set; }
|
|
}
|
|
""",
|
|
"""
|
|
using Sandbox;
|
|
|
|
public class MyClass
|
|
{
|
|
[|Sync( SyncFlags.FromHost )|]
|
|
public int MySyncProperty { get; set; }
|
|
}
|
|
""" );
|
|
}
|
|
}
|