-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Port the incremental source generator polyfill code to the 6.0 servic…
…ing branch. (#72219) * Port the incremental source generator polyfill code to the 6.0 servicing branch. * Use common code * Add logging extensions * Fix typo on project Compile item and enable producing packages for both packages that need to be serviced. * Allow ValueListBuilder to work with empty scratch spans (#70917) Co-authored-by: Jose Perez Rodriguez <joperezr@microsoft.com> Co-authored-by: Stephen Toub <stoub@microsoft.com>
- Loading branch information
1 parent
8f9ca76
commit 5a0d081
Showing
20 changed files
with
1,066 additions
and
12 deletions.
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,125 @@ | ||
// 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 | ||
{ | ||
Debug.Fail("This should not be reachable. Caller already checked we had a compilation unit or namespace."); | ||
} | ||
} | ||
|
||
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; | ||
} | ||
|
||
public override bool ContainsGlobalAliases(SyntaxNode root) | ||
{ | ||
// Global usings can only exist at the compilation-unit level, so no need to dive any deeper than that. | ||
var compilationUnit = (CompilationUnitSyntax)root; | ||
|
||
foreach (var directive in compilationUnit.Usings) | ||
{ | ||
if (directive.GlobalKeyword.IsKind(SyntaxKind.GlobalKeyword) && | ||
directive.Alias != null) | ||
{ | ||
return true; | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
} | ||
} |
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
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. | ||
// 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; | ||
|
||
/// <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. | ||
/// </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()); | ||
} | ||
} |
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,24 @@ | ||
// 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.Generic; | ||
using System.Collections.Immutable; | ||
using Microsoft.CodeAnalysis; | ||
|
||
namespace Roslyn.Utilities | ||
{ | ||
internal static class Hash | ||
{ | ||
/// <summary> | ||
/// This is how VB Anonymous Types combine hash values for fields. | ||
/// </summary> | ||
internal static int Combine(int newKey, int currentKey) | ||
{ | ||
return unchecked((currentKey * (int)0xA5555529) + newKey); | ||
} | ||
|
||
// The rest of this file was removed as they were not currently needed in the polyfill of SyntaxValueProvider.ForAttributeWithMetadataName. | ||
// If that changes, they should be added back as necessary. | ||
} | ||
} |
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,66 @@ | ||
// 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.Generic; | ||
using System.Collections.Immutable; | ||
using System.Linq; | ||
|
||
using Roslyn.Utilities; | ||
|
||
namespace Microsoft.CodeAnalysis.DotnetRuntime.Extensions | ||
{ | ||
internal interface ISyntaxHelper | ||
{ | ||
bool IsCaseSensitive { get; } | ||
|
||
bool IsValidIdentifier(string name); | ||
|
||
bool IsAnyNamespaceBlock(SyntaxNode node); | ||
|
||
bool IsAttributeList(SyntaxNode node); | ||
SeparatedSyntaxList<SyntaxNode> GetAttributesOfAttributeList(SyntaxNode node); | ||
|
||
void AddAttributeTargets(SyntaxNode node, ref ValueListBuilder<SyntaxNode> targets); | ||
|
||
bool IsAttribute(SyntaxNode node); | ||
SyntaxNode GetNameOfAttribute(SyntaxNode node); | ||
|
||
bool IsLambdaExpression(SyntaxNode node); | ||
|
||
SyntaxToken GetUnqualifiedIdentifierOfName(SyntaxNode node); | ||
|
||
/// <summary> | ||
/// <paramref name="node"/> must be a compilation unit or namespace block. | ||
/// </summary> | ||
void AddAliases(SyntaxNode node, ref ValueListBuilder<(string aliasName, string symbolName)> aliases, bool global); | ||
void AddAliases(CompilationOptions options, ref ValueListBuilder<(string aliasName, string symbolName)> aliases); | ||
|
||
bool ContainsGlobalAliases(SyntaxNode root); | ||
} | ||
|
||
internal abstract class AbstractSyntaxHelper : ISyntaxHelper | ||
{ | ||
public abstract bool IsCaseSensitive { get; } | ||
|
||
public abstract bool IsValidIdentifier(string name); | ||
|
||
public abstract SyntaxToken GetUnqualifiedIdentifierOfName(SyntaxNode name); | ||
|
||
public abstract bool IsAnyNamespaceBlock(SyntaxNode node); | ||
|
||
public abstract bool IsAttribute(SyntaxNode node); | ||
public abstract SyntaxNode GetNameOfAttribute(SyntaxNode node); | ||
|
||
public abstract bool IsAttributeList(SyntaxNode node); | ||
public abstract SeparatedSyntaxList<SyntaxNode> GetAttributesOfAttributeList(SyntaxNode node); | ||
public abstract void AddAttributeTargets(SyntaxNode node, ref ValueListBuilder<SyntaxNode> targets); | ||
|
||
public abstract bool IsLambdaExpression(SyntaxNode node); | ||
|
||
public abstract void AddAliases(SyntaxNode node, ref ValueListBuilder<(string aliasName, string symbolName)> aliases, bool global); | ||
public abstract void AddAliases(CompilationOptions options, ref ValueListBuilder<(string aliasName, string symbolName)> aliases); | ||
|
||
public abstract bool ContainsGlobalAliases(SyntaxNode root); | ||
} | ||
} |
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,42 @@ | ||
// 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 System.Linq; | ||
using Roslyn.Utilities; | ||
|
||
namespace Microsoft.CodeAnalysis.DotnetRuntime.Extensions; | ||
|
||
internal static partial class SyntaxValueProviderExtensions | ||
{ | ||
/// <summary> | ||
/// Wraps a grouping of nodes within a syntax tree so we can have value-semantics around them usable by the | ||
/// incremental driver. Note: we do something very sneaky here. Specifically, as long as we have the same <see | ||
/// cref="SyntaxTree"/> from before, then we know we must have the same nodes as before (since the nodes are | ||
/// entirely determined from the text+options which is exactly what the syntax tree represents). Similarly, if the | ||
/// syntax tree changes, we will always get different nodes (since they point back at the syntax tree). So we can | ||
/// just use the syntax tree itself to determine value semantics here. | ||
/// </summary> | ||
private sealed class SyntaxNodeGrouping<TSyntaxNode> : IEquatable<SyntaxNodeGrouping<TSyntaxNode>> | ||
where TSyntaxNode : SyntaxNode | ||
{ | ||
public readonly SyntaxTree SyntaxTree; | ||
public readonly ImmutableArray<TSyntaxNode> SyntaxNodes; | ||
|
||
public SyntaxNodeGrouping(IGrouping<SyntaxTree, TSyntaxNode> grouping) | ||
{ | ||
SyntaxTree = grouping.Key; | ||
SyntaxNodes = grouping.OrderBy(static n => n.FullSpan.Start).ToImmutableArray(); | ||
} | ||
|
||
public override int GetHashCode() | ||
=> SyntaxTree.GetHashCode(); | ||
|
||
public override bool Equals(object? obj) | ||
=> Equals(obj as SyntaxNodeGrouping<TSyntaxNode>); | ||
|
||
public bool Equals(SyntaxNodeGrouping<TSyntaxNode>? obj) | ||
=> this.SyntaxTree == obj?.SyntaxTree; | ||
} | ||
} |
Oops, something went wrong.