-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Polyfill the incremental generator ForAttributeWithMetadataName from roslyn. #70911
Merged
Merged
Changes from 5 commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
6e03dca
Add the initial roslyn files
CyrusNajmabadi 539b248
Merge remote-tracking branch 'upstream/main' into polyfill
CyrusNajmabadi e8f0a8e
IN progress
CyrusNajmabadi 1c0fbf2
Builds
CyrusNajmabadi b533f26
Use api
CyrusNajmabadi c1a76b1
ifdef
CyrusNajmabadi 2ef0717
Merge remote-tracking branch 'upstream/main' into polyfill
CyrusNajmabadi 1f35a7e
Move using outside namespace
CyrusNajmabadi f5d8137
Move to debug assert
CyrusNajmabadi be4ef0f
Optimize common cases
CyrusNajmabadi 2babd86
Merge remote-tracking branch 'upstream/main' into polyfill
CyrusNajmabadi 1cdc428
Explain if'defed regions
CyrusNajmabadi cfce553
Explain if'defed regions
CyrusNajmabadi fd9a27e
Update System.Text.RegularExpressions.Generator.csproj
CyrusNajmabadi e2613b6
Port latest changes over
CyrusNajmabadi a0e9d88
Merge branch 'polyfill' of https://github.com/CyrusNajmabadi/runtime …
CyrusNajmabadi 91672fe
Renames
CyrusNajmabadi 86de769
Merge branch 'main' into polyfill
CyrusNajmabadi cc933a2
Update src/libraries/Common/src/Roslyn/CSharpSyntaxHelper.cs
CyrusNajmabadi 8a5a8d0
Simplify
CyrusNajmabadi 4eb00d1
Dispose builders
CyrusNajmabadi 59cdea7
Dispose builders
CyrusNajmabadi e7a1f3e
Simplify by removing support for nested attributes
CyrusNajmabadi 20d62a2
Simplify
CyrusNajmabadi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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,108 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
|
||
using Microsoft.CodeAnalysis.CSharp; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
|
||
namespace Microsoft.CodeAnalysis.DotnetRuntime.Extensions | ||
{ | ||
internal sealed class CSharpSyntaxHelper : AbstractSyntaxHelper | ||
{ | ||
public static readonly ISyntaxHelper Instance = new CSharpSyntaxHelper(); | ||
|
||
private CSharpSyntaxHelper() | ||
{ | ||
} | ||
|
||
public override bool IsCaseSensitive | ||
=> true; | ||
|
||
public override bool IsValidIdentifier(string name) | ||
=> SyntaxFacts.IsValidIdentifier(name); | ||
|
||
public override bool IsAnyNamespaceBlock(SyntaxNode node) | ||
=> node is BaseNamespaceDeclarationSyntax; | ||
|
||
public override bool IsAttribute(SyntaxNode node) | ||
=> node is AttributeSyntax; | ||
|
||
public override SyntaxNode GetNameOfAttribute(SyntaxNode node) | ||
=> ((AttributeSyntax)node).Name; | ||
|
||
public override bool IsAttributeList(SyntaxNode node) | ||
=> node is AttributeListSyntax; | ||
|
||
public override void AddAttributeTargets(SyntaxNode node, ref ValueListBuilder<SyntaxNode> targets) | ||
{ | ||
var attributeList = (AttributeListSyntax)node; | ||
var container = attributeList.Parent; | ||
Debug.Assert(container != null); | ||
|
||
// For fields/events, the attribute applies to all the variables declared. | ||
if (container is FieldDeclarationSyntax field) | ||
{ | ||
foreach (var variable in field.Declaration.Variables) | ||
targets.Append(variable); | ||
} | ||
else if (container is EventFieldDeclarationSyntax ev) | ||
{ | ||
foreach (var variable in ev.Declaration.Variables) | ||
targets.Append(variable); | ||
} | ||
else | ||
{ | ||
targets.Append(container); | ||
} | ||
} | ||
|
||
public override SeparatedSyntaxList<SyntaxNode> GetAttributesOfAttributeList(SyntaxNode node) | ||
=> ((AttributeListSyntax)node).Attributes; | ||
|
||
public override bool IsLambdaExpression(SyntaxNode node) | ||
=> node is LambdaExpressionSyntax; | ||
|
||
public override SyntaxToken GetUnqualifiedIdentifierOfName(SyntaxNode node) | ||
=> ((NameSyntax)node).GetUnqualifiedName().Identifier; | ||
|
||
public override void AddAliases(SyntaxNode node, ref ValueListBuilder<(string aliasName, string symbolName)> aliases, bool global) | ||
{ | ||
if (node is CompilationUnitSyntax compilationUnit) | ||
{ | ||
AddAliases(compilationUnit.Usings, ref aliases, global); | ||
} | ||
else if (node is BaseNamespaceDeclarationSyntax namespaceDeclaration) | ||
{ | ||
AddAliases(namespaceDeclaration.Usings, ref aliases, global); | ||
} | ||
else | ||
{ | ||
throw new System.InvalidOperationException("Unreachable"); | ||
CyrusNajmabadi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
|
||
private static void AddAliases(SyntaxList<UsingDirectiveSyntax> usings, ref ValueListBuilder<(string aliasName, string symbolName)> aliases, bool global) | ||
{ | ||
foreach (var usingDirective in usings) | ||
{ | ||
if (usingDirective.Alias is null) | ||
continue; | ||
|
||
if (global != usingDirective.GlobalKeyword.Kind() is SyntaxKind.GlobalKeyword) | ||
continue; | ||
|
||
var aliasName = usingDirective.Alias.Name.Identifier.ValueText; | ||
var symbolName = usingDirective.Name.GetUnqualifiedName().Identifier.ValueText; | ||
aliases.Append((aliasName, symbolName)); | ||
} | ||
} | ||
|
||
public override void AddAliases(CompilationOptions compilation, ref ValueListBuilder<(string aliasName, string symbolName)> aliases) | ||
{ | ||
// C# doesn't have global aliases at the compilation level. | ||
return; | ||
} | ||
} | ||
} |
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 |
---|---|---|
@@ -1,7 +1,11 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
using System.Collections.Immutable; | ||
|
||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
|
||
namespace Microsoft.CodeAnalysis.DotnetRuntime.Extensions | ||
{ | ||
|
@@ -134,5 +138,34 @@ private enum SymbolVisibility | |
Private = 2, | ||
Friend = Internal, | ||
} | ||
|
||
internal static bool HasAttributeSuffix(this string name, bool isCaseSensitive) | ||
{ | ||
const string AttributeSuffix = "Attribute"; | ||
|
||
var comparison = isCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; | ||
return name.Length > AttributeSuffix.Length && name.EndsWith(AttributeSuffix, comparison); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. port of roslyn helper. |
||
|
||
public static ImmutableArray<T> ToImmutableArray<T>(this ReadOnlySpan<T> span) | ||
{ | ||
if (span.Length == 0) | ||
return ImmutableArray<T>.Empty; | ||
|
||
var builder = ImmutableArray.CreateBuilder<T>(span.Length); | ||
foreach (var item in span) | ||
builder.Add(item); | ||
|
||
return builder.MoveToImmutable(); | ||
} | ||
CyrusNajmabadi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
public static SimpleNameSyntax GetUnqualifiedName(this NameSyntax name) | ||
=> name switch | ||
{ | ||
AliasQualifiedNameSyntax alias => alias.Name, | ||
QualifiedNameSyntax qualified => qualified.Right, | ||
SimpleNameSyntax simple => simple, | ||
_ => throw new InvalidOperationException("Unreachable"), | ||
}; | ||
} | ||
} |
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,74 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. direct port with only necessary cleanup to fit into runtime. |
||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
using System.Collections.Immutable; | ||
using Microsoft.CodeAnalysis.PooledObjects; | ||
using Roslyn.Utilities; | ||
|
||
namespace Microsoft.CodeAnalysis.DotnetRuntime.Extensions; | ||
CyrusNajmabadi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/// <summary> | ||
/// Simple wrapper class around an immutable array so we can have the value-semantics needed for the incremental | ||
/// generator to know when a change actually happened and it should run later transform stages. | ||
stephentoub marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// </summary> | ||
internal sealed class GlobalAliases : IEquatable<GlobalAliases> | ||
{ | ||
public static readonly GlobalAliases Empty = new(ImmutableArray<(string aliasName, string symbolName)>.Empty); | ||
|
||
public readonly ImmutableArray<(string aliasName, string symbolName)> AliasAndSymbolNames; | ||
|
||
private int _hashCode; | ||
|
||
private GlobalAliases(ImmutableArray<(string aliasName, string symbolName)> aliasAndSymbolNames) | ||
{ | ||
AliasAndSymbolNames = aliasAndSymbolNames; | ||
} | ||
|
||
public static GlobalAliases Create(ImmutableArray<(string aliasName, string symbolName)> aliasAndSymbolNames) | ||
{ | ||
return aliasAndSymbolNames.IsEmpty ? Empty : new GlobalAliases(aliasAndSymbolNames); | ||
} | ||
|
||
public static GlobalAliases Concat(GlobalAliases ga1, GlobalAliases ga2) | ||
{ | ||
if (ga1.AliasAndSymbolNames.Length == 0) | ||
return ga2; | ||
|
||
if (ga2.AliasAndSymbolNames.Length == 0) | ||
return ga1; | ||
|
||
return new(ga1.AliasAndSymbolNames.AddRange(ga2.AliasAndSymbolNames)); | ||
} | ||
|
||
public override int GetHashCode() | ||
{ | ||
if (_hashCode == 0) | ||
{ | ||
var hashCode = 0; | ||
foreach (var tuple in this.AliasAndSymbolNames) | ||
hashCode = Hash.Combine(tuple.GetHashCode(), hashCode); | ||
|
||
_hashCode = hashCode == 0 ? 1 : hashCode; | ||
} | ||
|
||
return _hashCode; | ||
} | ||
|
||
public override bool Equals(object? obj) | ||
=> this.Equals(obj as GlobalAliases); | ||
|
||
public bool Equals(GlobalAliases? aliases) | ||
{ | ||
if (aliases is null) | ||
return false; | ||
|
||
if (ReferenceEquals(this, aliases)) | ||
return true; | ||
|
||
if (this.AliasAndSymbolNames == aliases.AliasAndSymbolNames) | ||
return true; | ||
|
||
return this.AliasAndSymbolNames.AsSpan().SequenceEqual(aliases.AliasAndSymbolNames.AsSpan()); | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the general approach i took was to copy the roslyn code whole-sale and make as few changes to it as possible. The only changes i made were:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These files are all then source-linked into the project that needs them. So subsequent updates to other generators will be much easier as they can just leverage this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to have someone from the Roslyn team review all files added under
Common/src/Roslyn
? I understand that this is a direct port with just minimal changes, but it may be worth it to have that extra pair of eyes over those minimal changes from the domain experts.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. @chsienki and @jaredpar both reviewed the roslyn side. They should likely review this port as well.