Skip to content

Commit 009d481

Browse files
authored
Merge pull request kekekeks#133 from MrJul/feature/experimental-attribute
Report diagnostics for ExperimentalAttribute
2 parents 6ff4c3e + acc1f85 commit 009d481

3 files changed

Lines changed: 184 additions & 22 deletions

File tree

src/XamlX/Transform/TransformerConfiguration.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ class XamlTypeWellKnownTypes
228228
public IXamlType IFormatProvider { get; }
229229
public IXamlType Delegate { get; }
230230
public IXamlType ObsoleteAttribute { get; }
231+
public IXamlType? ExperimentalAttribute { get; }
231232

232233
[UnconditionalSuppressMessage("Trimming", "IL2122", Justification = TrimmingMessages.TypeInCoreAssembly)]
233234
public XamlTypeWellKnownTypes(IXamlTypeSystem typeSystem)
@@ -247,6 +248,7 @@ public XamlTypeWellKnownTypes(IXamlTypeSystem typeSystem)
247248
NullableT = typeSystem.GetType("System.Nullable`1");
248249
Delegate = typeSystem.GetType("System.Delegate");
249250
ObsoleteAttribute = typeSystem.GetType("System.ObsoleteAttribute");
251+
ExperimentalAttribute = typeSystem.FindType("System.Diagnostics.CodeAnalysis.ExperimentalAttribute");
250252
}
251253
}
252254
}

src/XamlX/Transform/Transformers/ObsoleteWarningsTransformer.cs

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System;
2-
using System.Collections;
32
using System.Collections.Generic;
43
using System.Linq;
54
using XamlX.Ast;
@@ -15,38 +14,63 @@ class ObsoleteWarningsTransformer : IXamlAstTransformer
1514
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
1615
{
1716
var obsoleteAttributeType = context.Configuration.WellKnownTypes.ObsoleteAttribute;
17+
var experimentalAttributeType = context.Configuration.WellKnownTypes.ExperimentalAttribute;
1818

1919
if (node is XamlAstObjectNode ctorNode
20-
&& FindAttr(ctorNode.Type.GetClrType().CustomAttributes) is { } typeAttr)
20+
&& FindAttr(ctorNode.Type.GetClrType().CustomAttributes) is var (typeAttr, typeDiagnostic))
2121
{
2222
var type = ctorNode.Type.GetClrType();
23-
ReportObsolete(type.Name, typeAttr);
23+
Report(type.Name, typeAttr, typeDiagnostic);
2424
}
2525
else if (node is XamlAstXamlPropertyValueNode propNode
26-
&& FindAttr(propNode.Property.GetClrProperty().CustomAttributes) is { } propAttr)
26+
&& FindAttr(propNode.Property.GetClrProperty().CustomAttributes) is var (propAttr, propDiagnostic))
2727
{
2828
var prop = propNode.Property.GetClrProperty();
29-
ReportObsolete($"{prop.Name}.{prop.Name}", propAttr);
29+
Report($"{prop.DeclaringType.Name}.{prop.Name}", propAttr, propDiagnostic);
3030
}
3131
else if (node is XamlStaticExtensionNode staticExt)
3232
{
33-
var staticAttr = staticExt.ResolveMember(false) switch
33+
var member = staticExt.ResolveMember(false);
34+
var result = member switch
3435
{
3536
IXamlField field => FindAttr(field.CustomAttributes),
3637
IXamlProperty { Getter: not null } prop => FindAttr(prop.CustomAttributes.Concat(prop.Getter.CustomAttributes)),
3738
_ => null
3839
};
39-
if (staticAttr is not null)
40+
if (result is var (staticAttr, staticDiagnostic))
4041
{
41-
ReportObsolete($"{staticExt.Type.GetClrType().Name}.{staticExt.Member}", staticAttr);
42+
Report($"{member!.DeclaringType.Name}.{member.Name}", staticAttr, staticDiagnostic);
4243
}
4344
}
4445

4546
return node;
4647

