Browse Source
* Add diagnostics support to the Avalonia.Build.Tasks * HostApp and generators build fix * Diagnostics support in Avalonia XAML * Support multiple style selector errors at once * Improve avalonia intrinsics error handling + add tests * Add CompiledBindings multiple errors tests * Fix name generator * Make AvaloniaXamlIlDuplicateSettersChecker a warning * Fix Style_Parser_Throws_For_Duplicate_Setter test * Make XamlLoaderUnreachable respect warnings settings * Add AvaloniaXamlIlStyleValidatorTransformer * Throw more specific exceptions instead of XamlParseException * Get rid of XamlXDiagnosticCode to simplify diagnostics code * Simplify XAML exceptions by avoiding DiagnosticCode in them * Simplify XamlCompilerDiagnosticsFilter * Don't use AvaloniaXamlDiagnosticCodes in Avalonia.Generators * Fix some error handlings in compiler task * Update editor config for in-solution analysis * Update XamlX * Fix missing document path * Avoid Description field usage * Add AvaloniaXamlVerboseExceptions property and make exception formatting customizable * Make Avalonia.NameGenerator not crash if there are XAML errors, members should still be generated * Update tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleIncludeTests.cs --------- Co-authored-by: Jumar Macato <16554748+jmacato@users.noreply.github.com>pull/13644/head
committed by
GitHub
48 changed files with 923 additions and 342 deletions
@ -1,14 +0,0 @@ |
|||||
namespace Avalonia.Build.Tasks |
|
||||
{ |
|
||||
public enum BuildEngineErrorCode |
|
||||
{ |
|
||||
InvalidXAML = 1, |
|
||||
DuplicateXClass = 2, |
|
||||
LegacyResmScheme = 3, |
|
||||
TransformError = 4, |
|
||||
EmitError = 4, |
|
||||
Loader = 5, |
|
||||
|
|
||||
Unknown = 9999 |
|
||||
} |
|
||||
} |
|
||||
@ -1,41 +1,110 @@ |
|||||
using System; |
using System; |
||||
|
using System.Text; |
||||
|
using System.Xml; |
||||
using Microsoft.Build.Framework; |
using Microsoft.Build.Framework; |
||||
|
using XamlX; |
||||
|
|
||||
namespace Avalonia.Build.Tasks |
namespace Avalonia.Build.Tasks; |
||||
|
|
||||
|
internal static class Extensions |
||||
{ |
{ |
||||
static class Extensions |
public static void LogError(this IBuildEngine engine, string code, string file, Exception ex, |
||||
|
int? lineNumber = null, int? linePosition = null) |
||||
{ |
{ |
||||
static string FormatErrorCode(BuildEngineErrorCode code) => FormattableString.Invariant($"AVLN:{(int)code:0000}"); |
if (lineNumber is null && linePosition is null |
||||
|
&& ex is XmlException xe) |
||||
public static void LogError(this IBuildEngine engine, BuildEngineErrorCode code, string file, Exception ex, |
|
||||
int lineNumber = 0, int linePosition = 0) |
|
||||
{ |
{ |
||||
|
lineNumber = xe.LineNumber; |
||||
|
linePosition = xe.LinePosition; |
||||
|
} |
||||
|
|
||||
#if DEBUG
|
#if DEBUG
|
||||
LogError(engine, code, file, ex.ToString(), lineNumber, linePosition); |
LogError(engine, code, file, ex.ToString(), lineNumber, linePosition); |
||||
#else
|
#else
|
||||
LogError(engine, code, file, ex.Message, lineNumber, linePosition); |
LogError(engine, code, file, ex.Message, lineNumber, linePosition); |
||||
#endif
|
#endif
|
||||
|
} |
||||
|
|
||||
|
public static void LogDiagnostic(this IBuildEngine engine, XamlDiagnostic diagnostic) |
||||
|
{ |
||||
|
var message = diagnostic.Title; |
||||
|
|
||||
|
if (diagnostic.Severity == XamlDiagnosticSeverity.None) |
||||
|
{ |
||||
|
// Skip.
|
||||
} |
} |
||||
|
else if (diagnostic.Severity == XamlDiagnosticSeverity.Warning) |
||||
public static void LogError(this IBuildEngine engine, BuildEngineErrorCode code, string file, string message, |
|
||||
int lineNumber = 0, int linePosition = 0) |
|
||||
{ |
{ |
||||
engine.LogErrorEvent(new BuildErrorEventArgs("Avalonia", FormatErrorCode(code), file ?? "", |
engine.LogWarningEvent(new BuildWarningEventArgs("Avalonia", diagnostic.Code, diagnostic.Document ?? "", |
||||
lineNumber, linePosition, lineNumber, linePosition, message, |
diagnostic.LineNumber ?? 0, diagnostic.LinePosition ?? 0, |
||||
|
diagnostic.LineNumber ?? 0, diagnostic.LinePosition ?? 0, |
||||
|
message, |
||||
"", "Avalonia")); |
"", "Avalonia")); |
||||
} |
} |
||||
|
else |
||||
public static void LogWarning(this IBuildEngine engine, BuildEngineErrorCode code, string file, string message, |
|
||||
int lineNumber = 0, int linePosition = 0) |
|
||||
{ |
{ |
||||
engine.LogWarningEvent(new BuildWarningEventArgs("Avalonia", FormatErrorCode(code), file ?? "", |
engine.LogErrorEvent(new BuildErrorEventArgs("Avalonia", diagnostic.Code, diagnostic.Document ?? "", |
||||
lineNumber, linePosition, lineNumber, linePosition, message, |
diagnostic.LineNumber ?? 0, diagnostic.LinePosition ?? 0, |
||||
|
diagnostic.LineNumber ?? 0, diagnostic.LinePosition ?? 0, |
||||
|
message, |
||||
"", "Avalonia")); |
"", "Avalonia")); |
||||
} |
} |
||||
|
} |
||||
|
|
||||
|
public static void LogError(this IBuildEngine engine, string code, string file, string message, |
||||
|
int? lineNumber = null, int? linePosition = null) |
||||
|
{ |
||||
|
engine.LogErrorEvent(new BuildErrorEventArgs("Avalonia", code, file ?? "", |
||||
|
lineNumber ?? 0, linePosition ?? 0, lineNumber ?? 0, linePosition ?? 0, |
||||
|
message, "", "Avalonia")); |
||||
|
} |
||||
|
|
||||
public static void LogMessage(this IBuildEngine engine, string message, MessageImportance imp) |
public static void LogWarning(this IBuildEngine engine, string code, string file, string message, |
||||
|
int lineNumber = 0, int linePosition = 0) |
||||
|
{ |
||||
|
engine.LogWarningEvent(new BuildWarningEventArgs("Avalonia", code, file ?? "", |
||||
|
lineNumber, linePosition, lineNumber, linePosition, message, |
||||
|
"", "Avalonia")); |
||||
|
} |
||||
|
|
||||
|
public static void LogMessage(this IBuildEngine engine, string message, MessageImportance imp) |
||||
|
{ |
||||
|
engine.LogMessageEvent(new BuildMessageEventArgs(message, "", "Avalonia", imp)); |
||||
|
} |
||||
|
|
||||
|
public static string FormatException(this Exception exception, bool verbose) |
||||
|
{ |
||||
|
if (!verbose) |
||||
{ |
{ |
||||
engine.LogMessageEvent(new BuildMessageEventArgs(message, "", "Avalonia", imp)); |
return exception.Message; |
||||
|
} |
||||
|
|
||||
|
var builder = new StringBuilder(); |
||||
|
Process(exception); |
||||
|
return builder.ToString(); |
||||
|
|
||||
|
// Inspired by https://github.com/dotnet/msbuild/blob/e6409007d3a09255431eb28af01835ce1cd316b5/src/Shared/TaskLoggingHelper.cs#L909
|
||||
|
void Process(Exception exception) |
||||
|
{ |
||||
|
if (exception is AggregateException aggregateException) |
||||
|
{ |
||||
|
foreach (Exception innerException in aggregateException.Flatten().InnerExceptions) |
||||
|
{ |
||||
|
Process(innerException); |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
do |
||||
|
{ |
||||
|
builder.Append(exception.GetType().Name); |
||||
|
builder.Append(": "); |
||||
|
builder.AppendLine(exception.Message); |
||||
|
builder.AppendLine(exception.StackTrace); |
||||
|
|
||||
|
exception = exception.InnerException; |
||||
|
} while (exception != null); |
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -0,0 +1,70 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.IO; |
||||
|
using System.Linq; |
||||
|
using System.Text.RegularExpressions; |
||||
|
using Microsoft.Build.Framework; |
||||
|
using XamlX; |
||||
|
|
||||
|
namespace Avalonia.Build.Tasks; |
||||
|
|
||||
|
// With MSBuild, we don't need to read for TreatWarningsAsErrors/WarningsAsErrors/WarningsNotAsErrors/NoWarn properties.
|
||||
|
// Just by reporting them with LogWarning MSBuild will do the rest for us.
|
||||
|
// But we still need to read EditorConfig manually.
|
||||
|
public class XamlCompilerDiagnosticsFilter |
||||
|
{ |
||||
|
private static readonly Regex s_editorConfigRegex = |
||||
|
new("""avalonia_xaml_diagnostic\.([\w\d]+)\.severity\s*=\s*(\w*)"""); |
||||
|
|
||||
|
private readonly Lazy<Dictionary<string, string>> _lazyEditorConfig; |
||||
|
|
||||
|
public XamlCompilerDiagnosticsFilter( |
||||
|
ITaskItem[]? analyzerConfigFiles) |
||||
|
{ |
||||
|
_lazyEditorConfig = new Lazy<Dictionary<string, string>>(() => ParseEditorConfigFiles(analyzerConfigFiles)); |
||||
|
} |
||||
|
|
||||
|
internal XamlDiagnosticSeverity Handle(XamlDiagnostic diagnostic) |
||||
|
{ |
||||
|
return Handle(diagnostic.Severity, diagnostic.Code); |
||||
|
} |
||||
|
|
||||
|
internal XamlDiagnosticSeverity Handle(XamlDiagnosticSeverity currentSeverity, string diagnosticCode) |
||||
|
{ |
||||
|
if (_lazyEditorConfig.Value.TryGetValue(diagnosticCode, out var severity)) |
||||
|
{ |
||||
|
return severity.ToLowerInvariant() switch |
||||
|
{ |
||||
|
"default" => currentSeverity, |
||||
|
"error" => XamlDiagnosticSeverity.Error, |
||||
|
"warning" => XamlDiagnosticSeverity.Warning, |
||||
|
_ => XamlDiagnosticSeverity.None // "suggestion", "silent", "none"
|
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
return currentSeverity; |
||||
|
} |
||||
|
|
||||
|
private Dictionary<string, string> ParseEditorConfigFiles(ITaskItem[]? analyzerConfigFiles) |
||||
|
{ |
||||
|
// Very naive EditorConfig parser, supporting minimal properties set via regex:
|
||||
|
var severities = new Dictionary<string, string>(); |
||||
|
if (analyzerConfigFiles is not null) |
||||
|
{ |
||||
|
foreach (var fileItem in analyzerConfigFiles) |
||||
|
{ |
||||
|
if (File.Exists(fileItem.ItemSpec)) |
||||
|
{ |
||||
|
var fileContent = File.ReadAllText(fileItem.ItemSpec); |
||||
|
var matches = s_editorConfigRegex.Matches(fileContent); |
||||
|
foreach (Match match in matches) |
||||
|
{ |
||||
|
severities[match.Groups[1].Value] = match.Groups[2].Value; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return severities; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,63 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers; |
||||
|
using XamlX; |
||||
|
using XamlX.Ast; |
||||
|
|
||||
|
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions; |
||||
|
|
||||
|
internal static class AvaloniaXamlDiagnosticCodes |
||||
|
{ |
||||
|
public const string Unknown = "AVLN9999"; |
||||
|
|
||||
|
// XML/XAML parsing errors 1000-1999.
|
||||
|
public const string ParseError = "AVLN1000"; |
||||
|
public const string InvalidXAML = "AVLN1001"; |
||||
|
|
||||
|
// XAML transform errors 2000-2999.
|
||||
|
public const string TransformError = "AVLN2000"; |
||||
|
public const string DuplicateXClass = "AVLN2002"; |
||||
|
public const string TypeSystemError = "AVLN2003"; |
||||
|
public const string AvaloniaIntrinsicsError = "AVLN2005"; |
||||
|
public const string BindingsError = "AVLN2100"; |
||||
|
public const string DataContextResolvingError = "AVLN2101"; |
||||
|
public const string StyleTransformError = "AVLN2200"; |
||||
|
public const string SelectorsTransformError = "AVLN2201"; |
||||
|
public const string PropertyPathError = "AVLN2202"; |
||||
|
public const string DuplicateSetterError = "AVLN2203"; |
||||
|
public const string StyleInMergedDictionaries = "AVLN2204"; |
||||
|
|
||||
|
// XAML emit errors 3000-3999.
|
||||
|
public const string EmitError = "AVLN3000"; |
||||
|
public const string XamlLoaderUnreachable = "AVLN3001"; |
||||
|
|
||||
|
// Generator specific errors 4000-4999.
|
||||
|
public const string NameGeneratorError = "AVLN4001"; |
||||
|
|
||||
|
// Reserved 5000-9998
|
||||
|
public const string Obsolete = "AVLN5001"; |
||||
|
|
||||
|
internal static string XamlXDiagnosticCodeToAvalonia(object xamlException) |
||||
|
{ |
||||
|
return xamlException switch |
||||
|
{ |
||||
|
XamlXWellKnownDiagnosticCodes wellKnownDiagnosticCodes => wellKnownDiagnosticCodes switch |
||||
|
{ |
||||
|
XamlXWellKnownDiagnosticCodes.Obsolete => Obsolete, |
||||
|
_ => throw new ArgumentOutOfRangeException() |
||||
|
}, |
||||
|
|
||||
|
XamlDataContextException => DataContextResolvingError, |
||||
|
XamlBindingsTransformException => BindingsError, |
||||
|
XamlPropertyPathException => PropertyPathError, |
||||
|
XamlStyleTransformException => StyleTransformError, |
||||
|
XamlSelectorsTransformException => SelectorsTransformError, |
||||
|
|
||||
|
XamlTransformException => TransformError, |
||||
|
XamlTypeSystemException => TypeSystemError, |
||||
|
XamlLoadException => EmitError, |
||||
|
XamlParseException => ParseError, |
||||
|
|
||||
|
_ => Unknown |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
using System.Linq; |
||||
|
using XamlX; |
||||
|
using XamlX.Ast; |
||||
|
using XamlX.Transform; |
||||
|
|
||||
|
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers; |
||||
|
|
||||
|
internal class AvaloniaXamlIlStyleValidatorTransformer : IXamlAstTransformer |
||||
|
{ |
||||
|
// See https://github.com/AvaloniaUI/Avalonia/issues/7461
|
||||
|
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) |
||||
|
{ |
||||
|
if (!(node is XamlAstObjectNode on |
||||
|
&& context.GetAvaloniaTypes().IStyle.IsAssignableFrom(on.Type.GetClrType()))) |
||||
|
return node; |
||||
|
|
||||
|
if (context.ParentNodes().FirstOrDefault() is XamlAstXamlPropertyValueNode propertyValueNode |
||||
|
&& propertyValueNode.Property.GetClrProperty() is { } clrProperty |
||||
|
&& clrProperty.Name == "MergedDictionaries" |
||||
|
&& clrProperty.DeclaringType == context.GetAvaloniaTypes().ResourceDictionary) |
||||
|
{ |
||||
|
var nodeName = on.Type.GetClrType().Name; |
||||
|
context.ReportDiagnostic(new XamlDiagnostic( |
||||
|
AvaloniaXamlDiagnosticCodes.StyleInMergedDictionaries, |
||||
|
XamlDiagnosticSeverity.Warning, |
||||
|
// Keep it single line, as MSBuild splits multiline warnings into two warnings.
|
||||
|
$"Including {nodeName} as part of MergedDictionaries will ignore any nested styles." + |
||||
|
$"Instead, you can add {nodeName} to the Styles collection on the same control or application.", |
||||
|
node)); |
||||
|
} |
||||
|
|
||||
|
return node; |
||||
|
} |
||||
|
} |
||||
@ -1,26 +0,0 @@ |
|||||
using XamlX; |
|
||||
using XamlX.Ast; |
|
||||
|
|
||||
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions; |
|
||||
|
|
||||
internal class XamlDocumentParseException : XamlParseException |
|
||||
{ |
|
||||
public string FilePath { get; } |
|
||||
|
|
||||
public XamlDocumentParseException(string path, XamlParseException parseException) |
|
||||
: base(parseException.Message, parseException.LineNumber, parseException.LinePosition) |
|
||||
{ |
|
||||
FilePath = path; |
|
||||
} |
|
||||
|
|
||||
public XamlDocumentParseException(string path, string message, IXamlLineInfo lineInfo) |
|
||||
: base(message, lineInfo.Line, lineInfo.Position) |
|
||||
{ |
|
||||
FilePath = path; |
|
||||
} |
|
||||
|
|
||||
public XamlDocumentParseException(IXamlDocumentResource document, string message, IXamlLineInfo lineInfo) |
|
||||
: this(document.FileSource?.FilePath, message, lineInfo) |
|
||||
{ |
|
||||
} |
|
||||
} |
|
||||
@ -1 +1 @@ |
|||||
Subproject commit aa2223dec1e7c70679fdb73f9d364363a0285adb |
Subproject commit b7ed273273949a5dd9f01e682ab97f61b43697ad |
||||
@ -0,0 +1,134 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Avalonia.Controls; |
||||
|
using Avalonia.Input; |
||||
|
using Avalonia.Media; |
||||
|
using Avalonia.Media.Immutable; |
||||
|
using Avalonia.Styling; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Avalonia.Markup.Xaml.UnitTests.Xaml; |
||||
|
|
||||
|
public class AvaloniaIntrinsicsTests : XamlTestBase |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void All_Intrinsics_Are_Parsed_And_Set() |
||||
|
{ |
||||
|
var xaml = @"<local:TestIntrinsicsControl
|
||||
|
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.Xaml;assembly=Avalonia.Markup.Xaml.UnitTests' |
||||
|
TimeSpanProperty='00:10:10' |
||||
|
ThicknessProperty='1 1 1 1' |
||||
|
PointProperty='15, 15' |
||||
|
VectorProperty='16.6, 16.6' |
||||
|
SizeProperty='20, 20' |
||||
|
MatrixProperty='1 0 0 1 0 0' |
||||
|
CornerRadiusProperty='4' |
||||
|
ColorProperty='#44ff11' |
||||
|
RelativePointProperty='50%, 50%' |
||||
|
GridLengthProperty='10*' |
||||
|
IBrushProperty='#44ff11' |
||||
|
TextTrimmingProperty='CharacterEllipsis' |
||||
|
TextDecorationCollectionProperty='Strikethrough' |
||||
|
WindowTransparencyLevelProperty='AcrylicBlur' |
||||
|
UriProperty='https://avaloniaui.net/'
|
||||
|
ThemeVariantProperty='Dark' |
||||
|
PointsProperty='1, 1, 2, 2' />";
|
||||
|
|
||||
|
var target = AvaloniaRuntimeXamlLoader.Parse<TestIntrinsicsControl>(xaml); |
||||
|
|
||||
|
Assert.NotNull(target); |
||||
|
Assert.Equal(new TimeSpan(0, 10, 10), target.TimeSpanProperty); |
||||
|
Assert.Equal(new Thickness(1), target.ThicknessProperty); |
||||
|
Assert.Equal(new Thickness(1), target.ThicknessProperty); |
||||
|
Assert.Equal(new Point(15, 15), target.PointProperty); |
||||
|
Assert.Equal(new Vector(16.6, 16.6), target.VectorProperty); |
||||
|
Assert.Equal(new Size(20, 20), target.SizeProperty); |
||||
|
Assert.Equal(new Matrix(1, 0, 0, 1, 0, 0), target.MatrixProperty); |
||||
|
Assert.Equal(new CornerRadius(4), target.CornerRadiusProperty); |
||||
|
Assert.Equal(Color.Parse("#44ff11"), target.ColorProperty); |
||||
|
Assert.Equal(new RelativePoint(0.5, 0.5, RelativeUnit.Relative), target.RelativePointProperty); |
||||
|
Assert.Equal(new GridLength(10, GridUnitType.Star), target.GridLengthProperty); |
||||
|
Assert.Equal(new ImmutableSolidColorBrush(Color.Parse("#44ff11")), target.IBrushProperty); |
||||
|
Assert.Equal(TextTrimming.CharacterEllipsis, target.TextTrimmingProperty); |
||||
|
Assert.Equal(TextDecorations.Strikethrough, target.TextDecorationCollectionProperty); |
||||
|
Assert.Equal(WindowTransparencyLevel.AcrylicBlur, target.WindowTransparencyLevelProperty); |
||||
|
Assert.Equal(new Uri("https://avaloniaui.net/"), target.UriProperty); |
||||
|
Assert.Equal(ThemeVariant.Dark, target.ThemeVariantProperty); |
||||
|
Assert.Equal(new[] { new Point(1, 1), new Point(2, 2) }, target.PointsProperty); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void All_Intrinsics_Report_Errors_If_Failed() |
||||
|
{ |
||||
|
var xaml = @"<local:TestIntrinsicsControl
|
||||
|
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.Xaml;assembly=Avalonia.Markup.Xaml.UnitTests' |
||||
|
TimeSpanProperty='00:00:10,1' |
||||
|
ThicknessProperty='1 1 1' |
||||
|
PointProperty='15% 15%' |
||||
|
VectorProperty='16.6. 16.6' |
||||
|
SizeProperty='20%, 20%' |
||||
|
MatrixProperty='1 0 1 0 0' |
||||
|
CornerRadiusProperty='4 1 4' |
||||
|
ColorProperty='#44ff1' |
||||
|
RelativePointProperty='50, 50%' |
||||
|
GridLengthProperty='10%' |
||||
|
PointsProperty='1, 1, 2' />";
|
||||
|
// TODO: double check why we don't throw error on other supported types. Should it be warnings?
|
||||
|
|
||||
|
var diagnostics = new List<RuntimeXamlDiagnostic>(); |
||||
|
Assert.Throws<AggregateException>(() => AvaloniaRuntimeXamlLoader.Load(new RuntimeXamlLoaderDocument(xaml), |
||||
|
new RuntimeXamlLoaderConfiguration |
||||
|
{ |
||||
|
DiagnosticHandler = diagnostic => |
||||
|
{ |
||||
|
diagnostics.Add(diagnostic); |
||||
|
return diagnostic.Severity; |
||||
|
} |
||||
|
})); |
||||
|
|
||||
|
Assert.Collection( |
||||
|
diagnostics, |
||||
|
d => AssertDiagnostic(d, "time span"), |
||||
|
d => AssertDiagnostic(d, "thickness"), |
||||
|
d => AssertDiagnostic(d, "point"), |
||||
|
d => AssertDiagnostic(d, "vector"), |
||||
|
d => AssertDiagnostic(d, "size"), |
||||
|
d => AssertDiagnostic(d, "matrix"), |
||||
|
d => AssertDiagnostic(d, "corner radius"), |
||||
|
d => AssertDiagnostic(d, "color"), |
||||
|
d => AssertDiagnostic(d, "relative point"), |
||||
|
d => AssertDiagnostic(d, "grid length"), |
||||
|
d => AssertDiagnostic(d, "points list"), |
||||
|
// Compiler attempts to parse PointsList twice - as a list and as a point.
|
||||
|
d => AssertDiagnostic(d, "point")); |
||||
|
|
||||
|
void AssertDiagnostic(RuntimeXamlDiagnostic runtimeXamlDiagnostic, string contains) |
||||
|
{ |
||||
|
Assert.Equal(RuntimeXamlDiagnosticSeverity.Error, runtimeXamlDiagnostic.Severity); |
||||
|
Assert.Contains(contains, runtimeXamlDiagnostic.Title, StringComparison.OrdinalIgnoreCase); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class TestIntrinsicsControl : Control |
||||
|
{ |
||||
|
public TimeSpan TimeSpanProperty { get; set; } |
||||
|
|
||||
|
// public FontFamily FontFamilyProperty { get; set; }
|
||||
|
public Thickness ThicknessProperty { get; set; } |
||||
|
public Point PointProperty { get; set; } |
||||
|
public Vector VectorProperty { get; set; } |
||||
|
public Size SizeProperty { get; set; } |
||||
|
public Matrix MatrixProperty { get; set; } |
||||
|
public CornerRadius CornerRadiusProperty { get; set; } |
||||
|
public Color ColorProperty { get; set; } |
||||
|
public RelativePoint RelativePointProperty { get; set; } |
||||
|
public GridLength GridLengthProperty { get; set; } |
||||
|
public IBrush IBrushProperty { get; set; } |
||||
|
public TextTrimming TextTrimmingProperty { get; set; } |
||||
|
public TextDecorationCollection TextDecorationCollectionProperty { get; set; } |
||||
|
public WindowTransparencyLevel WindowTransparencyLevelProperty { get; set; } |
||||
|
public Uri UriProperty { get; set; } |
||||
|
public ThemeVariant ThemeVariantProperty { get; set; } |
||||
|
public Points PointsProperty { get; set; } |
||||
|
} |
||||
Loading…
Reference in new issue