-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* Make config binding gen incremental * Iterate on implementation * Add incremental tests & driver * Make incremental tests pass and revert functional regression * Address failing tests * Make tests pass * Suppress diagnostic * Address feedback on diag info creation * Refactor member access expr parsing to indicate assumptions * Address feedback & do misc clean up * Adjust model to minimize baseline diff / misc clean up Co-authored-by: Carlos Sánchez López <1175054+carlossanlop@users.noreply.github.com>
- Loading branch information
1 parent
ef6283a
commit bc0f1b0
Showing
62 changed files
with
2,677 additions
and
1,390 deletions.
There are no files selected for viewing
60 changes: 60 additions & 0 deletions
60
src/libraries/Common/src/SourceGenerators/DiagnosticInfo.cs
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,60 @@ | ||
// 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.Linq; | ||
using System.Numerics.Hashing; | ||
using Microsoft.CodeAnalysis; | ||
|
||
namespace SourceGenerators; | ||
|
||
/// <summary> | ||
/// Descriptor for diagnostic instances using structural equality comparison. | ||
/// Provides a work-around for https://github.com/dotnet/roslyn/issues/68291. | ||
/// </summary> | ||
internal readonly struct DiagnosticInfo : IEquatable<DiagnosticInfo> | ||
{ | ||
public DiagnosticDescriptor Descriptor { get; private init; } | ||
public object?[] MessageArgs { get; private init; } | ||
public Location? Location { get; private init; } | ||
|
||
public static DiagnosticInfo Create(DiagnosticDescriptor descriptor, Location? location, object?[]? messageArgs) | ||
{ | ||
Location? trimmedLocation = location is null ? null : GetTrimmedLocation(location); | ||
|
||
return new DiagnosticInfo | ||
{ | ||
Descriptor = descriptor, | ||
Location = trimmedLocation, | ||
MessageArgs = messageArgs ?? Array.Empty<object?>() | ||
}; | ||
|
||
// Creates a copy of the Location instance that does not capture a reference to Compilation. | ||
static Location GetTrimmedLocation(Location location) | ||
=> Location.Create(location.SourceTree?.FilePath ?? "", location.SourceSpan, location.GetLineSpan().Span); | ||
} | ||
|
||
public Diagnostic CreateDiagnostic() | ||
=> Diagnostic.Create(Descriptor, Location, MessageArgs); | ||
|
||
public override readonly bool Equals(object? obj) => obj is DiagnosticInfo info && Equals(info); | ||
|
||
public readonly bool Equals(DiagnosticInfo other) | ||
{ | ||
return Descriptor.Equals(other.Descriptor) && | ||
MessageArgs.SequenceEqual(other.MessageArgs) && | ||
Location == other.Location; | ||
} | ||
|
||
public override readonly int GetHashCode() | ||
{ | ||
int hashCode = Descriptor.GetHashCode(); | ||
foreach (object? messageArg in MessageArgs) | ||
{ | ||
hashCode = HashHelpers.Combine(hashCode, messageArg?.GetHashCode() ?? 0); | ||
} | ||
|
||
hashCode = HashHelpers.Combine(hashCode, Location?.GetHashCode() ?? 0); | ||
return hashCode; | ||
} | ||
} |
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
3 changes: 2 additions & 1 deletion
3
...ies/System.Text.Json/gen/Model/TypeRef.cs → ...es/Common/src/SourceGenerators/TypeRef.cs
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
84 changes: 84 additions & 0 deletions
84
src/libraries/Common/tests/SourceGenerators/GeneratorTestHelpers.cs
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,84 @@ | ||
// 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; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Reflection; | ||
using Xunit; | ||
|
||
namespace SourceGenerators.Tests | ||
{ | ||
internal static class GeneratorTestHelpers | ||
{ | ||
/// <summary> | ||
/// Asserts for structural equality, returning a path to the mismatching data when not equal. | ||
/// </summary> | ||
public static void AssertStructurallyEqual<T>(T expected, T actual) | ||
{ | ||
CheckAreEqualCore(expected, actual, new()); | ||
static void CheckAreEqualCore(object expected, object actual, Stack<string> path) | ||
{ | ||
if (expected is null || actual is null) | ||
{ | ||
if (expected is not null || actual is not null) | ||
{ | ||
FailNotEqual(); | ||
} | ||
|
||
return; | ||
} | ||
|
||
Type type = expected.GetType(); | ||
if (type != actual.GetType()) | ||
{ | ||
FailNotEqual(); | ||
return; | ||
} | ||
|
||
if (expected is IEnumerable leftCollection) | ||
{ | ||
if (actual is not IEnumerable rightCollection) | ||
{ | ||
FailNotEqual(); | ||
return; | ||
} | ||
|
||
object?[] expectedValues = leftCollection.Cast<object?>().ToArray(); | ||
object?[] actualValues = rightCollection.Cast<object?>().ToArray(); | ||
|
||
for (int i = 0; i < Math.Max(expectedValues.Length, actualValues.Length); i++) | ||
{ | ||
object? expectedElement = i < expectedValues.Length ? expectedValues[i] : "<end of collection>"; | ||
object? actualElement = i < actualValues.Length ? actualValues[i] : "<end of collection>"; | ||
|
||
path.Push($"[{i}]"); | ||
CheckAreEqualCore(expectedElement, actualElement, path); | ||
path.Pop(); | ||
} | ||
} | ||
|
||
if (type.GetProperty("EqualityContract", BindingFlags.Instance | BindingFlags.NonPublic, null, returnType: typeof(Type), types: Array.Empty<Type>(), null) != null) | ||
{ | ||
// Type is a C# record, run pointwise equality comparison. | ||
foreach (PropertyInfo property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) | ||
{ | ||
path.Push("." + property.Name); | ||
CheckAreEqualCore(property.GetValue(expected), property.GetValue(actual), path); | ||
path.Pop(); | ||
} | ||
|
||
return; | ||
} | ||
|
||
if (!expected.Equals(actual)) | ||
{ | ||
FailNotEqual(); | ||
} | ||
|
||
void FailNotEqual() => Assert.Fail($"Value not equal in ${string.Join("", path.Reverse())}: expected {expected}, but was {actual}."); | ||
} | ||
} | ||
} | ||
} |
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
Oops, something went wrong.