47-
IXamlCustomAttribute? FindAttr(IEnumerable<IXamlCustomAttribute> attributes)
48+
(IXamlCustomAttribute Attribute, DiagnosticType DiagnosticType)? FindAttr(IEnumerable<IXamlCustomAttribute> attributes)
4849
{
49-
return attributes.FirstOrDefault(a => a.Type.Equals(obsoleteAttributeType));
50+
foreach (var attribute in attributes)
51+
{
52+
if (attribute.Type.Equals(obsoleteAttributeType))
53+
return (attribute, DiagnosticType.Obsolete);
54+
if (experimentalAttributeType is not null && attribute.Type.Equals(experimentalAttributeType))
55+
return (attribute, DiagnosticType.Experimental);
56+
}
57+
58+
return null;
59+
}
60+
61+
void Report(string member, IXamlCustomAttribute attribute, DiagnosticType diagnosticType)
62+
{
63+
switch (diagnosticType)
64+
{
65+
case DiagnosticType.Obsolete:
66+
ReportObsolete(member, attribute);
67+
break;
68+
case DiagnosticType.Experimental:
69+
ReportExperimental(member, attribute);
70+
break;
71+
default:
72+
throw new ArgumentOutOfRangeException(nameof(diagnosticType), diagnosticType, null);
73+
}
5074
}
5175

5276
void ReportObsolete(string member, IXamlCustomAttribute attribute)
@@ -64,5 +88,27 @@ void ReportObsolete(string member, IXamlCustomAttribute attribute)
6488
isError ? XamlDiagnosticSeverity.Error : XamlDiagnosticSeverity.Warning,
6589
title, node);
6690
}
91+
92+
void ReportExperimental(string member, IXamlCustomAttribute attribute)
93+
{
94+
if (attribute.Parameters.FirstOrDefault() is not string diagnosticId)
95+
return;
96+
97+
attribute.Properties.TryGetValue("Message", out var messageObject);
98+
var message = messageObject as string;
99+
100+
var title = string.IsNullOrEmpty(message) ?
101+
$"'{member}' is for evaluation purposes only and is subject to change or removal in future updates." :
102+
$"'{member}' is for evaluation purposes only and is subject to change or removal in future updates: '{message}'.";
103+
104+
var code = context.Configuration.DiagnosticsHandler.CodeMappings(diagnosticId);
105+
context.ReportDiagnostic(code, XamlDiagnosticSeverity.Warning, title, node);
106+
}
107+
}
108+
109+
private enum DiagnosticType
110+
{
111+
Obsolete,
112+
Experimental
67113
}
68114
}
Lines changed: 126 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
using System;
2+
using System.Diagnostics.CodeAnalysis;
23
using XamlX;
34
using Xunit;
45

56
namespace XamlParserTests;
67

7-
[Obsolete("StandardWarningsTestsClass is obsolete")]
8-
public class StandardWarningsTestsClass
8+
[Obsolete("ObsoleteClass is obsolete")]
9+
public class ObsoleteClass
910
{
1011
[Obsolete("ObjectProperty is obsolete")]
1112
public object? ObjectProperty { get; set; }
@@ -14,22 +15,135 @@ public class StandardWarningsTestsClass
1415
public static object StaticProp { get; } = "StaticPropValue";
1516
}
1617

