Skip to content

Commit

Permalink
Merge branch 'main' into release/4.1
Browse files Browse the repository at this point in the history
  • Loading branch information
tomasherceg committed Feb 6, 2023
2 parents 1bfd0d6 + 3860d30 commit 9d4b198
Show file tree
Hide file tree
Showing 88 changed files with 867 additions and 131 deletions.
@@ -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\""));
}
}
}
1 change: 1 addition & 0 deletions src/Analyzers/Analyzers/AnalyzerReleases.Unshipped.md
Expand Up @@ -6,3 +6,4 @@ Rule ID | Category | Severity | Notes
--------|----------|----------|-------
DotVVM02 | Serializability | Warning | ViewModelSerializabilityAnalyzer
DotVVM03 | Serializability | Warning | ViewModelSerializabilityAnalyzer
DotVVM04 | ApiUsage | Warning | UnsupportedCallSiteAttributeAnalyzer
@@ -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 DiagnosticDescriptor(
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);
}
}
}
1 change: 1 addition & 0 deletions src/Analyzers/Analyzers/DiagnosticCategory.cs
Expand Up @@ -8,5 +8,6 @@ internal static class DiagnosticCategory
{
public const string Serializability = nameof(Serializability);
public const string StaticCommands = nameof(StaticCommands);
public const string ApiUsage = nameof(ApiUsage);
}
}
1 change: 1 addition & 0 deletions src/Analyzers/Analyzers/DotvvmDiagnosticIds.cs
Expand Up @@ -11,5 +11,6 @@ public static class DotvvmDiagnosticIds

public const string UseSerializablePropertiesInViewModelRuleId = "DotVVM02";
public const string DoNotUseFieldsInViewModelRuleId = "DotVVM03";
public const string DoNotInvokeMethodFromUnsupportedCallSiteRuleId = "DotVVM04";
}
}
27 changes: 27 additions & 0 deletions src/Analyzers/Analyzers/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions src/Analyzers/Analyzers/Resources.resx
Expand Up @@ -117,6 +117,15 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="ApiUsage_UnsupportedCallSite_Description" xml:space="preserve">
<value>Method that declares that it should not be called on server is only meant to be invoked on client.</value>
</data>
<data name="ApiUsage_UnsupportedCallSite_Message" xml:space="preserve">
<value>Method '{0}' invocation is not supported on server {1}</value>
</data>
<data name="ApiUsage_UnsupportedCallSite_Title" xml:space="preserve">
<value>Unsupported call site</value>
</data>
<data name="Serializability_DoNotUseFields_Description" xml:space="preserve">
<value>Fields are not supported in viewmodels. Use properties to save state of viewmodels instead.</value>
</data>
Expand Down
33 changes: 0 additions & 33 deletions src/AutoUI/Annotations/ComboBoxSettingsAttribute.cs

This file was deleted.

2 changes: 1 addition & 1 deletion src/AutoUI/Annotations/DotVVM.AutoUI.Annotations.csproj
Expand Up @@ -7,7 +7,7 @@
<PackageId>DotVVM.AutoUI.Annotations</PackageId>
<Description>Annotation attributes for DotVVM AutoUI.</Description>
<PackageProjectUrl>https://www.dotvvm.com/docs/3.0/pages/community-add-ons/dotvvm-dynamic-data</PackageProjectUrl>
<PackageTags>($PackageTags);autoui;annotations;metadata;ui generation</PackageTags>
<PackageTags>$(PackageTags);autoui;annotations;metadata;ui generation</PackageTags>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.1' ">
Expand Down
2 changes: 1 addition & 1 deletion src/AutoUI/Annotations/EnabledAttribute.cs
Expand Up @@ -30,7 +30,7 @@ public class EnabledAttribute : Attribute, IConditionalFieldAttribute
public string Roles { get; set; }

/// <summary>
/// Gets or sets whether the field should be editable for authenticated or non-authenticated users, or null for both kinds (default behavior).
/// Gets or sets whether the field should be editable for authenticated or non-authenticated users, or for both (default behavior).
/// </summary>
public AuthenticationMode IsAuthenticated { get; set; }

Expand Down
2 changes: 2 additions & 0 deletions src/AutoUI/Annotations/ISelectionProvider.cs
Expand Up @@ -4,12 +4,14 @@

namespace DotVVM.AutoUI.Annotations;

/// <summary> The service providing <see cref="Selection{TKey}" /> items. Automatically used from the SelectionViewModel, unless Items are set explicitly. </summary>
public interface ISelectionProvider<TItem>
{
[AllowStaticCommand]
Task<List<TItem>> GetSelectorItems();
}

