Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,9 @@ dotnet_diagnostic.xUnit2020.severity = none
dotnet_diagnostic.xUnit2023.severity = none
dotnet_diagnostic.xUnit2029.severity = none

# RS0006: Roslyn experimental language API
dotnet_diagnostic.RSEXPERIMENTAL006.severity = none
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you consider putting this into a .globalconfig file instead? Then we wouldn't need separate suppressions in source-generated files.


# CSharp code style settings:
[*.cs]
# Newline settings
Expand Down
36 changes: 21 additions & 15 deletions docs/contributing/Compiler Test Plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,23 @@ This document provides guidance for thinking about language interactions and tes
- BCL (including mono) and other customer impact
- Determinism
- Loading from metadata (source vs. loaded from metadata)
- VB/F# interop
- C++/CLI interop (particularly for metadata format changes, e.g. DIMs, static abstracts in interfaces, or generic attributes)
- Performance and stress testing
- Can build VS
- Check that `Obsolete` is honored for members used in binding/lowering
- LangVersion
- IL verification (file issue on `runtime` repo as needed and track [here](https://github.com/dotnet/roslyn/issues/22872))
- Does the feature use cryptographic hashes in any way? (examples: metadata names of file-local types, extension types, assembly strong naming, PDB document table, etc.)
- Consider using non-cryptographic hash such as `XxHash128` instead.
- If you must use a cryptographic hash in the feature implementation, then use `SourceHashAlgorithms.Default`, and not any specific hash.
- A cryptographic hash must never be included in a public API name. Taking a change to the default crypto algorithm would then change public API surface, which would be enormously breaking.
- **DO NOT** allow using the value of a crypto hash in a field, method or type name
- **DO** allow using the value of a crypto hash in attribute or field values
- Any time the compiler reads in metadata containing crypto hashes, even if it's an attribute value, ensure the crypto hash algorithm name is included in the metadata (e.g. prefixing it to the hash value), so that it can be changed over time and the compiler can continue to read both metadata using both the old and new algorithms.

## Public APIs

- Public compiler APIs (including semantic model and other APIs listed below):
- GetDeclaredSymbol
- GetEnclosingSymbol
Expand All @@ -32,21 +49,10 @@ This document provides guidance for thinking about language interactions and tes
- GetOperation (`IOperation`)
- GetCFG (`ControlFlowGraph`), including a scenario with some nested conditional
- DocumentationCommentId APIs
- VB/F# interop
- C++/CLI interop (particularly for metadata format changes, e.g. DIMs, static abstracts in interfaces, or generic attributes)
- Performance and stress testing
- Can build VS
- Check that `Obsolete` is honored for members used in binding/lowering
- LangVersion
- IL verification (file issue on `runtime` repo as needed and track [here](https://github.com/dotnet/roslyn/issues/22872))

- Does the feature use cryptographic hashes in any way? (examples: metadata names of file-local types, extension types, assembly strong naming, PDB document table, etc.)
- Consider using non-cryptographic hash such as `XxHash128` instead.
- If you must use a cryptographic hash in the feature implementation, then use `SourceHashAlgorithms.Default`, and not any specific hash.
- A cryptographic hash must never be included in a public API name. Taking a change to the default crypto algorithm would then change public API surface, which would be enormously breaking.
- **DO NOT** allow using the value of a crypto hash in a field, method or type name
- **DO** allow using the value of a crypto hash in attribute or field values
- Any time the compiler reads in metadata containing crypto hashes, even if it's an attribute value, ensure the crypto hash algorithm name is included in the metadata (e.g. prefixing it to the hash value), so that it can be changed over time and the compiler can continue to read both metadata using both the old and new algorithms.
- All newly added APIs are experimental
- Tracking issue for marking APIs as non-experimental
- APIs are marked with `[Experimental(RoslynExperiments.PreviewLanguageFeatureApi, UrlFormat = @"link to tracking issue")]`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't expect backslashes in the URL

Suggested change
- APIs are marked with `[Experimental(RoslynExperiments.PreviewLanguageFeatureApi, UrlFormat = @"link to tracking issue")]`
- APIs are marked with `[Experimental(RoslynExperiments.PreviewLanguageFeatureApi, UrlFormat = "link to tracking issue")]`

- APIs have gone through API review

## Type and members

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// <auto-generated />

#nullable enable
#pragma warning disable RSEXPERIMENTAL006 // Preview language feature API

using System;
using System.Collections.Generic;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// <auto-generated />

#nullable enable
#pragma warning disable RSEXPERIMENTAL006 // Preview language feature API

using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -229,6 +230,7 @@ public partial class CSharpSyntaxVisitor<TResult>
public virtual TResult? VisitSpreadElement(SpreadElementSyntax node) => this.DefaultVisit(node);

/// <summary>Called when the visitor visits a WithElementSyntax node.</summary>
[Experimental(global::Microsoft.CodeAnalysis.RoslynExperiments.PreviewLanguageFeatureApi, UrlFormat = @"https://github.com/dotnet/roslyn/issues/82210")]
public virtual TResult? VisitWithElement(WithElementSyntax node) => this.DefaultVisit(node);

/// <summary>Called when the visitor visits a QueryExpressionSyntax node.</summary>
Expand Down Expand Up @@ -976,6 +978,7 @@ public partial class CSharpSyntaxVisitor
public virtual void VisitSpreadElement(SpreadElementSyntax node) => this.DefaultVisit(node);

/// <summary>Called when the visitor visits a WithElementSyntax node.</summary>
[Experimental(global::Microsoft.CodeAnalysis.RoslynExperiments.PreviewLanguageFeatureApi, UrlFormat = @"https://github.com/dotnet/roslyn/issues/82210")]
public virtual void VisitWithElement(WithElementSyntax node) => this.DefaultVisit(node);

/// <summary>Called when the visitor visits a QueryExpressionSyntax node.</summary>
Expand Down Expand Up @@ -1722,6 +1725,7 @@ public partial class CSharpSyntaxRewriter : CSharpSyntaxVisitor<SyntaxNode?>
public override SyntaxNode? VisitSpreadElement(SpreadElementSyntax node)
=> node.Update(VisitToken(node.OperatorToken), (ExpressionSyntax?)Visit(node.Expression) ?? throw new ArgumentNullException("expression"));

[Experimental(global::Microsoft.CodeAnalysis.RoslynExperiments.PreviewLanguageFeatureApi, UrlFormat = @"https://github.com/dotnet/roslyn/issues/82210")]
public override SyntaxNode? VisitWithElement(WithElementSyntax node)
=> node.Update(VisitToken(node.WithKeyword), (ArgumentListSyntax?)Visit(node.ArgumentList) ?? throw new ArgumentNullException("argumentList"));

Expand Down Expand Up @@ -3446,6 +3450,7 @@ public static SpreadElementSyntax SpreadElement(ExpressionSyntax expression)
=> SyntaxFactory.SpreadElement(SyntaxFactory.Token(SyntaxKind.DotDotToken), expression);

/// <summary>Creates a new WithElementSyntax instance.</summary>
[Experimental(global::Microsoft.CodeAnalysis.RoslynExperiments.PreviewLanguageFeatureApi, UrlFormat = @"https://github.com/dotnet/roslyn/issues/82210")]
public static WithElementSyntax WithElement(SyntaxToken withKeyword, ArgumentListSyntax argumentList)
{
if (withKeyword.Kind() != SyntaxKind.WithKeyword) throw new ArgumentException(nameof(withKeyword));
Expand All @@ -3454,6 +3459,7 @@ public static WithElementSyntax WithElement(SyntaxToken withKeyword, ArgumentLis
}

/// <summary>Creates a new WithElementSyntax instance.</summary>
[Experimental(global::Microsoft.CodeAnalysis.RoslynExperiments.PreviewLanguageFeatureApi, UrlFormat = @"https://github.com/dotnet/roslyn/issues/82210")]
public static WithElementSyntax WithElement(ArgumentListSyntax? argumentList = default)
=> SyntaxFactory.WithElement(SyntaxFactory.Token(SyntaxKind.WithKeyword), argumentList ?? SyntaxFactory.ArgumentList());

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// <auto-generated />

#nullable enable
#pragma warning disable RSEXPERIMENTAL006 // Preview language feature API

using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -4279,6 +4280,7 @@ public SpreadElementSyntax Update(SyntaxToken operatorToken, ExpressionSyntax ex
/// <item><description><see cref="SyntaxKind.WithElement"/></description></item>
/// </list>
/// </remarks>
[Experimental(global::Microsoft.CodeAnalysis.RoslynExperiments.PreviewLanguageFeatureApi, UrlFormat = @"https://github.com/dotnet/roslyn/issues/82210")]
public sealed partial class WithElementSyntax : CollectionElementSyntax
{
private ArgumentListSyntax? argumentList;
Expand Down
30 changes: 15 additions & 15 deletions src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
Microsoft.CodeAnalysis.CSharp.ForEachStatementInfo.DisposeAwaitableInfo.get -> Microsoft.CodeAnalysis.CSharp.AwaitExpressionInfo
Microsoft.CodeAnalysis.CSharp.ForEachStatementInfo.MoveNextAwaitableInfo.get -> Microsoft.CodeAnalysis.CSharp.AwaitExpressionInfo
Microsoft.CodeAnalysis.CSharp.SyntaxKind.WithElement = 9081 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind
[RSEXPERIMENTAL006]Microsoft.CodeAnalysis.CSharp.SyntaxKind.WithElement = 9081 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind
static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetAwaitExpressionInfo(this Microsoft.CodeAnalysis.SemanticModel? semanticModel, Microsoft.CodeAnalysis.CSharp.Syntax.LocalDeclarationStatementSyntax! awaitUsingDeclaration) -> Microsoft.CodeAnalysis.CSharp.AwaitExpressionInfo
static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetAwaitExpressionInfo(this Microsoft.CodeAnalysis.SemanticModel? semanticModel, Microsoft.CodeAnalysis.CSharp.Syntax.UsingStatementSyntax! awaitUsingStatement) -> Microsoft.CodeAnalysis.CSharp.AwaitExpressionInfo
Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.AddArgumentListArguments(params Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax![]! items) -> Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax!
Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.ArgumentList.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax!
Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken withKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax! argumentList) -> Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax!
Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.WithArgumentList(Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax! argumentList) -> Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax!
Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.WithKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken
Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.WithWithKeyword(Microsoft.CodeAnalysis.SyntaxToken withKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax!
override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitWithElement(Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax! node) -> Microsoft.CodeAnalysis.SyntaxNode?
override Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor! visitor) -> void
override Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.Accept<TResult>(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor<TResult>! visitor) -> TResult?
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.WithElement(Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax? argumentList = null) -> Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax!
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.WithElement(Microsoft.CodeAnalysis.SyntaxToken withKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax! argumentList) -> Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax!
virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitWithElement(Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax! node) -> void
virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor<TResult>.VisitWithElement(Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax! node) -> TResult?
[RSEXPERIMENTAL006]Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the SyntaxKind above bet considered experimental too?

[RSEXPERIMENTAL006]Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.AddArgumentListArguments(params Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax![]! items) -> Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax!
[RSEXPERIMENTAL006]Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.ArgumentList.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax!
[RSEXPERIMENTAL006]Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken withKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax! argumentList) -> Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax!
[RSEXPERIMENTAL006]Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.WithArgumentList(Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax! argumentList) -> Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax!
[RSEXPERIMENTAL006]Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.WithKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken
[RSEXPERIMENTAL006]Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.WithWithKeyword(Microsoft.CodeAnalysis.SyntaxToken withKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax!
[RSEXPERIMENTAL006]override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitWithElement(Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax! node) -> Microsoft.CodeAnalysis.SyntaxNode?
[RSEXPERIMENTAL006]override Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor! visitor) -> void
[RSEXPERIMENTAL006]override Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.Accept<TResult>(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor<TResult>! visitor) -> TResult?
[RSEXPERIMENTAL006]static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.WithElement(Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax? argumentList = null) -> Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax!
[RSEXPERIMENTAL006]static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.WithElement(Microsoft.CodeAnalysis.SyntaxToken withKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax! argumentList) -> Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax!
[RSEXPERIMENTAL006]virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitWithElement(Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax! node) -> void
[RSEXPERIMENTAL006]virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor<TResult>.VisitWithElement(Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax! node) -> TResult?
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Syntax/Syntax.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1839,7 +1839,7 @@
</Field>
<Field Name="Expression" Type="ExpressionSyntax" />
</Node>
<Node Name="WithElementSyntax" Base="CollectionElementSyntax">
<Node Name="WithElementSyntax" Base="CollectionElementSyntax" ExperimentalUrl="https://github.com/dotnet/roslyn/issues/82210">
<Kind Name="WithElement"/>
<Field Name="WithKeyword" Type="SyntaxToken">
<Kind Name="WithKeyword"/>
Expand Down
10 changes: 10 additions & 0 deletions src/Compilers/CSharp/Portable/Syntax/Syntax.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<xs:complexType>
<xs:attribute name="Name" type="xs:string" use="required" />
<xs:attribute name="Base" type="xs:string" use="required" />
<xs:attribute name="ExperimentalUrl" type="xs:string" use="optional" />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<xs:attribute name="ExperimentalUrl" type="xs:string" use="optional" />

Is this file maintained manually? If so, why do we need it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is maintained manually. It predates my time here, and presumably provides completion for the XML structure in Syntax.xml. If we decide we don't want it, that's ok with me, but I think that should be a separate PR. For now, I'm going to keep this as is and update appropriately.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's used for that. I think the checker also uses this to validate the structure of the XML too?

</xs:complexType>
</xs:element>
<xs:element maxOccurs="unbounded" name="AbstractNode">
Expand All @@ -34,6 +35,7 @@
</xs:sequence>
<xs:attribute name="Name" type="xs:string" use="required" />
<xs:attribute name="Type" type="xs:string" use="required" />
<xs:attribute name="ExperimentalUrl" type="xs:string" use="optional" />
<xs:attribute name="Optional" type="xs:boolean" use="optional" />
</xs:complexType>
</xs:element>
Expand All @@ -60,6 +62,7 @@
</xs:sequence>
<xs:attribute name="Name" type="xs:string" use="required" />
<xs:attribute name="Type" type="xs:string" use="required" />
<xs:attribute name="ExperimentalUrl" type="xs:string" use="optional" />
</xs:complexType>
</xs:element>
<xs:element minOccurs="0" name="Sequence">
Expand All @@ -83,6 +86,7 @@
</xs:sequence>
<xs:attribute name="Name" type="xs:string" use="required" />
<xs:attribute name="Type" type="xs:string" use="required" />
<xs:attribute name="ExperimentalUrl" type="xs:string" use="optional" />
</xs:complexType>
</xs:element>
</xs:sequence>
Expand All @@ -95,6 +99,7 @@
</xs:sequence>
<xs:attribute name="Name" type="xs:string" use="required" />
<xs:attribute name="Base" type="xs:string" use="required" />
<xs:attribute name="ExperimentalUrl" type="xs:string" use="optional" />
</xs:complexType>
</xs:element>
<xs:element maxOccurs="unbounded" name="Node">
Expand Down Expand Up @@ -131,6 +136,7 @@
<xs:attribute name="MinCount" type="xs:unsignedByte" use="optional" />
<xs:attribute name="Optional" type="xs:boolean" use="optional" />
<xs:attribute name="AllowTrailingSeparator" type="xs:boolean" use="optional" />
<xs:attribute name="ExperimentalUrl" type="xs:string" use="optional" />
</xs:complexType>
</xs:element>
<xs:element name="TypeComment">
Expand Down Expand Up @@ -170,6 +176,7 @@
<xs:attribute name="Type" type="xs:string" use="required" />
<xs:attribute name="Override" type="xs:boolean" use="optional" />
<xs:attribute name="Optional" type="xs:boolean" use="optional" />
<xs:attribute name="ExperimentalUrl" type="xs:string" use="optional" />
</xs:complexType>
</xs:element>
<xs:element minOccurs="0" name="Sequence">
Expand All @@ -182,6 +189,7 @@
<xs:complexType>
<xs:attribute name="Name" type="xs:string" use="required" />
<xs:attribute name="Type" type="xs:string" use="required" />
<xs:attribute name="ExperimentalUrl" type="xs:string" use="optional" />
</xs:complexType>
</xs:element>
</xs:sequence>
Expand All @@ -206,6 +214,7 @@
<xs:attribute name="Name" type="xs:string" use="required" />
<xs:attribute name="Type" type="xs:string" use="required" />
<xs:attribute name="Override" type="xs:boolean" use="optional" />
<xs:attribute name="ExperimentalUrl" type="xs:string" use="optional" />
</xs:complexType>
</xs:element>
</xs:sequence>
Expand All @@ -220,6 +229,7 @@
<xs:attribute name="Name" type="xs:string" use="required" />
<xs:attribute name="Base" type="xs:string" use="required" />
<xs:attribute name="SkipConvenienceFactories" type="xs:boolean" use="optional" />
<xs:attribute name="ExperimentalUrl" type="xs:string" use="optional" />
</xs:complexType>
</xs:element>
</xs:choice>
Expand Down
Loading
Loading