-
Notifications
You must be signed in to change notification settings - Fork 97
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1545 from riganti/fix/tobrowserlocaltime-issues
Added `UnsupportedCallSiteAttribute` and an analyzer
- Loading branch information
Showing
14 changed files
with
284 additions
and
4 deletions.
There are no files selected for viewing
95 changes: 95 additions & 0 deletions
95
src/Analyzers/Analyzers.Tests/ApiUsage/UnsupportedCallSiteAttributeTests.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,95 @@ | ||
using System.Threading.Tasks; | ||
using DotVVM.Analyzers.ApiUsage; | ||
using Xunit; | ||
using VerifyCS = DotVVM.Analyzers.Tests.CSharpAnalyzerVerifier< | ||
DotVVM.Analyzers.ApiUsage.UnsupportedCallSiteAttributeAnalyzer>; | ||
|
||
namespace DotVVM.Analyzers.Tests.ApiUsage | ||
{ | ||
public class UnsupportedCallSiteAttributeTests | ||
{ | ||
[Fact] | ||
public async Task Test_NoDiagnostics_InvokeMethod_WithoutUnsupportedCallSiteAttribute() | ||
{ | ||
var test = @" | ||
using System; | ||
using System.IO; | ||
namespace ConsoleApplication1 | ||
{ | ||
public class RegularClass | ||
{ | ||
public void Target() | ||
{ | ||
} | ||
public void CallSite() | ||
{ | ||
Target(); | ||
} | ||
} | ||
}"; | ||
|
||
await VerifyCS.VerifyAnalyzerAsync(test); | ||
} | ||
|
||
[Fact] | ||
public async Task Test_Warning_InvokeMethod_WithUnsupportedCallSiteAttribute() | ||
{ | ||
await VerifyCS.VerifyAnalyzerAsync(@" | ||
using System; | ||
using System.IO; | ||
using DotVVM.Framework.CodeAnalysis; | ||
namespace ConsoleApplication1 | ||
{ | ||
public class RegularClass | ||
{ | ||
[UnsupportedCallSite(CallSiteType.ServerSide)] | ||
public void Target() | ||
{ | ||
} | ||
public void CallSite() | ||
{ | ||
{|#0:Target()|}; | ||
} | ||
} | ||
}", | ||
|
||
VerifyCS.Diagnostic(UnsupportedCallSiteAttributeAnalyzer.DoNotInvokeMethodFromUnsupportedCallSite) | ||
.WithLocation(0).WithArguments("Target", string.Empty)); | ||
} | ||
|
||
[Fact] | ||
public async Task Test_Warning_InvokeMethod_WithUnsupportedCallSiteAttribute_WithReason() | ||
{ | ||
await VerifyCS.VerifyAnalyzerAsync(@" | ||
using System; | ||
using System.IO; | ||
using DotVVM.Framework.CodeAnalysis; | ||
namespace ConsoleApplication1 | ||
{ | ||
public class RegularClass | ||
{ | ||
[UnsupportedCallSite(CallSiteType.ServerSide, ""REASON"")] | ||
public void Target() | ||
{ | ||
} | ||
public void CallSite() | ||
{ | ||
{|#0:Target()|}; | ||
} | ||
} | ||
}", | ||
|
||
VerifyCS.Diagnostic(UnsupportedCallSiteAttributeAnalyzer.DoNotInvokeMethodFromUnsupportedCallSite) | ||
.WithLocation(0).WithArguments("Target", "due to: \"REASON\"")); | ||
} | ||
} | ||
} |
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
62 changes: 62 additions & 0 deletions
62
src/Analyzers/Analyzers/ApiUsage/UnsupportedCallSiteAttributeAnalyzer.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,62 @@ | ||
using System.Collections.Immutable; | ||
using System.Linq; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
using Microsoft.CodeAnalysis.Operations; | ||
|
||
namespace DotVVM.Analyzers.ApiUsage | ||
{ | ||
[DiagnosticAnalyzer(LanguageNames.CSharp)] | ||
public sealed class UnsupportedCallSiteAttributeAnalyzer : DiagnosticAnalyzer | ||
{ | ||
private static readonly LocalizableResourceString unsupportedCallSiteTitle = new(nameof(Resources.ApiUsage_UnsupportedCallSite_Title), Resources.ResourceManager, typeof(Resources)); | ||
private static readonly LocalizableResourceString unsupportedCallSiteMessage = new(nameof(Resources.ApiUsage_UnsupportedCallSite_Message), Resources.ResourceManager, typeof(Resources)); | ||
private static readonly LocalizableResourceString unsupportedCallSiteDescription = new(nameof(Resources.ApiUsage_UnsupportedCallSite_Description), Resources.ResourceManager, typeof(Resources)); | ||
private const string unsupportedCallSiteAttributeMetadataName = "DotVVM.Framework.CodeAnalysis.UnsupportedCallSiteAttribute"; | ||
private const int callSiteTypeServerUnderlyingValue = 0; | ||
|
||
public static DiagnosticDescriptor DoNotInvokeMethodFromUnsupportedCallSite = new( | ||
DotvvmDiagnosticIds.DoNotInvokeMethodFromUnsupportedCallSiteRuleId, | ||
unsupportedCallSiteTitle, | ||
unsupportedCallSiteMessage, | ||
DiagnosticCategory.ApiUsage, | ||
DiagnosticSeverity.Warning, | ||
isEnabledByDefault: true, | ||
unsupportedCallSiteDescription); | ||
|
||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics | ||
=> ImmutableArray.Create(DoNotInvokeMethodFromUnsupportedCallSite); | ||
|
||
public override void Initialize(AnalysisContext context) | ||
{ | ||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); | ||
context.EnableConcurrentExecution(); | ||
|
||
context.RegisterOperationAction(context => | ||
{ | ||
var unsupportedCallSiteAttribute = context.Compilation.GetTypeByMetadataName(unsupportedCallSiteAttributeMetadataName); | ||
if (unsupportedCallSiteAttribute is null) | ||
return; | ||
|
||
if (context.Operation is IInvocationOperation invocation) | ||
{ | ||
var method = invocation.TargetMethod; | ||
var attribute = method.GetAttributes().FirstOrDefault(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, unsupportedCallSiteAttribute)); | ||
if (attribute is null || !attribute.ConstructorArguments.Any()) | ||
return; | ||
|
||
if (attribute.ConstructorArguments.First().Value is not int callSiteType || callSiteTypeServerUnderlyingValue != callSiteType) | ||
return; | ||
|
||
var reason = (string?)attribute.ConstructorArguments.Skip(1).First().Value; | ||
context.ReportDiagnostic( | ||
Diagnostic.Create( | ||
DoNotInvokeMethodFromUnsupportedCallSite, | ||
invocation.Syntax.GetLocation(), | ||
invocation.TargetMethod.Name, | ||
(reason != null) ? $"due to: \"{reason}\"" : string.Empty)); | ||
} | ||
}, OperationKind.Invocation); | ||
} | ||
} | ||
} |
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
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
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,8 @@ | ||
namespace DotVVM.Framework.CodeAnalysis | ||
{ | ||
public enum CallSiteType | ||
{ | ||
ServerSide, | ||
ClientSide | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
src/Framework/Core/CodeAnalysis/UnsupportedCallSiteAttribute.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,17 @@ | ||
using System; | ||
|
||
namespace DotVVM.Framework.CodeAnalysis | ||
{ | ||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false)] | ||
public class UnsupportedCallSiteAttribute : Attribute | ||
{ | ||
public readonly CallSiteType Type; | ||
public readonly string? Reason; | ||
|
||
public UnsupportedCallSiteAttribute(CallSiteType type, string? reason = null) | ||
{ | ||
Type = type; | ||
Reason = reason; | ||
} | ||
} | ||
} |
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
43 changes: 43 additions & 0 deletions
43
src/Framework/Framework/Compilation/ControlTree/UnsupportedCallSiteCheckingVisitor.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,43 @@ | ||
using System; | ||
using System.Linq; | ||
using System.Linq.Expressions; | ||
using System.Reflection; | ||
using DotVVM.Framework.CodeAnalysis; | ||
using DotVVM.Framework.Binding.Expressions; | ||
using DotVVM.Framework.Compilation.ControlTree.Resolved; | ||
|
||
namespace DotVVM.Framework.Compilation.ControlTree | ||
{ | ||
public class UnsupportedCallSiteCheckingVisitor : ResolvedControlTreeVisitor | ||
{ | ||
class ExpressionInspectingVisitor : ExpressionVisitor | ||
{ | ||
public event Action<MethodInfo>? InvalidCallSiteDetected; | ||
|
||
protected override Expression VisitMethodCall(MethodCallExpression node) | ||
{ | ||
if (node.Method.IsDefined(typeof(UnsupportedCallSiteAttribute))) | ||
{ | ||
var callSiteAttr = node.Method.CustomAttributes.FirstOrDefault(a => a.AttributeType == typeof(UnsupportedCallSiteAttribute))!; | ||
if (callSiteAttr.ConstructorArguments.Any() && callSiteAttr.ConstructorArguments.First().Value is int type && type == (int)CallSiteType.ServerSide) | ||
InvalidCallSiteDetected?.Invoke(node.Method); | ||
} | ||
|
||
return base.VisitMethodCall(node); | ||
} | ||
} | ||
|
||
public override void VisitBinding(ResolvedBinding binding) | ||
{ | ||
base.VisitBinding(binding); | ||
if (binding.Binding is not ResourceBindingExpression and not CommandBindingExpression) | ||
return; | ||
|
||
var expressionVisitor = new ExpressionInspectingVisitor(); | ||
expressionVisitor.InvalidCallSiteDetected += method => | ||
binding.DothtmlNode?.AddWarning($"Evaluation of method \"{method.Name}\" on server-side may yield unexpected results."); | ||
|
||
expressionVisitor.Visit(binding.Expression); | ||
} | ||
} | ||
} |
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