/// <summary> The service providing <see cref="Selection{TKey}" /> items. Automatically used from the SelectionViewModel, unless Items are set explicitly. </summary>
public interface ISelectionProvider<TItem, TParam>
{
[AllowStaticCommand]
Expand Down
14 changes: 14 additions & 0 deletions src/AutoUI/Annotations/README.md
@@ -0,0 +1,14 @@
# Annotations for DotVVM Auto UI

This package only contains annotations and some interfaces used to annotate classes for which [DotVVM.AutoUI](https://www.nuget.org/packages/DotVVM.AutoUI) can create forms and tables.

DotVVM.AutoUI.Annotations only depends on DotVVM.Core, and is intended to be included in non-web projects.

Attributes included:
* `VisibleAttribute`, `EnabledAttribute`
* `ComboBoxSettingsAttribute`
* `StyleAttribute` for hardcoding css classes
* `SelectionAttribute` for declaring selectable fields using components like RadioButton

Base classes included:
* `Selection` for selectable items
20 changes: 20 additions & 0 deletions src/AutoUI/Annotations/Selection.cs
@@ -1,14 +1,34 @@
namespace DotVVM.AutoUI.Annotations;

/// <summary>
/// Base class for selectable items, please prefer to derive from <see cref="Selection{TKey}" />
/// </summary>
public abstract record Selection
{
/// <summary> The label to display in the selector component </summary>
public string DisplayName { get; set; }

private protected abstract void SorryWeCannotAllowYouToInheritThisClass();
}

/// <summary>
/// Base class for selectable items. See also <see cref="SelectionAttribute" />
/// </summary>
/// <typeparam name="TKey">Type of the value (the identifier). The property labeled with [Selection(typeof(This))] will have to be of type <typeparamref name="TKey"/></typeparam>
/// <example>
/// public record ProductSelection : Selection&lt;Guid&gt;;
/// // and then ...
/// public class ProductSelectionProvider : ISelectionProvider&lt;ProductSelection&gt;
/// {
/// public Task&lt;List&lt;ProductSelection&gt;&gt; GetSelectorItems() =>
/// Task.FromResult(new() {
/// new ProductSelection() { Value = new Guid("00000000-0000-0000-0000-000000000001"), DisplayName = "First product" },
/// });
/// }
/// </example>
public abstract record Selection<TKey> : Selection
{
/// <summary> The value identifying this selection item </summary>
public TKey Value { get; set; }

private protected override void SorryWeCannotAllowYouToInheritThisClass() => throw new System.NotImplementedException("Mischief managed.");
Expand Down
1 change: 1 addition & 0 deletions src/AutoUI/Annotations/SelectionAttribute.cs
Expand Up @@ -8,6 +8,7 @@ namespace DotVVM.AutoUI.Annotations;
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class SelectionAttribute : System.Attribute
{
/// <summary> The <see cref="Selection{TKey}" /> implementation </summary>
public Type SelectionType { get; }

public SelectionAttribute(Type selectionType)
Expand Down
2 changes: 1 addition & 1 deletion src/AutoUI/Annotations/VisibleAttribute.cs
Expand Up @@ -30,7 +30,7 @@ public class VisibleAttribute : Attribute, IConditionalFieldAttribute
public string Roles { get; set; }

/// <summary>
/// Gets or sets whether the field should be visible for authenticated or non-authenticated users, or null for both kinds (default behavior).
/// Gets or sets whether the field should be visible for authenticated or non-authenticated users, or for both (default behavior).
/// </summary>
public AuthenticationMode IsAuthenticated { get; set; }

Expand Down
2 changes: 1 addition & 1 deletion src/AutoUI/Core/Controls/AutoFormBase.cs
Expand Up @@ -119,7 +119,7 @@ internal static PropertyDisplayMetadata[] GetPropertiesToDisplay(AutoUIContext c

if (property.IsDefaultLabelAllowed)
{
return new Label(id).AppendChildren(new Literal(property.GetDisplayName().ToBinding(autoUiContext)));
return new Label(id).AppendChildren(new Literal(property.GetDisplayName().ToBinding(autoUiContext.BindingService)));
}
return null;
}
Expand Down
2 changes: 1 addition & 1 deletion src/AutoUI/Core/Controls/AutoGridViewColumn.cs
Expand Up @@ -45,7 +45,7 @@ public static GridViewColumn Replace(IStyleMatchContext<AutoGridViewColumn> col)

if (props.HeaderTemplate is null && props.HeaderText is null)
{
props = props with { HeaderText = propertyMetadata.GetDisplayName().ToBinding(context) };
props = props with { HeaderText = propertyMetadata.GetDisplayName().ToBinding(context.BindingService) };
}

var control = CreateColumn(context, props, propertyMetadata);
Expand Down
2 changes: 1 addition & 1 deletion src/AutoUI/Core/Controls/BulmaForm.cs
Expand Up @@ -39,7 +39,7 @@ public DotvvmControl GetContents(FieldProps props)
}

var help = property.Description is { } description
? new HtmlGenericControl("div").AddCssClass("help").SetProperty(c => c.InnerText, description.ToBinding(context)!)
? new HtmlGenericControl("div").AddCssClass("help").SetProperty(c => c.InnerText, description.ToBinding(context.BindingService)!)
: null;
var validator = new Validator()
.AddCssClass("help is-danger")
Expand Down

0 comments on commit 9d4b198

Please sign in to comment.