This repository has been archived by the owner on Jul 23, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
33 changed files
with
1,355 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
18 changes: 18 additions & 0 deletions
18
Source/UdonRabbit.Analyzer.CodeFixes/CodeFixResources.Designer.cs
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
59 changes: 59 additions & 0 deletions
59
...onRabbit.Analyzer.CodeFixes/InvalidTargetPropertyForFieldChangeCallbackCodeFixProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
using System.Collections.Immutable; | ||
using System.Composition; | ||
using System.Linq; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CodeFixes; | ||
using Microsoft.CodeAnalysis.CSharp; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
|
||
using UdonRabbit.Analyzer.Abstractions; | ||
using UdonRabbit.Analyzer.Extensions; | ||
using UdonRabbit.Analyzer.Udon; | ||
|
||
namespace UdonRabbit.Analyzer | ||
{ | ||
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(InvalidTargetPropertyForFieldChangeCallbackCodeFixProvider))] | ||
[Shared] | ||
public class InvalidTargetPropertyForFieldChangeCallbackCodeFixProvider : CodeFixProviderBase | ||
{ | ||
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(InvalidTargetPropertyForFieldChangeCallback.ComponentId); | ||
|
||
public override async Task RegisterCodeFixesAsync(CodeFixContext context) | ||
{ | ||
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); | ||
if (!TryFindFirstAncestorOrSelf<FieldDeclarationSyntax>(root, context.Span, out var declaration)) | ||
return; | ||
|
||
var document = context.Document; | ||
var diagnostic = context.Diagnostics[0]; | ||
var action = CreateCodeAction(CodeFixResources.URA0053CodeFixTitle, ct => CreatePropertyDeclaration(document, declaration, ct), diagnostic.Id); | ||
context.RegisterCodeFix(action, diagnostic); | ||
} | ||
|
||
private static async Task<Document> CreatePropertyDeclaration(Document document, FieldDeclarationSyntax fieldDeclaration, CancellationToken cancellationToken) | ||
{ | ||
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); | ||
var backingField = fieldDeclaration.Declaration.Variables.First().Identifier.ValueText; | ||
var type = fieldDeclaration.Declaration.Type; | ||
var attribute = fieldDeclaration.AttributeLists.SelectMany(w => w.Attributes) | ||
.First(w => UdonSharpBehaviourUtility.PrettyTypeName(semanticModel.GetSymbolInfo(w).Symbol) == UdonConstants.UdonSharpFieldChangeCallbackFullName); | ||
var targetProperty = attribute.ArgumentList.Arguments.First().Expression.ParseValue(); | ||
|
||
var modifiers = SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword)); | ||
var getter = SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration, default, default, SyntaxFactory.ArrowExpressionClause(SyntaxFactory.IdentifierName(backingField))) | ||
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)); | ||
var setterBlock = SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, SyntaxFactory.IdentifierName(backingField), SyntaxFactory.IdentifierName("value")); | ||
var setter = SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration, SyntaxFactory.Block(SyntaxFactory.ExpressionStatement(setterBlock))); | ||
var accessors = SyntaxFactory.List(new[] { getter, setter }); | ||
var property = SyntaxFactory.PropertyDeclaration(type, targetProperty).WithModifiers(modifiers).WithAccessorList(SyntaxFactory.AccessorList(accessors)); | ||
|
||
var oldNode = fieldDeclaration.FirstAncestorOrSelf<ClassDeclarationSyntax>(); | ||
var newNode = oldNode.AddMembers(property); | ||
|
||
return await document.ReplaceNodeAsync(oldNode, newNode, cancellationToken).ConfigureAwait(false); | ||
} | ||
} | ||
} |
84 changes: 84 additions & 0 deletions
84
...bbit.Analyzer.CodeFixes/NotAllowDirectlySetValueWithFieldChangeCallbackCodeFixProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
using System.Collections.Generic; | ||
using System.Collections.Immutable; | ||
using System.Composition; | ||
using System.Linq; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CodeFixes; | ||
using Microsoft.CodeAnalysis.CSharp; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
|
||
using UdonRabbit.Analyzer.Abstractions; | ||
using UdonRabbit.Analyzer.Extensions; | ||
using UdonRabbit.Analyzer.Udon; | ||
|
||
namespace UdonRabbit.Analyzer | ||
{ | ||
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(NotAllowDirectlySetValueWithFieldChangeCallbackCodeFixProvider))] | ||
[Shared] | ||
public class NotAllowDirectlySetValueWithFieldChangeCallbackCodeFixProvider : CodeFixProviderBase | ||
{ | ||
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(NotAllowDirectlySetValueWithFieldChangeCallback.ComponentId); | ||
|
||
public override async Task RegisterCodeFixesAsync(CodeFixContext context) | ||
{ | ||
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); | ||
if (!TryFindFirstAncestorOrSelf<AssignmentExpressionSyntax>(root, context.Span, out var assignment)) | ||
return; | ||
|
||
var document = context.Document; | ||
var diagnostic = context.Diagnostics[0]; | ||
var action = CreateCodeAction(CodeFixResources.URA0052CodeFixTitle, ct => UsePropertyAssignment(document, assignment, ct), diagnostic.Id); | ||
context.RegisterCodeFix(action, diagnostic); | ||
} | ||
|
||
private static async Task<Document> UsePropertyAssignment(Document document, AssignmentExpressionSyntax assignment, CancellationToken cancellationToken) | ||
{ | ||
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); | ||
var memberAccess = assignment.Left as MemberAccessExpressionSyntax; | ||
if (memberAccess == null) | ||
return document; | ||
|
||
var symbol = ModelExtensions.GetSymbolInfo(semanticModel, memberAccess); | ||
if (symbol.Symbol is not IFieldSymbol field) | ||
return document; | ||
|
||
var attributes = field.GetAttributes(); | ||
var attribute = attributes.First(w => w.AttributeClass.ToClassString() == UdonConstants.UdonSharpFieldChangeCallbackFullName); | ||
var reference = await attribute.ApplicationSyntaxReference.GetSyntaxAsync(cancellationToken).ConfigureAwait(false) as AttributeSyntax; | ||
if (reference == null) | ||
return document; | ||
|
||
var targetProperty = reference.ArgumentList.Arguments[0].Expression.ParseValue(); | ||
var t = semanticModel.GetTypeInfo(memberAccess.Expression); | ||
var members = t.Type.GetMembers(targetProperty); | ||
var property = members.FirstOrDefault(w => w is IPropertySymbol p && p.Name == targetProperty) as IPropertySymbol; | ||
if (property == null) | ||
return document; | ||
|
||
if (property.SetMethod is { DeclaredAccessibility: Accessibility.Public }) | ||
{ | ||
var oldNode = assignment.FirstAncestorOrSelf<AssignmentExpressionSyntax>(); | ||
var newNode = assignment.WithLeft(SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, memberAccess.Expression, SyntaxFactory.IdentifierName(targetProperty))); | ||
|
||
return await document.ReplaceNodeAsync(oldNode, newNode, cancellationToken).ConfigureAwait(false); | ||
} | ||
else | ||
{ | ||
var oldNode = assignment.FirstAncestorOrSelf<AssignmentExpressionSyntax>(); | ||
|
||
var ma = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, memberAccess.Expression, SyntaxFactory.IdentifierName("SetProgramVariable")); | ||
var arguments = new List<ArgumentSyntax> | ||
{ | ||
SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(memberAccess.Name.Identifier.ValueText))), | ||
SyntaxFactory.Argument(assignment.Right) | ||
}; | ||
var newNode = SyntaxFactory.InvocationExpression(ma, SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(arguments))); | ||
|
||
return await document.ReplaceNodeAsync(oldNode, newNode, cancellationToken).ConfigureAwait(false); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
81 changes: 81 additions & 0 deletions
81
Source/UdonRabbit.Analyzer.Test/InvalidTargetPropertyForFieldChangeCallbackTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
using System.Threading.Tasks; | ||
|
||
using Microsoft.CodeAnalysis; | ||
|
||
using UdonRabbit.Analyzer.Test.Infrastructure; | ||
|
||
using Xunit; | ||
|
||
namespace UdonRabbit.Analyzer.Test | ||
{ | ||
public class InvalidTargetPropertyForFieldChangeCallbackTest : CodeFixVerifier<InvalidTargetPropertyForFieldChangeCallback, InvalidTargetPropertyForFieldChangeCallbackCodeFixProvider> | ||
{ | ||
[Fact] | ||
public async Task UdonSharpBehaviourSpecifyDeclaredPropertyInFieldChangeCallbackHasNoDiagnosticsReport() | ||
{ | ||
const string source = @" | ||
using UdonSharp; | ||
namespace UdonRabbit | ||
{ | ||
public class TestBehaviour : UdonSharpBehaviour | ||
{ | ||
[FieldChangeCallback(""Property"")] | ||
public string _bkProperty; | ||
public string Property { get; set; } | ||
} | ||
} | ||
"; | ||
|
||
DisableVerifierOn("0.20.0", Comparision.LesserThan); | ||
await VerifyAnalyzerAsync(source); | ||
} | ||
|
||
[Fact] | ||
public async Task UdonSharpBehaviourSpecifyNoDeclaredPropertyInFieldChangeCallbackHasDiagnosticsReport() | ||
{ | ||
var diagnostic = ExpectDiagnostic(InvalidTargetPropertyForFieldChangeCallback.ComponentId) | ||
.WithSeverity(DiagnosticSeverity.Error) | ||
.WithArguments("TestBehaviour"); | ||
|
||
const string source = @" | ||
using UdonSharp; | ||
namespace UdonRabbit | ||
{ | ||
public class TestBehaviour : UdonSharpBehaviour | ||
{ | ||
[|[FieldChangeCallback(""Property"")] | ||
public string _bkProperty;|] | ||
} | ||
} | ||
"; | ||
|
||
const string newSource = @" | ||
using UdonSharp; | ||
namespace UdonRabbit | ||
{ | ||
public class TestBehaviour : UdonSharpBehaviour | ||
{ | ||
[FieldChangeCallback(""Property"")] | ||
public string _bkProperty; | ||
public string Property | ||
{ | ||
get => _bkProperty; | ||
set | ||
{ | ||
_bkProperty = value; | ||
} | ||
} | ||
} | ||
} | ||
"; | ||
|
||
DisableVerifierOn("0.20.0", Comparision.LesserThan); | ||
await VerifyCodeFixAsync(source, new[] { diagnostic }, newSource); | ||
} | ||
} | ||
} |
Oops, something went wrong.