18+
#if NET8_0_OR_GREATER && !USE_NETSTANDARD_BUILD
19+
20+
[Experimental("FOO123")]
21+
public class ExperimentalClass
22+
{
23+
[Experimental("FOO123")]
24+
public object? ObjectProperty { get; set; }
25+
26+
[Experimental("FOO123")]
27+
public static object StaticProp { get; } = "StaticPropValue";
28+
}
29+
30+
#endif
31+
32+
#if NET10_0_OR_GREATER && !USE_NETSTANDARD_BUILD
33+
34+
[Experimental("FOO123", Message = "ExperimentalClass is experimental")]
35+
public class ExperimentalWithMessageClass
36+
{
37+
[Experimental("FOO123", Message = "ObjectProperty is experimental")]
38+
public object? ObjectProperty { get; set; }
39+
40+
[Experimental("FOO123", Message = "StaticProp is experimental")]
41+
public static object StaticProp { get; } = "StaticPropValue";
42+
}
43+
44+
#endif
45+
1746
public class StandardWarningsTests : CompilerTestBase
1847
{
48+
private static void VerifyDiagnostic(XamlDiagnostic[] diagnostics, string title, string code, XamlDiagnosticSeverity severity)
49+
=> Assert.Contains(diagnostics, d => d.Title == title && d.Code == code && d.Severity == severity);
50+
51+
[Fact]
52+
public void Obsolete_Is_Reported()
53+
{
54+
Transform(@"
55+
<ObsoleteClass
56+
xmlns='test'
57+
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
58+
>
59+
<ObsoleteClass.ObjectProperty><x:Static Member='ObsoleteClass.StaticProp'/></ObsoleteClass.ObjectProperty>
60+
</ObsoleteClass>");
61+
62+
var diagnostics = Diagnostics.ToArray();
63+
64+
VerifyDiagnostic(
65+
diagnostics,
66+
"'ObsoleteClass' is obsolete: ObsoleteClass is obsolete",
67+
"Obsolete",
68+
XamlDiagnosticSeverity.Warning);
69+
VerifyDiagnostic(
70+
diagnostics,
71+
"'ObsoleteClass.ObjectProperty' is obsolete: ObjectProperty is obsolete",
72+
"Obsolete",
73+
XamlDiagnosticSeverity.Warning);
74+
VerifyDiagnostic(
75+
diagnostics,
76+
"'ObsoleteClass.StaticProp' is obsolete: StaticProp is obsolete",
77+
"Obsolete",
78+
XamlDiagnosticSeverity.Error);
79+
}
80+
81+
#if NET8_0_OR_GREATER && !USE_NETSTANDARD_BUILD
82+
1983
[Fact]
20-
public void Static_Extension_Resolves_Values()
84+
public void Experimental_Is_Reported()
2185
{
22-
Transform($@"
23-
<StandardWarningsTestsClass
86+
Transform(@"
87+
<ExperimentalClass
2488
xmlns='test'
2589
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
2690
>
27-
<StandardWarningsTestsClass.ObjectProperty><x:Static Member='StandardWarningsTestsClass.StaticProp'/></StandardWarningsTestsClass.ObjectProperty>
28-
</StandardWarningsTestsClass>");
91+
<ExperimentalClass.ObjectProperty><x:Static Member='ExperimentalClass.StaticProp'/></ExperimentalClass.ObjectProperty>
92+
</ExperimentalClass>");
2993

30-
var obsoletes = Diagnostics.ToArray();
31-
Assert.Contains(obsoletes, d => d.Title.Contains("StandardWarningsTestsClass is obsolete") && d is { Code: "Obsolete", Severity: XamlDiagnosticSeverity.Warning });
32-
Assert.Contains(obsoletes, d => d.Title.Contains("ObjectProperty is obsolete") && d is { Code: "Obsolete", Severity: XamlDiagnosticSeverity.Warning });
33-
Assert.Contains(obsoletes, d => d.Title.Contains("StaticProp is obsolete") && d is { Code: "Obsolete", Severity: XamlDiagnosticSeverity.Error });
94+
var diagnostics = Diagnostics.ToArray();
95+
96+
VerifyDiagnostic(
97+
diagnostics,
98+
"'ExperimentalClass' is for evaluation purposes only and is subject to change or removal in future updates.",
99+
"FOO123",
100+
XamlDiagnosticSeverity.Warning);
101+
VerifyDiagnostic(
102+
diagnostics,
103+
"'ExperimentalClass.ObjectProperty' is for evaluation purposes only and is subject to change or removal in future updates.",
104+
"FOO123",
105+
XamlDiagnosticSeverity.Warning);
106+
VerifyDiagnostic(
107+
diagnostics,
108+
"'ExperimentalClass.StaticProp' is for evaluation purposes only and is subject to change or removal in future updates.",
109+
"FOO123",
110+
XamlDiagnosticSeverity.Warning);
34111
}
35-
}
112+
113+
#endif
114+
115+
#if NET10_0_OR_GREATER && !USE_NETSTANDARD_BUILD
116+
117+
[Fact]
118+
public void Experimental_With_Message_Is_Reported()
119+
{
120+
Transform(@"
121+
<ExperimentalWithMessageClass
122+
xmlns='test'
123+
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
124+
>
125+
<ExperimentalWithMessageClass.ObjectProperty><x:Static Member='ExperimentalWithMessageClass.StaticProp'/></ExperimentalWithMessageClass.ObjectProperty>
126+
</ExperimentalWithMessageClass>");
127+
128+
var diagnostics = Diagnostics.ToArray();
129+
130+
VerifyDiagnostic(
131+
diagnostics,
132+
"'ExperimentalWithMessageClass' is for evaluation purposes only and is subject to change or removal in future updates: 'ExperimentalClass is experimental'.",
133+
"FOO123",
134+
XamlDiagnosticSeverity.Warning);
135+
VerifyDiagnostic(
136+
diagnostics,
137+
"'ExperimentalWithMessageClass.ObjectProperty' is for evaluation purposes only and is subject to change or removal in future updates: 'ObjectProperty is experimental'.",
138+
"FOO123",
139+
XamlDiagnosticSeverity.Warning);
140+
VerifyDiagnostic(
141+
diagnostics,
142+
"'ExperimentalWithMessageClass.StaticProp' is for evaluation purposes only and is subject to change or removal in future updates: 'StaticProp is experimental'.",
143+
"FOO123",
144+
XamlDiagnosticSeverity.Warning);
145+
}
146+
147+
#endif
148+
149+
}

0 commit comments

Comments
 (0)