From 28fbb5952da325326b102c09611c1a497cf4a497 Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Mon, 15 Jul 2024 15:54:03 -0700 Subject: [PATCH 01/30] Add GetPooledArraySpan(...) extension method This change adds a new `GetPooledArraySpan(...)` extension method that returns a pooled array as a `Span` with the expected length. This can help avoid bugs that can occur if a pooled array's `Length` is accidental used. --- .../Utilities/FilePathNormalizer.cs | 39 +++++++++---------- .../BufferExtensions.cs | 7 ++++ .../PooledObjects/PooledArray`1.cs | 4 ++ 3 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/FilePathNormalizer.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/FilePathNormalizer.cs index 54bd1ac5c91..c9b2a9c2a15 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/FilePathNormalizer.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/FilePathNormalizer.cs @@ -28,16 +28,15 @@ public static string NormalizeDirectory(string? directoryFilePath) // Ensure that the array is at least 1 character larger, so that we can add // a trailing space after normalization if necessary. var arrayLength = directoryFilePathSpan.Length + 1; - using var _ = ArrayPool.Shared.GetPooledArray(arrayLength, out var array); - var arraySpan = array.AsSpan(0, arrayLength); - var (start, length) = NormalizeCore(directoryFilePathSpan, arraySpan); - ReadOnlySpan normalizedSpan = arraySpan.Slice(start, length); + using var _ = ArrayPool.Shared.GetPooledArraySpan(arrayLength, out var destination); + var (start, length) = NormalizeCore(directoryFilePathSpan, destination); + ReadOnlySpan normalizedSpan = destination.Slice(start, length); // Add a trailing slash if the normalized span doesn't end in one. if (normalizedSpan is not [.., '/']) { - arraySpan[start + length] = '/'; - normalizedSpan = arraySpan.Slice(start, length + 1); + destination[start + length] = '/'; + normalizedSpan = destination.Slice(start, length + 1); } if (directoryFilePathSpan.Equals(normalizedSpan, StringComparison.Ordinal)) @@ -58,8 +57,8 @@ public static string Normalize(string? filePath) var filePathSpan = filePath.AsSpan(); // Rent a buffer for Normalize to write to. - using var _ = ArrayPool.Shared.GetPooledArray(filePathSpan.Length, out var array); - var normalizedSpan = NormalizeCoreAndGetSpan(filePathSpan, array); + using var _ = ArrayPool.Shared.GetPooledArraySpan(filePathSpan.Length, out var destination); + var normalizedSpan = NormalizeCoreAndGetSpan(filePathSpan, destination); // If we didn't change anything, just return the original string. if (filePathSpan.Equals(normalizedSpan, StringComparison.Ordinal)) @@ -83,8 +82,8 @@ public static string GetNormalizedDirectoryName(string? filePath) var filePathSpan = filePath.AsSpan(); - using var _1 = ArrayPool.Shared.GetPooledArray(filePathSpan.Length, out var array); - var directoryNameSpan = NormalizeDirectoryNameCore(filePathSpan, array); + using var _1 = ArrayPool.Shared.GetPooledArraySpan(filePathSpan.Length, out var destination); + var directoryNameSpan = NormalizeDirectoryNameCore(filePathSpan, destination); if (filePathSpan.Equals(directoryNameSpan, StringComparison.Ordinal)) { @@ -108,11 +107,11 @@ public static bool AreDirectoryPathsEquivalent(string? filePath1, string? filePa return false; } - using var _1 = ArrayPool.Shared.GetPooledArray(filePathSpan1.Length, out var array1); - var normalizedSpan1 = NormalizeDirectoryNameCore(filePathSpan1, array1); + using var _1 = ArrayPool.Shared.GetPooledArraySpan(filePathSpan1.Length, out var destination1); + var normalizedSpan1 = NormalizeDirectoryNameCore(filePathSpan1, destination1); - using var _2 = ArrayPool.Shared.GetPooledArray(filePathSpan2.Length, out var array2); - var normalizedSpan2 = NormalizeDirectoryNameCore(filePathSpan2, array2); + using var _2 = ArrayPool.Shared.GetPooledArraySpan(filePathSpan2.Length, out var destination2); + var normalizedSpan2 = NormalizeDirectoryNameCore(filePathSpan2, destination2); return normalizedSpan1.Equals(normalizedSpan2, FilePathComparison.Instance); } @@ -131,11 +130,11 @@ public static bool AreFilePathsEquivalent(string? filePath1, string? filePath2) return false; } - using var _1 = ArrayPool.Shared.GetPooledArray(filePathSpan1.Length, out var array1); - var normalizedSpan1 = NormalizeCoreAndGetSpan(filePathSpan1, array1); + using var _1 = ArrayPool.Shared.GetPooledArraySpan(filePathSpan1.Length, out var destination1); + var normalizedSpan1 = NormalizeCoreAndGetSpan(filePathSpan1, destination1); - using var _2 = ArrayPool.Shared.GetPooledArray(filePathSpan2.Length, out var array2); - var normalizedSpan2 = NormalizeCoreAndGetSpan(filePathSpan2, array2); + using var _2 = ArrayPool.Shared.GetPooledArraySpan(filePathSpan2.Length, out var destination2); + var normalizedSpan2 = NormalizeCoreAndGetSpan(filePathSpan2, destination2); return normalizedSpan1.Equals(normalizedSpan2, FilePathComparison.Instance); } @@ -149,8 +148,8 @@ public static int GetHashCode(string filePath) var filePathSpan = filePath.AsSpanOrDefault(); - using var _ = ArrayPool.Shared.GetPooledArray(filePathSpan.Length, out var array1); - var normalizedSpan = NormalizeCoreAndGetSpan(filePathSpan, array1); + using var _ = ArrayPool.Shared.GetPooledArraySpan(filePathSpan.Length, out var destination); + var normalizedSpan = NormalizeCoreAndGetSpan(filePathSpan, destination); var hashCombiner = HashCodeCombiner.Start(); diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/BufferExtensions.cs b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/BufferExtensions.cs index ea4e7823eff..8786c059ea4 100644 --- a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/BufferExtensions.cs +++ b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/BufferExtensions.cs @@ -13,4 +13,11 @@ public static PooledArray GetPooledArray(this ArrayPool pool, int minim array = result.Array; return result; } + + public static PooledArray GetPooledArraySpan(this ArrayPool pool, int minimumLength, out Span span) + { + var result = new PooledArray(pool, minimumLength); + span = result.Span; + return result; + } } diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/PooledObjects/PooledArray`1.cs b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/PooledObjects/PooledArray`1.cs index 6bd248baffc..0d78e9183e5 100644 --- a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/PooledObjects/PooledArray`1.cs +++ b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/PooledObjects/PooledArray`1.cs @@ -9,6 +9,7 @@ namespace Microsoft.AspNetCore.Razor.PooledObjects; internal struct PooledArray : IDisposable { private readonly ArrayPool _pool; + private readonly int _minimumLength; private T[]? _array; // Because of how this API is intended to be used, we don't want the consumption code to have @@ -16,10 +17,13 @@ internal struct PooledArray : IDisposable // non-null until this is disposed. public readonly T[] Array => _array!; + public readonly Span Span => _array!.AsSpan(0, _minimumLength); + public PooledArray(ArrayPool pool, int minimumLength) : this() { _pool = pool; + _minimumLength = minimumLength; _array = pool.Rent(minimumLength); } From 5288d76153bbf69275a52aed082c8b779cc44d6a Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Mon, 15 Jul 2024 19:31:54 -0700 Subject: [PATCH 02/30] Add XML doc comments --- .../BufferExtensions.cs | 12 ++++++++++++ .../PooledObjects/PooledArray`1.cs | 16 +++++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/BufferExtensions.cs b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/BufferExtensions.cs index 8786c059ea4..a897c734fec 100644 --- a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/BufferExtensions.cs +++ b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/BufferExtensions.cs @@ -7,6 +7,13 @@ namespace System.Buffers; internal static class BufferExtensions { + /// + /// Rents an array of the given minimum length from the specified . + /// + /// + /// The array is guaranteed to be at least in length. However, + /// it will likely be larger. + /// public static PooledArray GetPooledArray(this ArrayPool pool, int minimumLength, out T[] array) { var result = new PooledArray(pool, minimumLength); @@ -14,6 +21,11 @@ public static PooledArray GetPooledArray(this ArrayPool pool, int minim return result; } + /// + /// Rents an array of the given minimum length from the specified . + /// The rented array is provided as a representing a portion of the rented array + /// from its start to the minimum length. + /// public static PooledArray GetPooledArraySpan(this ArrayPool pool, int minimumLength, out Span span) { var result = new PooledArray(pool, minimumLength); diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/PooledObjects/PooledArray`1.cs b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/PooledObjects/PooledArray`1.cs index 0d78e9183e5..c2f6ff32c45 100644 --- a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/PooledObjects/PooledArray`1.cs +++ b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/PooledObjects/PooledArray`1.cs @@ -12,11 +12,21 @@ internal struct PooledArray : IDisposable private readonly int _minimumLength; private T[]? _array; - // Because of how this API is intended to be used, we don't want the consumption code to have - // to deal with Array being a nullable reference type. Instead, the guarantee is that this is - // non-null until this is disposed. + /// + /// Returns the array that was rented from . + /// + /// + /// Returns a non-null array until is disposed. + /// public readonly T[] Array => _array!; + /// + /// Returns a representing a portion of the rented array + /// from its start to the minimum length. + /// + /// + /// The array has been returned to the pool. + /// public readonly Span Span => _array!.AsSpan(0, _minimumLength); public PooledArray(ArrayPool pool, int minimumLength) From 72132015f0128ffeac1f7bd5125c0b4f517fac23 Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Mon, 15 Jul 2024 19:39:43 -0700 Subject: [PATCH 03/30] Add GetPooledArray(...) overloads to clear the array when returned --- .../BufferExtensions.cs | 64 ++++++++++++++++++- .../PooledObjects/PooledArray`1.cs | 6 +- 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/BufferExtensions.cs b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/BufferExtensions.cs index a897c734fec..b3b1f58d087 100644 --- a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/BufferExtensions.cs +++ b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/BufferExtensions.cs @@ -10,13 +10,44 @@ internal static class BufferExtensions /// /// Rents an array of the given minimum length from the specified . /// + /// + /// The to use. + /// + /// + /// The minimum length of the array. + /// + /// + /// The rented array. + /// /// /// The array is guaranteed to be at least in length. However, /// it will likely be larger. /// public static PooledArray GetPooledArray(this ArrayPool pool, int minimumLength, out T[] array) + => pool.GetPooledArray(minimumLength, clearOnReturn: false, out array); + + /// + /// Rents an array of the given minimum length from the specified . + /// + /// + /// The to use. + /// + /// + /// The minimum length of the array. + /// + /// + /// Indicates whether the contents of the array should be cleared before it is returned to the pool. + /// + /// + /// The rented array. + /// + /// + /// The array is guaranteed to be at least in length. However, + /// it will likely be larger. + /// + public static PooledArray GetPooledArray(this ArrayPool pool, int minimumLength, bool clearOnReturn, out T[] array) { - var result = new PooledArray(pool, minimumLength); + var result = new PooledArray(pool, minimumLength, clearOnReturn); array = result.Array; return result; } @@ -26,9 +57,38 @@ public static PooledArray GetPooledArray(this ArrayPool pool, int minim /// The rented array is provided as a representing a portion of the rented array /// from its start to the minimum length. /// + /// + /// The to use. + /// + /// + /// The minimum length of the array. + /// + /// + /// The representing a portion of the rented array from its start to the minimum length. + /// public static PooledArray GetPooledArraySpan(this ArrayPool pool, int minimumLength, out Span span) + => pool.GetPooledArraySpan(minimumLength, clearOnReturn: false, out span); + + /// + /// Rents an array of the given minimum length from the specified . + /// The rented array is provided as a representing a portion of the rented array + /// from its start to the minimum length. + /// + /// + /// The to use. + /// + /// + /// The minimum length of the array. + /// + /// + /// Indicates whether the contents of the array should be cleared before it is returned to the pool. + /// + /// + /// The representing a portion of the rented array from its start to the minimum length. + /// + public static PooledArray GetPooledArraySpan(this ArrayPool pool, int minimumLength, bool clearOnReturn, out Span span) { - var result = new PooledArray(pool, minimumLength); + var result = new PooledArray(pool, minimumLength, clearOnReturn); span = result.Span; return result; } diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/PooledObjects/PooledArray`1.cs b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/PooledObjects/PooledArray`1.cs index c2f6ff32c45..4f6850c092b 100644 --- a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/PooledObjects/PooledArray`1.cs +++ b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/PooledObjects/PooledArray`1.cs @@ -10,6 +10,7 @@ internal struct PooledArray : IDisposable { private readonly ArrayPool _pool; private readonly int _minimumLength; + private readonly bool _clearOnReturn; private T[]? _array; /// @@ -29,11 +30,12 @@ internal struct PooledArray : IDisposable /// public readonly Span Span => _array!.AsSpan(0, _minimumLength); - public PooledArray(ArrayPool pool, int minimumLength) + public PooledArray(ArrayPool pool, int minimumLength, bool clearOnReturn) : this() { _pool = pool; _minimumLength = minimumLength; + _clearOnReturn = clearOnReturn; _array = pool.Rent(minimumLength); } @@ -41,7 +43,7 @@ public void Dispose() { if (_array is T[] array) { - _pool.Return(array); + _pool.Return(array, clearArray: _clearOnReturn); _array = null; } } From 721308d46836ba44fcb08cd000c2648e90f001a0 Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Tue, 16 Jul 2024 08:27:18 -0700 Subject: [PATCH 04/30] Add overloads for string AsSpan(), AsMemory() and *OrDefault() variants - Add overloads for string.AsSpan() and string.AsMemory() that take Razor's Index and Range polyfill types. - Add overloads for string.AsSpanOrDefault() and string.AsMemoryOrDefault() that take start, start+length, and Razor Index and Range polyfill types. Note: If compiling for .NET, these forward to the relevant runtime methods. --- .../StringExtensions.cs | 152 ++++++++++++++++++ .../Utilities/ThrowHelper.cs | 28 ++++ 2 files changed, 180 insertions(+) diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/StringExtensions.cs b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/StringExtensions.cs index f649b756075..fb0b34d9b3b 100644 --- a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/StringExtensions.cs +++ b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/StringExtensions.cs @@ -3,6 +3,10 @@ using System.Diagnostics.CodeAnalysis; +#if !NET +using ThrowHelper = Microsoft.AspNetCore.Razor.Utilities.ThrowHelper; +#endif + namespace System; internal static class StringExtensions @@ -13,12 +17,160 @@ public static bool IsNullOrEmpty([NotNullWhen(false)] this string? s) public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? s) => string.IsNullOrWhiteSpace(s); + public static ReadOnlySpan AsSpan(this string? s, Index startIndex) + { +#if NET + return MemoryExtensions.AsSpan(s, startIndex); +#else + if (s is null) + { + if (!startIndex.Equals(Index.Start)) + { + ThrowHelper.ThrowArgumentOutOfRange(nameof(startIndex)); + } + + return default; + } + + return s.AsSpan(startIndex.GetOffset(s.Length)); +#endif + } + + public static ReadOnlySpan AsSpan(this string? s, Range range) + { +#if NET + return MemoryExtensions.AsSpan(s, range); +#else + if (s is null) + { + if (!range.Start.Equals(Index.Start) || !range.End.Equals(Index.Start)) + { + ThrowHelper.ThrowArgumentNull(nameof(s)); + } + + return default; + } + + var (start, length) = range.GetOffsetAndLength(s.Length); + return s.AsSpan(start, length); +#endif + } + public static ReadOnlySpan AsSpanOrDefault(this string? s) => s is not null ? s.AsSpan() : default; + public static ReadOnlySpan AsSpanOrDefault(this string? s, int start) + => s is not null ? s.AsSpan(start) : default; + + public static ReadOnlySpan AsSpanOrDefault(this string? s, int start, int length) + => s is not null ? s.AsSpan(start, length) : default; + + public static ReadOnlySpan AsSpanOrDefault(this string? s, Index startIndex) + { + if (s is null) + { + return default; + } + +#if NET + return MemoryExtensions.AsSpan(s, startIndex); +#else + return s.AsSpan(startIndex.GetOffset(s.Length)); +#endif + } + + public static ReadOnlySpan AsSpanOrDefault(this string? s, Range range) + { + if (s is null) + { + return default; + } + +#if NET + return MemoryExtensions.AsSpan(s, range); +#else + var (start, length) = range.GetOffsetAndLength(s.Length); + return s.AsSpan(start, length); +#endif + } + + public static ReadOnlyMemory AsMemory(this string? s, Index startIndex) + { +#if NET + return MemoryExtensions.AsMemory(s, startIndex); +#else + if (s is null) + { + if (!startIndex.Equals(Index.Start)) + { + ThrowHelper.ThrowArgumentOutOfRange(nameof(startIndex)); + } + + return default; + } + + return s.AsMemory(startIndex.GetOffset(s.Length)); +#endif + } + + public static ReadOnlyMemory AsMemory(this string? s, Range range) + { +#if NET + return MemoryExtensions.AsMemory(s, range); +#else + if (s is null) + { + if (!range.Start.Equals(Index.Start) || !range.End.Equals(Index.Start)) + { + ThrowHelper.ThrowArgumentNull(nameof(s)); + } + + return default; + } + + var (start, length) = range.GetOffsetAndLength(s.Length); + return s.AsMemory(start, length); +#endif + } + public static ReadOnlyMemory AsMemoryOrDefault(this string? s) => s is not null ? s.AsMemory() : default; + public static ReadOnlyMemory AsMemoryOrDefault(this string? s, int start) + => s is not null ? s.AsMemory(start) : default; + + public static ReadOnlyMemory AsMemoryOrDefault(this string? s, int start, int length) + => s is not null ? s.AsMemory(start, length) : default; + + public static ReadOnlyMemory AsMemoryOrDefault(this string? s, Index startIndex) + { + if (s is null) + { + return default; + } + +#if NET + return MemoryExtensions.AsMemory(s, startIndex); +#else + return s.AsMemory(startIndex.GetOffset(s.Length)); +#endif + } + + public static ReadOnlyMemory AsMemoryOrDefault(this string? s, Range range) + { + if (s is null) + { + return default; + } + +#if NET + return MemoryExtensions.AsMemory(s, range); +#else + var (start, length) = range.GetOffsetAndLength(s.Length); + return s.AsMemory(start, length); +#endif + } + // This method doesn't exist on .NET Framework, but it does on .NET Core. public static bool Contains(this string s, char ch) => s.IndexOf(ch) >= 0; diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/Utilities/ThrowHelper.cs b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/Utilities/ThrowHelper.cs index 1a78ee3a3d2..168871c0218 100644 --- a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/Utilities/ThrowHelper.cs +++ b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/Utilities/ThrowHelper.cs @@ -8,6 +8,34 @@ namespace Microsoft.AspNetCore.Razor.Utilities; internal static class ThrowHelper { + /// + /// This is present to help the JIT inline methods that need to throw an . + /// + [DoesNotReturn] + public static void ThrowArgumentNull(string paramName) + => throw new ArgumentNullException(paramName); + + /// + /// This is present to help the JIT inline methods that need to throw an . + /// + [DoesNotReturn] + public static T ThrowArgumentNull(string paramName) + => throw new ArgumentNullException(paramName); + + /// + /// This is present to help the JIT inline methods that need to throw an . + /// + [DoesNotReturn] + public static void ThrowArgumentOutOfRange(string paramName) + => throw new ArgumentOutOfRangeException(paramName); + + /// + /// This is present to help the JIT inline methods that need to throw an . + /// + [DoesNotReturn] + public static T ThrowArgumentOutOfRange(string paramName) + => throw new ArgumentOutOfRangeException(paramName); + /// /// This is present to help the JIT inline methods that need to throw an . /// From 2e994ba3adf7ad65410739ebfa848a0dbfba5957 Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Tue, 16 Jul 2024 09:01:04 -0700 Subject: [PATCH 05/30] Add overloads for string.Contains(), IndexOf, StartsWith, and EndsWith Add overloads for string.Contains(), IndexOf(), StartsWith, and EndsWith() extensions methods that exist on .NET Core but not on .NET Framework or .NET Standard 2.0 --- .../StringExtensions.cs | 155 +++++++++++++++++- 1 file changed, 149 insertions(+), 6 deletions(-) diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/StringExtensions.cs b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/StringExtensions.cs index fb0b34d9b3b..59e7f106e6b 100644 --- a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/StringExtensions.cs +++ b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/StringExtensions.cs @@ -171,11 +171,154 @@ public static ReadOnlyMemory AsMemoryOrDefault(this string? s, Range range #endif } - // This method doesn't exist on .NET Framework, but it does on .NET Core. - public static bool Contains(this string s, char ch) - => s.IndexOf(ch) >= 0; + /// + /// Returns a value indicating whether a specified character occurs within a string instance. + /// + /// + /// The string instance. + /// + /// + /// The character to seek. + /// + /// + /// if the value parameter occurs within the string; otherwise, . + /// + /// + /// This method exists on .NET Core, but doesn't on .NET Framework or .NET Standard 2.0. + /// + public static bool Contains(this string text, char value) + { +#if NET + return text.Contains(value); +#else + return text.IndexOf(value) >= 0; +#endif + } + + /// + /// Returns a value indicating whether a specified character occurs within a string instance, + /// using the specified comparison rules. + /// + /// + /// The string instance. + /// + /// + /// The character to seek. + /// + /// + /// One of the enumeration values that specifies the rules to use in the comparison. + /// + /// + /// if the value parameter occurs within the string; otherwise, . + /// + /// + /// This method exists on .NET Core, but doesn't on .NET Framework or .NET Standard 2.0. + /// + public static bool Contains(this string text, char value, StringComparison comparisonType) + { +#if NET + return text.Contains(value, comparisonType); +#else + return text.IndexOf(value, comparisonType) != 0; +#endif + } + + /// + /// Reports the zero-based index of the first occurrence of the specified Unicode character in a string instance. + /// A parameter specifies the type of search to use for the specified character. + /// + /// + /// The string instance. + /// + /// + /// The character to compare to the character at the start of this string. + /// + /// + /// An enumeration value that specifies the rules for the search. + /// + /// + /// The zero-based index of if that character is found, or -1 if it is not. + /// + /// + /// + /// Index numbering starts from zero. + /// + /// + /// The parameter is a enumeration member + /// that specifies whether the search for the argument uses the current or invariant culture, + /// is case-sensitive or case-insensitive, or uses word or ordinal comparison rules. + /// + /// + /// This method exists on .NET Core, but doesn't on .NET Framework or .NET Standard 2.0. + /// + /// + public static int IndexOf(this string text, char value, StringComparison comparisonType) + { +#if NET + return text.IndexOf(value, comparisonType); +#else + // [ch] produces a ReadOnlySpan using a ref to ch. + return text.AsSpan().IndexOf([value], comparisonType); +#endif + } + + /// + /// Determines whether a string instance starts with the specified character. + /// + /// + /// The string instance. + /// + /// + /// The character to compare to the character at the start of this string. + /// + /// + /// if matches the start of the string; + /// otherwise, . + /// + /// + /// + /// This method performs an ordinal (case-sensitive and culture-insensitive) comparison. + /// + /// + /// This method exists on .NET Core, but doesn't on .NET Framework or .NET Standard 2.0. + /// + /// + public static bool StartsWith(this string text, char value) + { +#if NET + return text.StartsWith(value); +#else + return text.Length > 0 && text[0] == value; +#endif + } - // This method doesn't exist on .NET Framework, but it does on .NET Core. - public static bool EndsWith(this string s, char ch) - => s.Length > 0 && s[^1] == ch; + /// + /// Determines whether the end of a string instance matches the specified character. + /// + /// + /// The string instance. + /// + /// + /// The character to compare to the character at the end of this string. + /// + /// + /// if matches the end of this string; + /// otherwise, . + /// + /// + /// + /// This method performs an ordinal (case-sensitive and culture-insensitive) comparison. + /// + /// + /// This method exists on .NET Core, but doesn't on .NET Framework or .NET Standard 2.0. + /// + /// + public static bool EndsWith(this string text, char value) + { +#if NET + return text.EndsWith(value); +#else + return text.Length > 0 && text[^1] == value; +#endif + } } From 55aeabb88bcbcd5b76ddc58e8634a97781847ed1 Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Tue, 16 Jul 2024 10:00:14 -0700 Subject: [PATCH 06/30] Add more XML doc comments --- .../StringExtensions.cs | 341 +++++++++++++++--- 1 file changed, 287 insertions(+), 54 deletions(-) diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/StringExtensions.cs b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/StringExtensions.cs index 59e7f106e6b..9dde4e4b539 100644 --- a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/StringExtensions.cs +++ b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/StringExtensions.cs @@ -11,18 +11,64 @@ namespace System; internal static class StringExtensions { - public static bool IsNullOrEmpty([NotNullWhen(false)] this string? s) - => string.IsNullOrEmpty(s); + /// + /// Indicates whether the specified string is or an empty string (""). + /// + /// + /// The string to test. + /// + /// + /// if the parameter is + /// or an empty string (""); otherwise, . + /// + /// + /// This extension method is useful on .NET Framework and .NET Standard 2.0 where + /// is not annotated for nullability. + /// + public static bool IsNullOrEmpty([NotNullWhen(false)] this string? value) + => string.IsNullOrEmpty(value); - public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? s) - => string.IsNullOrWhiteSpace(s); + /// + /// Indicates whether a specified string is , empty, or consists only + /// of white-space characters. + /// + /// + /// The string to test. + /// + /// + /// if the parameter is + /// or , or if consists exclusively of + /// white-space characters. + /// + /// + /// This extension method is useful on .NET Framework and .NET Standard 2.0 where + /// is not annotated for nullability. + /// + public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? value) + => string.IsNullOrWhiteSpace(value); - public static ReadOnlySpan AsSpan(this string? s, Index startIndex) + /// + /// Creates a new over a portion of the target string from + /// a specified position to the end of the string. + /// + /// + /// The target string. + /// + /// + /// The index at which to begin this slice. + /// + /// + /// This uses Razor's type, which is type-forwarded on .NET. + /// + /// + /// is less than 0 or greater than .Length. + /// + public static ReadOnlySpan AsSpan(this string? text, Index startIndex) { #if NET - return MemoryExtensions.AsSpan(s, startIndex); + return MemoryExtensions.AsSpan(text, startIndex); #else - if (s is null) + if (text is null) { if (!startIndex.Equals(Index.Start)) { @@ -32,74 +78,175 @@ public static ReadOnlySpan AsSpan(this string? s, Index startIndex) return default; } - return s.AsSpan(startIndex.GetOffset(s.Length)); + return text.AsSpan(startIndex.GetOffset(text.Length)); #endif } - public static ReadOnlySpan AsSpan(this string? s, Range range) + /// + /// Creates a new over a portion of a target string using + /// the range start and end indexes. + /// + /// + /// The target string. + /// + /// + /// The range that has start and end indexes to use for slicing the string. + /// + /// + /// This uses Razor's type, which is type-forwarded on .NET. + /// + /// + /// 's start or end index is not within the bounds of the string. + /// + /// + /// 's start index is greater than its end index. + /// + public static ReadOnlySpan AsSpan(this string? text, Range range) { #if NET - return MemoryExtensions.AsSpan(s, range); + return MemoryExtensions.AsSpan(text, range); #else - if (s is null) + if (text is null) { if (!range.Start.Equals(Index.Start) || !range.End.Equals(Index.Start)) { - ThrowHelper.ThrowArgumentNull(nameof(s)); + ThrowHelper.ThrowArgumentNull(nameof(text)); } return default; } - var (start, length) = range.GetOffsetAndLength(s.Length); - return s.AsSpan(start, length); + var (start, length) = range.GetOffsetAndLength(text.Length); + return text.AsSpan(start, length); #endif } - public static ReadOnlySpan AsSpanOrDefault(this string? s) - => s is not null ? s.AsSpan() : default; + /// + /// Creates a new over a string. If the target string + /// is a () is returned. + /// + /// + /// The target string. + /// + public static ReadOnlySpan AsSpanOrDefault(this string? text) + => text is not null ? text.AsSpan() : default; - public static ReadOnlySpan AsSpanOrDefault(this string? s, int start) - => s is not null ? s.AsSpan(start) : default; + /// + /// Creates a new over a portion of the target string from + /// a specified position to the end of the string. If the target string is + /// a () is returned. + /// + /// + /// The target string. + /// + /// + /// The index at which to begin this slice. + /// + /// + /// is less than 0 or greater than .Length. + /// + public static ReadOnlySpan AsSpanOrDefault(this string? text, int start) + => text is not null ? text.AsSpan(start) : default; - public static ReadOnlySpan AsSpanOrDefault(this string? s, int start, int length) - => s is not null ? s.AsSpan(start, length) : default; + /// + /// Creates a new over a portion of the target string from + /// a specified position for a specified number of characters. If the target string is + /// a () is returned. + /// + /// + /// The target string. + /// + /// + /// The index at which to begin this slice. + /// + /// + /// The desired length for the slice. + /// + /// + /// , , or + + /// is not in the range of . + /// + public static ReadOnlySpan AsSpanOrDefault(this string? text, int start, int length) + => text is not null ? text.AsSpan(start, length) : default; - public static ReadOnlySpan AsSpanOrDefault(this string? s, Index startIndex) + /// + /// Creates a new over a portion of the target string from + /// a specified position to the end of the string. If the target string is + /// a () is returned. + /// + /// + /// The target string. + /// + /// + /// The index at which to begin this slice. + /// + public static ReadOnlySpan AsSpanOrDefault(this string? text, Index startIndex) { - if (s is null) + if (text is null) { return default; } #if NET - return MemoryExtensions.AsSpan(s, startIndex); + return MemoryExtensions.AsSpan(text, startIndex); #else - return s.AsSpan(startIndex.GetOffset(s.Length)); + return text.AsSpan(startIndex.GetOffset(text.Length)); #endif } - public static ReadOnlySpan AsSpanOrDefault(this string? s, Range range) + /// + /// Creates a new over a portion of the target string using the range + /// start and end indexes. If the target string is a + /// () is returned. + /// + /// + /// The target string. + /// + /// + /// The range that has start and end indexes to use for slicing the string. + /// + /// + /// 's start or end index is not within the bounds of the string. + /// + /// + /// 's start index is greater than its end index. + /// + public static ReadOnlySpan AsSpanOrDefault(this string? text, Range range) { - if (s is null) + if (text is null) { return default; } #if NET - return MemoryExtensions.AsSpan(s, range); + return MemoryExtensions.AsSpan(text, range); #else - var (start, length) = range.GetOffsetAndLength(s.Length); - return s.AsSpan(start, length); + var (start, length) = range.GetOffsetAndLength(text.Length); + return text.AsSpan(start, length); #endif } - public static ReadOnlyMemory AsMemory(this string? s, Index startIndex) + /// + /// Creates a new over a portion of a target string starting at a specified index. + /// + /// + /// The target string. + /// + /// + /// The index at which to begin this slice. + /// + /// + /// This uses Razor's type, which is type-forwarded on .NET. + /// + /// + /// is less than 0 or greater than .Length. + /// + public static ReadOnlyMemory AsMemory(this string? text, Index startIndex) { #if NET - return MemoryExtensions.AsMemory(s, startIndex); + return MemoryExtensions.AsMemory(text, startIndex); #else - if (s is null) + if (text is null) { if (!startIndex.Equals(Index.Start)) { @@ -109,65 +256,151 @@ public static ReadOnlyMemory AsMemory(this string? s, Index startIndex) return default; } - return s.AsMemory(startIndex.GetOffset(s.Length)); + return text.AsMemory(startIndex.GetOffset(text.Length)); #endif } - public static ReadOnlyMemory AsMemory(this string? s, Range range) + /// + /// Creates a new over a portion of a target string using + /// the range start and end indexes. + /// + /// + /// The target string. + /// + /// + /// The range that has start and end indexes to use for slicing the string. + /// + /// + /// This uses Razor's type, which is type-forwarded on .NET. + /// + /// + /// 's start or end index is not within the bounds of the string. + /// + /// + /// 's start index is greater than its end index. + /// + public static ReadOnlyMemory AsMemory(this string? text, Range range) { #if NET - return MemoryExtensions.AsMemory(s, range); + return MemoryExtensions.AsMemory(text, range); #else - if (s is null) + if (text is null) { if (!range.Start.Equals(Index.Start) || !range.End.Equals(Index.Start)) { - ThrowHelper.ThrowArgumentNull(nameof(s)); + ThrowHelper.ThrowArgumentNull(nameof(text)); } return default; } - var (start, length) = range.GetOffsetAndLength(s.Length); - return s.AsMemory(start, length); + var (start, length) = range.GetOffsetAndLength(text.Length); + return text.AsMemory(start, length); #endif } - public static ReadOnlyMemory AsMemoryOrDefault(this string? s) - => s is not null ? s.AsMemory() : default; + /// + /// Creates a new over a string. If the target string + /// is a () is returned. + /// + /// + /// The target string. + /// + public static ReadOnlyMemory AsMemoryOrDefault(this string? text) + => text is not null ? text.AsMemory() : default; - public static ReadOnlyMemory AsMemoryOrDefault(this string? s, int start) - => s is not null ? s.AsMemory(start) : default; + /// + /// Creates a new over a portion of the target string from + /// a specified position to the end of the string. If the target string is + /// a () is returned. + /// + /// + /// The target string. + /// + /// + /// The index at which to begin this slice. + /// + /// + /// is less than 0 or greater than .Length. + /// + public static ReadOnlyMemory AsMemoryOrDefault(this string? text, int start) + => text is not null ? text.AsMemory(start) : default; - public static ReadOnlyMemory AsMemoryOrDefault(this string? s, int start, int length) - => s is not null ? s.AsMemory(start, length) : default; + /// + /// Creates a new over a portion of the target string from + /// a specified position for a specified number of characters. If the target string is + /// a () is returned. + /// + /// + /// The target string. + /// + /// + /// The index at which to begin this slice. + /// + /// + /// The desired length for the slice. + /// + /// + /// , , or + + /// is not in the range of . + /// + public static ReadOnlyMemory AsMemoryOrDefault(this string? text, int start, int length) + => text is not null ? text.AsMemory(start, length) : default; - public static ReadOnlyMemory AsMemoryOrDefault(this string? s, Index startIndex) + /// + /// Creates a new over a portion of the target string from + /// a specified position to the end of the string. If the target string is + /// a () is returned. + /// + /// + /// The target string. + /// + /// + /// The index at which to begin this slice. + /// + public static ReadOnlyMemory AsMemoryOrDefault(this string? text, Index startIndex) { - if (s is null) + if (text is null) { return default; } #if NET - return MemoryExtensions.AsMemory(s, startIndex); + return MemoryExtensions.AsMemory(text, startIndex); #else - return s.AsMemory(startIndex.GetOffset(s.Length)); + return text.AsMemory(startIndex.GetOffset(text.Length)); #endif } - public static ReadOnlyMemory AsMemoryOrDefault(this string? s, Range range) + /// + /// Creates a new over a portion of the target string using the range + /// start and end indexes. If the target string is a + /// () is returned. + /// + /// + /// The target string. + /// + /// + /// The range that has start and end indexes to use for slicing the string. + /// + /// + /// 's start or end index is not within the bounds of the string. + /// + /// + /// 's start index is greater than its end index. + /// + public static ReadOnlyMemory AsMemoryOrDefault(this string? text, Range range) { - if (s is null) + if (text is null) { return default; } #if NET - return MemoryExtensions.AsMemory(s, range); + return MemoryExtensions.AsMemory(text, range); #else - var (start, length) = range.GetOffsetAndLength(s.Length); - return s.AsMemory(start, length); + var (start, length) = range.GetOffsetAndLength(text.Length); + return text.AsMemory(start, length); #endif } From 19f2f7fe0d222367a7193833406c34eacd58d263 Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Tue, 16 Jul 2024 10:23:08 -0700 Subject: [PATCH 07/30] Add array AsSpan and AsMemory extensions that use Razor's polyfill types --- .../ArrayExtensions.cs | 175 ++++++++++++++++++ 1 file changed, 175 insertions(+) diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ArrayExtensions.cs b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ArrayExtensions.cs index 2663e636bd7..f09b4c8176e 100644 --- a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ArrayExtensions.cs +++ b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ArrayExtensions.cs @@ -1,13 +1,188 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT license. See License.txt in the project root for license information. +using System; using System.Collections.Generic; using System.Collections.Immutable; +#if !NET +using ThrowHelper = Microsoft.AspNetCore.Razor.Utilities.ThrowHelper; +#endif + namespace Microsoft.AspNetCore.Razor; internal static class ArrayExtensions { + /// + /// Creates a new span over the portion of the target array defined by an value. + /// + /// + /// The array to convert. + /// + /// + /// The starting index. + /// + /// + /// This uses Razor's type, which is type-forwarded on .NET. + /// + /// + /// is . + /// + /// + /// is less than 0 or greater than .Length. + /// + /// + /// is covariant, and the array's type is not exactly []. + /// + public static ReadOnlySpan AsSpan(this T[]? array, Index startIndex) + { +#if NET + return MemoryExtensions.AsSpan(array, startIndex); +#else + if (array is null) + { + if (!startIndex.Equals(Index.Start)) + { + ThrowHelper.ThrowArgumentOutOfRange(nameof(startIndex)); + } + + return default; + } + + return MemoryExtensions.AsSpan(array, startIndex.GetOffset(array.Length)); +#endif + } + + /// + /// Creates a new span over the portion of the target array defined by a value. + /// + /// + /// The array to convert. + /// + /// + /// The range of the array to convert. + /// + /// + /// This uses Razor's type, which is type-forwarded on .NET. + /// + /// + /// is . + /// + /// + /// 's start or end index is not within the bounds of the string. + /// + /// + /// 's start index is greater than its end index. + /// + /// + /// is covariant, and the array's type is not exactly []. + /// + public static ReadOnlySpan AsSpan(this T[]? array, Range range) + { +#if NET + return MemoryExtensions.AsSpan(array, range); +#else + if (array is null) + { + if (!range.Start.Equals(Index.Start) || !range.End.Equals(Index.Start)) + { + ThrowHelper.ThrowArgumentNull(nameof(array)); + } + + return default; + } + + var (start, length) = range.GetOffsetAndLength(array.Length); + return MemoryExtensions.AsSpan(array, start, length); +#endif + } + + /// + /// Creates a new memory region over the portion of the target starting at the specified index + /// to the end of the array. + /// + /// + /// The array to convert. + /// + /// + /// The first position of the array. + /// + /// + /// This uses Razor's type, which is type-forwarded on .NET. + /// + /// + /// is . + /// + /// + /// is less than 0 or greater than .Length. + /// + /// + /// is covariant, and the array's type is not exactly []. + /// + public static ReadOnlyMemory AsMemory(this T[]? array, Index startIndex) + { +#if NET + return MemoryExtensions.AsMemory(array, startIndex); +#else + if (array is null) + { + if (!startIndex.Equals(Index.Start)) + { + ThrowHelper.ThrowArgumentOutOfRange(nameof(startIndex)); + } + + return default; + } + + return MemoryExtensions.AsMemory(array, startIndex.GetOffset(array.Length)); +#endif + } + + /// + /// Creates a new memory region over the portion of the target array beginning at + /// inclusive start index of the range and ending at the exclusive end index of the range. + /// + /// + /// The array to convert. + /// + /// + /// The range of the array to convert. + /// + /// + /// This uses Razor's type, which is type-forwarded on .NET. + /// + /// + /// is . + /// + /// + /// 's start or end index is not within the bounds of the string. + /// + /// + /// 's start index is greater than its end index. + /// + /// + /// is covariant, and the array's type is not exactly []. + /// + public static ReadOnlyMemory AsMemory(this T[]? array, Range range) + { +#if NET + return MemoryExtensions.AsMemory(array, range); +#else + if (array is null) + { + if (!range.Start.Equals(Index.Start) || !range.End.Equals(Index.Start)) + { + ThrowHelper.ThrowArgumentNull(nameof(array)); + } + + return default; + } + + var (start, length) = range.GetOffsetAndLength(array.Length); + return MemoryExtensions.AsMemory(array, start, length); +#endif + } + public static ImmutableDictionary ToImmutableDictionary( this (TKey key, TValue value)[] array, IEqualityComparer keyComparer) where TKey : notnull From 90cbe54474deca5d2844e6714d69fb484858c8c5 Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Tue, 16 Jul 2024 10:28:25 -0700 Subject: [PATCH 08/30] Remote DrainToImmutable() extension method Since moving to System.Collections.Immutable 8.0, this extension method is no longer needed. --- .../ImmutableArrayExtensions.cs | 33 ------------------- 1 file changed, 33 deletions(-) diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs index e0d7478cca7..3107a833101 100644 --- a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs +++ b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs @@ -29,39 +29,6 @@ public static void SetCapacityIfLarger(this ImmutableArray.Builder builder } } - /// - /// Returns the current contents as an and sets - /// the collection to a zero length array. - /// - /// - /// If equals - /// , the internal array will be extracted - /// as an without copying the contents. Otherwise, the - /// contents will be copied into a new array. The collection will then be set to a - /// zero-length array. - /// - /// An immutable array. - public static ImmutableArray DrainToImmutable(this ImmutableArray.Builder builder) - { -#if NET8_0_OR_GREATER - return builder.DrainToImmutable(); -#else - if (builder.Count == 0) - { - return []; - } - - if (builder.Count == builder.Capacity) - { - return builder.MoveToImmutable(); - } - - var result = builder.ToImmutable(); - builder.Clear(); - return result; -#endif - } - public static ImmutableArray SelectAsArray(this ImmutableArray source, Func selector) { return source switch From e67fab8002fc552c3adf008abc0fb0dddd5646ba Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Tue, 16 Jul 2024 14:13:24 -0700 Subject: [PATCH 09/30] Add missing `ReturnAsync` for mock --- .../Documents/VisualStudioFileChangeTrackerTest.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Documents/VisualStudioFileChangeTrackerTest.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Documents/VisualStudioFileChangeTrackerTest.cs index 2b75b031509..b708a37e657 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Documents/VisualStudioFileChangeTrackerTest.cs +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Documents/VisualStudioFileChangeTrackerTest.cs @@ -79,6 +79,7 @@ public async Task StopListening_UnadvisesForFileChange() .Verifiable(); fileChangeService .Setup(f => f.UnadviseFileChangeAsync(123, It.IsAny())) + .ReturnsAsync(TestProjectData.SomeProjectImportFile.FilePath) .Verifiable(); var tracker = new VisualStudioFileChangeTracker( TestProjectData.SomeProjectImportFile.FilePath, From 2edc20169db3a0d42f710a7b9900cfc076640636 Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Tue, 16 Jul 2024 14:28:18 -0700 Subject: [PATCH 10/30] Move CreateTextLoader helper to TestMocks class --- .../TagHelperTooltipFactoryBaseTest.cs | 15 +++++----- .../DocumentContextFactoryTest.cs | 6 ++-- .../LanguageServer/LanguageServerTestBase.cs | 18 ------------ .../TestMocks.cs | 29 +++++++++++++++++++ .../RazorProjectInfoDriverTest.cs | 13 +++++---- 5 files changed, 47 insertions(+), 34 deletions(-) create mode 100644 src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/TestMocks.cs diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/TagHelperTooltipFactoryBaseTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/TagHelperTooltipFactoryBaseTest.cs index 7a635a05f7a..e5ccc0e268d 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/TagHelperTooltipFactoryBaseTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/TagHelperTooltipFactoryBaseTest.cs @@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Razor.Language.Components; using Microsoft.AspNetCore.Razor.LanguageServer.Tooltip; using Microsoft.AspNetCore.Razor.ProjectSystem; +using Microsoft.AspNetCore.Razor.Test.Common; using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer; using Microsoft.CodeAnalysis.Razor.ProjectSystem; using Roslyn.Test.Utilities; @@ -374,7 +375,7 @@ await projectManager.UpdateAsync(updater => { updater.ProjectAdded(hostProject); updater.ProjectWorkspaceStateChanged(hostProject.Key, projectWorkspaceState); - updater.DocumentAdded(hostProject.Key, hostDocument, CreateTextLoader(hostDocument.FilePath, text: "")); + updater.DocumentAdded(hostProject.Key, hostDocument, TestMocks.CreateTextLoader(hostDocument.FilePath, text: "")); }); var service = new TestTagHelperToolTipFactory(projectManager); @@ -419,11 +420,11 @@ await projectManager.UpdateAsync(updater => { updater.ProjectAdded(hostProject1); updater.ProjectWorkspaceStateChanged(hostProject1.Key, projectWorkspaceState); - updater.DocumentAdded(hostProject1.Key, hostDocument, CreateTextLoader(hostDocument.FilePath, text: "")); + updater.DocumentAdded(hostProject1.Key, hostDocument, TestMocks.CreateTextLoader(hostDocument.FilePath, text: "")); updater.ProjectAdded(hostProject2); updater.ProjectWorkspaceStateChanged(hostProject2.Key, projectWorkspaceState); - updater.DocumentAdded(hostProject2.Key, hostDocument, CreateTextLoader(hostDocument.FilePath, text: "")); + updater.DocumentAdded(hostProject2.Key, hostDocument, TestMocks.CreateTextLoader(hostDocument.FilePath, text: "")); }); var service = new TestTagHelperToolTipFactory(projectManager); @@ -468,10 +469,10 @@ await projectManager.UpdateAsync(updater => { updater.ProjectAdded(hostProject1); updater.ProjectWorkspaceStateChanged(hostProject1.Key, projectWorkspaceState); - updater.DocumentAdded(hostProject1.Key, hostDocument, CreateTextLoader(hostDocument.FilePath, text: "")); + updater.DocumentAdded(hostProject1.Key, hostDocument, TestMocks.CreateTextLoader(hostDocument.FilePath, text: "")); updater.ProjectAdded(hostProject2); - updater.DocumentAdded(hostProject2.Key, hostDocument, CreateTextLoader(hostDocument.FilePath, text: "")); + updater.DocumentAdded(hostProject2.Key, hostDocument, TestMocks.CreateTextLoader(hostDocument.FilePath, text: "")); }); var service = new TestTagHelperToolTipFactory(projectManager); @@ -512,10 +513,10 @@ public async Task GetAvailableProjects_NotAvailableInAnyProject_ReturnsText() await projectManager.UpdateAsync(updater => { updater.ProjectAdded(hostProject1); - updater.DocumentAdded(hostProject1.Key, hostDocument, CreateTextLoader(hostDocument.FilePath, text: "")); + updater.DocumentAdded(hostProject1.Key, hostDocument, TestMocks.CreateTextLoader(hostDocument.FilePath, text: "")); updater.ProjectAdded(hostProject2); - updater.DocumentAdded(hostProject2.Key, hostDocument, CreateTextLoader(hostDocument.FilePath, text: "")); + updater.DocumentAdded(hostProject2.Key, hostDocument, TestMocks.CreateTextLoader(hostDocument.FilePath, text: "")); }); var service = new TestTagHelperToolTipFactory(projectManager); diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/DocumentContextFactoryTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/DocumentContextFactoryTest.cs index 3c549217fe2..31394f347d2 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/DocumentContextFactoryTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/DocumentContextFactoryTest.cs @@ -67,7 +67,7 @@ public async Task TryCreateForOpenDocumentAsync_CanNotResolveVersion_ReturnsNull await _projectManager.UpdateAsync(updater => { - updater.DocumentAdded(MiscFilesHostProject.Instance.Key, hostDocument, CreateTextLoader(filePath, "")); + updater.DocumentAdded(MiscFilesHostProject.Instance.Key, hostDocument, TestMocks.CreateTextLoader(filePath, "")); }); var factory = new DocumentContextFactory(_projectManager, _documentVersionCache, LoggerFactory); @@ -87,7 +87,7 @@ public async Task TryCreateAsync_ResolvesContent() await _projectManager.UpdateAsync(updater => { - updater.DocumentAdded(MiscFilesHostProject.Instance.Key, hostDocument, CreateTextLoader(filePath, "")); + updater.DocumentAdded(MiscFilesHostProject.Instance.Key, hostDocument, TestMocks.CreateTextLoader(filePath, "")); }); var miscFilesProject = _projectManager.GetMiscellaneousProject(); @@ -142,7 +142,7 @@ public async Task TryCreateForOpenDocumentAsync_ResolvesContent() await _projectManager.UpdateAsync(updater => { - updater.DocumentAdded(MiscFilesHostProject.Instance.Key, hostDocument, CreateTextLoader(filePath, "")); + updater.DocumentAdded(MiscFilesHostProject.Instance.Key, hostDocument, TestMocks.CreateTextLoader(filePath, "")); }); var miscFilesProject = _projectManager.GetMiscellaneousProject(); diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/LanguageServer/LanguageServerTestBase.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/LanguageServer/LanguageServerTestBase.cs index b8a4e215aad..e7e0f6e87e2 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/LanguageServer/LanguageServerTestBase.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/LanguageServer/LanguageServerTestBase.cs @@ -24,7 +24,6 @@ using Microsoft.CodeAnalysis.Text; using Microsoft.CommonLanguageServerProtocol.Framework; using Microsoft.VisualStudio.LanguageServer.Protocol; -using Moq; using Xunit.Abstractions; namespace Microsoft.AspNetCore.Razor.Test.Common.LanguageServer; @@ -131,23 +130,6 @@ private protected static VersionedDocumentContext CreateDocumentContext(Uri uri, return new VersionedDocumentContext(uri, snapshot, projectContext: null, version: 0); } - protected static TextLoader CreateTextLoader(string filePath, string text) - { - return CreateTextLoader(filePath, SourceText.From(text)); - } - - protected static TextLoader CreateTextLoader(string filePath, SourceText text) - { - var mock = new StrictMock(); - - var textAndVersion = TextAndVersion.Create(text, VersionStamp.Create(), filePath); - - mock.Setup(x => x.LoadTextAndVersionAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(textAndVersion); - - return mock.Object; - } - private protected static RazorLSPOptionsMonitor GetOptionsMonitor(bool enableFormatting = true, bool autoShowCompletion = true, bool autoListParams = true, bool formatOnType = true, bool autoInsertAttributeQuotes = true, bool colorBackground = false, bool codeBlockBraceOnNextLine = false, bool commitElementsWithSpace = true) { var configService = StrictMock.Of(); diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/TestMocks.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/TestMocks.cs new file mode 100644 index 00000000000..1e0a3dbd2e7 --- /dev/null +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/TestMocks.cs @@ -0,0 +1,29 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT license. See License.txt in the project root for license information. + +using System.Threading; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; +using Moq; + +namespace Microsoft.AspNetCore.Razor.Test.Common; + +internal static class TestMocks +{ + public static TextLoader CreateTextLoader(string filePath, string text) + { + return CreateTextLoader(filePath, SourceText.From(text)); + } + + public static TextLoader CreateTextLoader(string filePath, SourceText text) + { + var mock = new StrictMock(); + + var textAndVersion = TextAndVersion.Create(text, VersionStamp.Create(), filePath); + + mock.Setup(x => x.LoadTextAndVersionAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(textAndVersion); + + return mock.Object; + } +} diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/LanguageClient/ProjectSystem/RazorProjectInfoDriverTest.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/LanguageClient/ProjectSystem/RazorProjectInfoDriverTest.cs index 6ff9e9b0675..1cf6ee0b788 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/LanguageClient/ProjectSystem/RazorProjectInfoDriverTest.cs +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/LanguageClient/ProjectSystem/RazorProjectInfoDriverTest.cs @@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem; using Microsoft.AspNetCore.Razor.ProjectSystem; +using Microsoft.AspNetCore.Razor.Test.Common; using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer; using Microsoft.CodeAnalysis.Razor.ProjectSystem; using Xunit; @@ -41,10 +42,10 @@ public async Task ProcessesExistingProjectsDuringInitialization() await projectManager.UpdateAsync(static updater => { updater.ProjectAdded(s_hostProject1); - updater.DocumentAdded(s_hostProject1.Key, s_hostDocument1, CreateTextLoader(s_hostDocument1.FilePath, "

Hello World

")); + updater.DocumentAdded(s_hostProject1.Key, s_hostDocument1, TestMocks.CreateTextLoader(s_hostDocument1.FilePath, "

Hello World

")); updater.ProjectAdded(s_hostProject2); - updater.DocumentAdded(s_hostProject2.Key, s_hostDocument2, CreateTextLoader(s_hostDocument2.FilePath, "

Hello World

")); + updater.DocumentAdded(s_hostProject2.Key, s_hostDocument2, TestMocks.CreateTextLoader(s_hostDocument2.FilePath, "

Hello World

")); }); var (driver, testAccessor) = await CreateDriverAndInitializeAsync(projectManager); @@ -93,10 +94,10 @@ public async Task ProcessesProjectsAddedAfterInitialization() await projectManager.UpdateAsync(static updater => { updater.ProjectAdded(s_hostProject1); - updater.DocumentAdded(s_hostProject1.Key, s_hostDocument1, CreateTextLoader(s_hostDocument1.FilePath, "

Hello World

")); + updater.DocumentAdded(s_hostProject1.Key, s_hostDocument1, TestMocks.CreateTextLoader(s_hostDocument1.FilePath, "

Hello World

")); updater.ProjectAdded(s_hostProject2); - updater.DocumentAdded(s_hostProject2.Key, s_hostDocument2, CreateTextLoader(s_hostDocument2.FilePath, "

Hello World

")); + updater.DocumentAdded(s_hostProject2.Key, s_hostDocument2, TestMocks.CreateTextLoader(s_hostDocument2.FilePath, "

Hello World

")); }); await testAccessor.WaitUntilCurrentBatchCompletesAsync(); @@ -134,7 +135,7 @@ await projectManager.UpdateAsync(static updater => await projectManager.UpdateAsync(static updater => { - updater.DocumentAdded(s_hostProject1.Key, s_hostDocument1, CreateTextLoader(s_hostDocument1.FilePath, "

Hello World

")); + updater.DocumentAdded(s_hostProject1.Key, s_hostDocument1, TestMocks.CreateTextLoader(s_hostDocument1.FilePath, "

Hello World

")); }); await testAccessor.WaitUntilCurrentBatchCompletesAsync(); @@ -204,7 +205,7 @@ await projectManager.UpdateAsync(static updater => await projectManager.UpdateAsync(static updater => { - updater.DocumentAdded(s_hostProject1.Key, s_hostDocument1, CreateTextLoader(s_hostDocument1.FilePath, "

Hello World

")); + updater.DocumentAdded(s_hostProject1.Key, s_hostDocument1, TestMocks.CreateTextLoader(s_hostDocument1.FilePath, "

Hello World

")); }); await testAccessor.WaitUntilCurrentBatchCompletesAsync(); From d96c004800d2336f85371647369993e86f30fe8b Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Tue, 16 Jul 2024 15:27:39 -0700 Subject: [PATCH 11/30] Fix BackgroundDocumentGeneratorTest that was throwing exception The ProcessWorkAndRestart test was throwing an exception and swallowing it because a document was added to a project when the document's file path was outside of the project's. --- .../BackgroundDocumentGeneratorTest.cs | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/DocumentGenerator/BackgroundDocumentGeneratorTest.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/DocumentGenerator/BackgroundDocumentGeneratorTest.cs index 45628cfe6af..67a071ed8a7 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/DocumentGenerator/BackgroundDocumentGeneratorTest.cs +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/DocumentGenerator/BackgroundDocumentGeneratorTest.cs @@ -14,14 +14,17 @@ using Microsoft.AspNetCore.Razor.ProjectSystem; using Microsoft.AspNetCore.Razor.Test.Common; using Microsoft.AspNetCore.Razor.Test.Common.VisualStudio; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Razor; using Microsoft.CodeAnalysis.Razor.Logging; +using Microsoft.CodeAnalysis.Razor.ProjectSystem; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Razor.DynamicFiles; using Moq; using Xunit; using Xunit.Abstractions; -namespace Microsoft.CodeAnalysis.Razor.ProjectSystem; +namespace Microsoft.VisualStudio.Razor.ProjectSystem; // These tests are really integration tests. There isn't a good way to unit test this functionality since // the only thing in here is threading. @@ -219,27 +222,32 @@ await projectManager.UpdateAsync(updater => [UIFact] public async Task ProcessWorkAndRestart() { + var hostProject1 = TestProjectData.SomeProject; + var hostProject2 = TestProjectData.AnotherProject; + var hostDocument1 = TestProjectData.SomeProjectFile1; + var hostDocument2 = TestProjectData.SomeProjectFile2; + // Arrange var projectManager = CreateProjectSnapshotManager(); await projectManager.UpdateAsync(updater => { - updater.ProjectAdded(s_hostProject1); - updater.ProjectAdded(s_hostProject2); - updater.DocumentAdded(s_hostProject1.Key, s_documents[0], null!); - updater.DocumentAdded(s_hostProject1.Key, s_documents[1], null!); + updater.ProjectAdded(hostProject1); + updater.ProjectAdded(hostProject2); + updater.DocumentAdded(hostProject1.Key, hostDocument1, null!); + updater.DocumentAdded(hostProject1.Key, hostDocument2, null!); }); - var project = projectManager.GetLoadedProject(s_hostProject1.Key); - var documentKey1 = new DocumentKey(project.Key, s_documents[0].FilePath); - var documentKey2 = new DocumentKey(project.Key, s_documents[1].FilePath); + var project = projectManager.GetLoadedProject(hostProject1.Key); + var documentKey1 = new DocumentKey(project.Key, hostDocument1.FilePath); + var documentKey2 = new DocumentKey(project.Key, hostDocument2.FilePath); using var generator = new TestBackgroundDocumentGenerator(projectManager, _dynamicFileInfoProvider, LoggerFactory); // Act & Assert // First, enqueue some work. - generator.Enqueue(project, project.GetDocument(s_documents[0].FilePath).AssumeNotNull()); + generator.Enqueue(project, project.GetDocument(hostDocument1.FilePath).AssumeNotNull()); // Wait for the work to complete. await generator.WaitUntilCurrentBatchCompletesAsync(); @@ -248,14 +256,14 @@ await projectManager.UpdateAsync(updater => Assert.Single(generator.CompletedWork, documentKey1); // Enqueue more work. - generator.Enqueue(project, project.GetDocument(s_documents[1].FilePath).AssumeNotNull()); + generator.Enqueue(project, project.GetDocument(hostDocument2.FilePath).AssumeNotNull()); // Wait for the work to complete. await generator.WaitUntilCurrentBatchCompletesAsync(); Assert.Collection(generator.CompletedWork.OrderBy(key => key.DocumentFilePath), - key => Assert.Equal(documentKey2, key), - key => Assert.Equal(documentKey1, key)); + key => Assert.Equal(documentKey1, key), + key => Assert.Equal(documentKey2, key)); } [UIFact] From 3138fb0be600b4b86a64cb21b9cd9deae8341162 Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Tue, 16 Jul 2024 15:38:43 -0700 Subject: [PATCH 12/30] Do not swallow test logging errors that occur outside of tests --- .../Logging/TestOutputLogger.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/Logging/TestOutputLogger.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/Logging/TestOutputLogger.cs index f0cc8891511..ac0cfbee1de 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/Logging/TestOutputLogger.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/Logging/TestOutputLogger.cs @@ -74,10 +74,6 @@ public void Log(LogLevel logLevel, string message, Exception? exception) { _provider.TestOutputHelper.WriteLine(finalMessage); } - catch (InvalidOperationException iex) when (iex.Message == "There is no currently active test.") - { - // Ignore, something is logging a message outside of a test. Other loggers will capture it. - } catch (Exception ex) { // If an exception is thrown while writing a message, throw an AggregateException that includes From 214d921112a0516a1d0a98a65dd0c1ecd32387df Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Tue, 16 Jul 2024 15:41:19 -0700 Subject: [PATCH 13/30] Ensure that we log exceptions during tests --- .../Logging/TestOutputLogger.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/Logging/TestOutputLogger.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/Logging/TestOutputLogger.cs index ac0cfbee1de..3db5c90b6f4 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/Logging/TestOutputLogger.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/Logging/TestOutputLogger.cs @@ -73,6 +73,11 @@ public void Log(LogLevel logLevel, string message, Exception? exception) try { _provider.TestOutputHelper.WriteLine(finalMessage); + + if (exception is not null) + { + _provider.TestOutputHelper.WriteLine(exception.ToString()); + } } catch (Exception ex) { From 59aa56d3962f7f4e29a249a3b3581ff3bb20a4c8 Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Tue, 16 Jul 2024 17:11:34 -0700 Subject: [PATCH 14/30] Don't read past end of span in NormalizeAndDedupeSlashes --- .../Utilities/FilePathNormalizer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/FilePathNormalizer.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/FilePathNormalizer.cs index 54bd1ac5c91..2fa1426795d 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/FilePathNormalizer.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/FilePathNormalizer.cs @@ -253,7 +253,8 @@ private static void NormalizeAndDedupeSlashes(Span span, ref int charsWrit { writeSlot = '/'; - if (Unsafe.Add(ref readSlot, 1) is '/' or '\\') + // Be careful not to read past the end of the span. + if (read < charsWritten - 1 && Unsafe.Add(ref readSlot, 1) is '/' or '\\') { // We found two slashes in a row. If we are at the start of the path, // we we are at '\\network' paths, so want to leave them alone. Otherwise From 6d76c6d52f629cfc2151dcbcdaecb2639ded1d2e Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Tue, 16 Jul 2024 17:13:05 -0700 Subject: [PATCH 15/30] Add "/" as special case when normalizing directories --- .../Utilities/FilePathNormalizer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/FilePathNormalizer.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/FilePathNormalizer.cs index 2fa1426795d..e632c7c5add 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/FilePathNormalizer.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/FilePathNormalizer.cs @@ -18,7 +18,7 @@ internal static class FilePathNormalizer public static string NormalizeDirectory(string? directoryFilePath) { - if (directoryFilePath.IsNullOrEmpty()) + if (directoryFilePath.IsNullOrEmpty() || directoryFilePath == "/") { return "/"; } @@ -76,7 +76,7 @@ public static string Normalize(string? filePath) ///
public static string GetNormalizedDirectoryName(string? filePath) { - if (filePath.IsNullOrEmpty()) + if (filePath.IsNullOrEmpty() || filePath == "/") { return "/"; } From b8f14d39bffd11f65b4fc140a69d5413b7c53d8d Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Mon, 15 Jul 2024 18:18:59 -0700 Subject: [PATCH 16/30] Fix issue with cancelling AsyncBatchingWorkQueue This was simply a mistake on my part. We should not assume that code is unreachable when it is reached because an OperationCanceledException is caught. --- .../Utilities/AsyncBatchingWorkQueue`2.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/AsyncBatchingWorkQueue`2.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/AsyncBatchingWorkQueue`2.cs index 2565ce36abc..23fb11979b7 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/AsyncBatchingWorkQueue`2.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/AsyncBatchingWorkQueue`2.cs @@ -271,9 +271,8 @@ void AddItemsToBatch(IEnumerable items) catch (OperationCanceledException) { // Silently continue to allow the next batch to be processed. + return default; } - - return Assumed.Unreachable(); } private (ImmutableArray items, CancellationToken batchCancellationToken) GetNextBatchAndResetQueue() From 78859c75f201e39b0708cb49e2c1ed3dcaf5b732 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Tue, 16 Jul 2024 14:59:01 +1000 Subject: [PATCH 17/30] Fix bad assumption in tests --- .../Cohost/HtmlDocumentSynchronizerTest.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/HtmlDocumentSynchronizerTest.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/HtmlDocumentSynchronizerTest.cs index e6683df3ac7..6e31aeef793 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/HtmlDocumentSynchronizerTest.cs +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/HtmlDocumentSynchronizerTest.cs @@ -40,10 +40,6 @@ public async Task TrySynchronize_NewDocument_Generates() Assert.True(await synchronizer.TrySynchronizeAsync(document, DisposalToken)); - var version = await RazorDocumentVersion.CreateAsync(document, DisposalToken); - - Assert.Equal(1, version.WorkspaceVersion); - Assert.Collection(publisher.Publishes, i => { From 2b28abc90695abe064bf8a1c7c86d1916ccc6150 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Tue, 16 Jul 2024 14:59:07 +1000 Subject: [PATCH 18/30] Remove statics --- .../ProjectSystem/DocumentSnapshotFactory.cs | 2 +- .../ProjectSystem/ProjectSnapshotFactory.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/ProjectSystem/DocumentSnapshotFactory.cs b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/ProjectSystem/DocumentSnapshotFactory.cs index 241458f1efc..d5c6adac062 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/ProjectSystem/DocumentSnapshotFactory.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/ProjectSystem/DocumentSnapshotFactory.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.Remote.Razor.ProjectSystem; [method: ImportingConstructor] internal class DocumentSnapshotFactory(Lazy projectSnapshotFactory) { - private static readonly ConditionalWeakTable _documentSnapshots = new(); + private readonly ConditionalWeakTable _documentSnapshots = new(); private readonly Lazy _projectSnapshotFactory = projectSnapshotFactory; diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/ProjectSystem/ProjectSnapshotFactory.cs b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/ProjectSystem/ProjectSnapshotFactory.cs index f5b16a49e51..e57436af364 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/ProjectSystem/ProjectSnapshotFactory.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/ProjectSystem/ProjectSnapshotFactory.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.Remote.Razor.ProjectSystem; [method: ImportingConstructor] internal class ProjectSnapshotFactory(DocumentSnapshotFactory documentSnapshotFactory, ITelemetryReporter telemetryReporter) { - private static readonly ConditionalWeakTable _projectSnapshots = new(); + private readonly ConditionalWeakTable _projectSnapshots = new(); private readonly DocumentSnapshotFactory _documentSnapshotFactory = documentSnapshotFactory; private readonly ITelemetryReporter _telemetryReporter = telemetryReporter; From 98c64945f0af082f6ee99e95d3b4c155c6f8f9f4 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Tue, 16 Jul 2024 14:59:33 +1000 Subject: [PATCH 19/30] Use the test solution store to actual map solution infos to solutions --- .../Cohost/TestBrokeredServiceInterceptor.cs | 23 +++++++++++++------ .../Cohost/TestRemoteServiceInvoker.cs | 6 +---- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/TestBrokeredServiceInterceptor.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/TestBrokeredServiceInterceptor.cs index 4c8e70ed2ea..c4a9b8d411e 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/TestBrokeredServiceInterceptor.cs +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/TestBrokeredServiceInterceptor.cs @@ -4,21 +4,20 @@ using System; using System.Threading; using System.Threading.Tasks; -using Microsoft.AspNetCore.Razor; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.ExternalAccess.Razor; +using Microsoft.CodeAnalysis.ExternalAccess.Razor.Testing; using Microsoft.CodeAnalysis.Remote.Razor; +using Xunit; namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost; internal sealed class TestBrokeredServiceInterceptor : IRazorBrokeredServiceInterceptor { - private Solution? _solution; + private readonly TestSolutionStore _solutionStore = new(); - public void UpdateSolution(Solution solution) - { - _solution = solution; - } + public Task GetSolutionInfoAsync(Solution solution, CancellationToken cancellationToken) + => _solutionStore.AddAsync(solution, cancellationToken); public ValueTask RunServiceAsync( Func implementation, @@ -29,5 +28,15 @@ public ValueTask RunServiceAsync( RazorPinnedSolutionInfoWrapper solutionInfo, Func> implementation, CancellationToken cancellationToken) - => implementation(_solution.AssumeNotNull()); + { + var solution = _solutionStore.Get(solutionInfo); + + Assert.NotNull(solution); + + return implementation(solution); + } + + public void Dispose() + { + } } diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/TestRemoteServiceInvoker.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/TestRemoteServiceInvoker.cs index 52f5af1e270..9e5c8651aac 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/TestRemoteServiceInvoker.cs +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/TestRemoteServiceInvoker.cs @@ -53,11 +53,7 @@ private async Task GetOrCreateServiceAsync() { var service = await GetOrCreateServiceAsync(); - // In an ideal world we'd be able to maintain a dictionary of solution checksums in TestServiceBroker, and use - // the RazorPinnedSolutionInfoWrapper properly, but we need Roslyn changes for that. For now, this works fine - // as we don't have any code that makes multiple parallel calls to TryInvokeAsync in the same test. - var solutionInfo = new RazorPinnedSolutionInfoWrapper(); - _serviceInterceptor.UpdateSolution(solution); + var solutionInfo = await _serviceInterceptor.GetSolutionInfoAsync(solution, cancellationToken); return await invocation(service, solutionInfo, cancellationToken); } From 19f2329629679877d0b0f9137b19ef2f936ba0d5 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Tue, 16 Jul 2024 14:59:42 +1000 Subject: [PATCH 20/30] Respond to CLaSP changes --- .../RazorLanguageServerTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorLanguageServerTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorLanguageServerTest.cs index 70477b6bb44..9db1b0b0868 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorLanguageServerTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorLanguageServerTest.cs @@ -40,14 +40,14 @@ public async Task LocaleIsSetCorrectly() Locale = "de-DE" }; - await queue.ExecuteAsync(initializeParams, Methods.InitializeName, LanguageServerConstants.DefaultLanguageName, server.GetLspServices(), DisposalToken); + await queue.ExecuteAsync(initializeParams, Methods.InitializeName, server.GetLspServices(), DisposalToken); // We have to send one more request, because culture is set before any request starts, but the first initialize request has to // be started in order to set the culture. // The request isn't actually valid, so we wrap it in a try catch, but we don't care for this test try { - await queue.ExecuteAsync(new(), VSInternalMethods.DocumentPullDiagnosticName, LanguageServerConstants.DefaultLanguageName, server.GetLspServices(), DisposalToken); + await queue.ExecuteAsync(new(), VSInternalMethods.DocumentPullDiagnosticName, server.GetLspServices(), DisposalToken); } catch { } From 43f12e84252979d073ba17112b9cc978dc622889 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Wed, 17 Jul 2024 07:26:24 +1000 Subject: [PATCH 21/30] Apply suggestions from code review --- .../ProjectSystem/DocumentSnapshotFactory.cs | 2 +- .../ProjectSystem/ProjectSnapshotFactory.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/ProjectSystem/DocumentSnapshotFactory.cs b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/ProjectSystem/DocumentSnapshotFactory.cs index d5c6adac062..241458f1efc 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/ProjectSystem/DocumentSnapshotFactory.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/ProjectSystem/DocumentSnapshotFactory.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.Remote.Razor.ProjectSystem; [method: ImportingConstructor] internal class DocumentSnapshotFactory(Lazy projectSnapshotFactory) { - private readonly ConditionalWeakTable _documentSnapshots = new(); + private static readonly ConditionalWeakTable _documentSnapshots = new(); private readonly Lazy _projectSnapshotFactory = projectSnapshotFactory; diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/ProjectSystem/ProjectSnapshotFactory.cs b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/ProjectSystem/ProjectSnapshotFactory.cs index e57436af364..f5b16a49e51 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/ProjectSystem/ProjectSnapshotFactory.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/ProjectSystem/ProjectSnapshotFactory.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.Remote.Razor.ProjectSystem; [method: ImportingConstructor] internal class ProjectSnapshotFactory(DocumentSnapshotFactory documentSnapshotFactory, ITelemetryReporter telemetryReporter) { - private readonly ConditionalWeakTable _projectSnapshots = new(); + private static readonly ConditionalWeakTable _projectSnapshots = new(); private readonly DocumentSnapshotFactory _documentSnapshotFactory = documentSnapshotFactory; private readonly ITelemetryReporter _telemetryReporter = telemetryReporter; From e1b7bc982a6f0ae171a09e3d8b93d93f7c95ce30 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Wed, 17 Jul 2024 12:02:43 +1000 Subject: [PATCH 22/30] Bump Roslyn --- eng/Versions.props | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index ab3d814a8d8..898101a3ff1 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -53,25 +53,25 @@ 9.0.0-beta.24352.2 1.0.0-beta.23475.1 1.0.0-beta.23475.1 - 4.11.0-3.24303.3 - 4.11.0-3.24303.3 - 4.11.0-3.24303.3 - 4.11.0-3.24303.3 - 4.11.0-3.24303.3 - 4.11.0-3.24303.3 - 4.11.0-3.24303.3 - 4.11.0-3.24303.3 - 4.11.0-3.24303.3 - 4.11.0-3.24303.3 - 4.11.0-3.24303.3 - 4.11.0-3.24303.3 - 4.11.0-3.24303.3 - 4.11.0-3.24303.3 - 4.11.0-3.24303.3 - 4.11.0-3.24303.3 - 4.11.0-3.24303.3 - 4.11.0-3.24303.3 - 4.11.0-3.24303.3 + 4.12.0-1.24366.3 + 4.12.0-1.24366.3 + 4.12.0-1.24366.3 + 4.12.0-1.24366.3 + 4.12.0-1.24366.3 + 4.12.0-1.24366.3 + 4.12.0-1.24366.3 + 4.12.0-1.24366.3 + 4.12.0-1.24366.3 + 4.12.0-1.24366.3 + 4.12.0-1.24366.3 + 4.12.0-1.24366.3 + 4.12.0-1.24366.3 + 4.12.0-1.24366.3 + 4.12.0-1.24366.3 + 4.12.0-1.24366.3 + 4.12.0-1.24366.3 + 4.12.0-1.24366.3 + 4.12.0-1.24366.3 - + https://github.com/dotnet/roslyn - ec443fac0dcc4e9ce2f83b9202f689dc2872b9f3 + 5866fabb8ffe6cbdb17e46902d6b96900ab5937b From 0884b799fb8c86e13354107c7db7dd8888274605 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Wed, 17 Jul 2024 14:20:35 +1000 Subject: [PATCH 26/30] Fix test --- .../RazorLanguageServerTest.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorLanguageServerTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorLanguageServerTest.cs index 9db1b0b0868..3fc2585b22d 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorLanguageServerTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorLanguageServerTest.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using System.Reflection; +using System.Text.Json; using System.Threading.Tasks; using Microsoft.AspNetCore.Razor.LanguageServer.Extensions; using Microsoft.AspNetCore.Razor.LanguageServer.Hosting; @@ -34,11 +35,11 @@ public async Task LocaleIsSetCorrectly() server.Initialize(); var queue = server.GetTestAccessor().GetRequestExecutionQueue(); - var initializeParams = new InitializeParams + var initializeParams = JsonSerializer.SerializeToElement(new InitializeParams { Capabilities = new(), Locale = "de-DE" - }; + }); await queue.ExecuteAsync(initializeParams, Methods.InitializeName, server.GetLspServices(), DisposalToken); @@ -47,7 +48,7 @@ public async Task LocaleIsSetCorrectly() // The request isn't actually valid, so we wrap it in a try catch, but we don't care for this test try { - await queue.ExecuteAsync(new(), VSInternalMethods.DocumentPullDiagnosticName, server.GetLspServices(), DisposalToken); + await queue.ExecuteAsync(JsonSerializer.SerializeToElement(new object()), VSInternalMethods.DocumentPullDiagnosticName, server.GetLspServices(), DisposalToken); } catch { } From c0b9aa27c6ef613ae1a367ef1d26950e4f0cb626 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Wed, 17 Jul 2024 10:06:30 +0200 Subject: [PATCH 27/30] Handle `:get`/`:set` in `EditorRequired` checking (#10628) * Add utility for verifying razor diagnostics * Handle `:get`/`:set` in `EditorRequired` checking * Simplify code --- .../ComponentCodeGenerationTestBase.cs | 90 +++++++++++++++++++ .../Components/ComponentLoweringPass.cs | 9 +- .../DiagnosticExtensions.cs | 20 +++++ 3 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 src/Shared/Microsoft.AspNetCore.Razor.Test.Common/DiagnosticExtensions.cs diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/ComponentCodeGenerationTestBase.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/ComponentCodeGenerationTestBase.cs index 364e9b87341..84f21cea83b 100644 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/ComponentCodeGenerationTestBase.cs +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/ComponentCodeGenerationTestBase.cs @@ -1198,6 +1198,96 @@ public class ComponentWithEditorRequiredParameters : ComponentBase Assert.Empty(generated.RazorDiagnostics); } + [IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/10553")] + public void Component_WithEditorRequiredParameter_ValueSpecifiedUsingBindGetSet() + { + AdditionalSyntaxTrees.Add(Parse(""" + using System; + using Microsoft.AspNetCore.Components; + + namespace Test; + + public class ComponentWithEditorRequiredParameters : ComponentBase + { + [Parameter] + [EditorRequired] + public string Property1 { get; set; } + } + """)); + + var generated = CompileToCSharp(""" + + + @code { + private string myField = "Some Value"; + private void OnFieldChanged(string value) { } + } + """); + + CompileToAssembly(generated); + Assert.Empty(generated.RazorDiagnostics); + } + + [IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/10553")] + public void Component_WithEditorRequiredParameter_ValueSpecifiedUsingBindGet() + { + AdditionalSyntaxTrees.Add(Parse(""" + using System; + using Microsoft.AspNetCore.Components; + + namespace Test; + + public class ComponentWithEditorRequiredParameters : ComponentBase + { + [Parameter] + [EditorRequired] + public string Property1 { get; set; } + } + """)); + + var generated = CompileToCSharp(""" + + + @code { + private string myField = "Some Value"; + } + """); + + CompileToAssembly(generated); + Assert.Empty(generated.RazorDiagnostics); + } + + [IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/10553")] + public void Component_WithEditorRequiredParameter_ValueSpecifiedUsingBindSet() + { + AdditionalSyntaxTrees.Add(Parse(""" + using System; + using Microsoft.AspNetCore.Components; + + namespace Test; + + public class ComponentWithEditorRequiredParameters : ComponentBase + { + [Parameter] + [EditorRequired] + public string Property1 { get; set; } + } + """)); + + var generated = CompileToCSharp(""" + + + @code { + private void OnFieldChanged(string value) { } + } + """); + + var compiled = CompileToAssembly(generated); + generated.RazorDiagnostics.Verify( + // x:\dir\subdir\Test\TestComponent.cshtml(1,61): error RZ10016: Attribute 'bind-Property1:set' was used but no attribute 'bind-Property1:get' was found. + Diagnostic("RZ10016").WithLocation(1, 61)); + } + [IntegrationTestFact] public void Component_WithEditorRequiredChildContent_NoValueSpecified() { diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentLoweringPass.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentLoweringPass.cs index e01340e82cf..8d809696490 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentLoweringPass.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentLoweringPass.cs @@ -203,10 +203,17 @@ static bool IsPresentAsAttribute(string attributeName, ComponentIntermediateNode const string bindPrefix = "@bind-"; if (child is TagHelperDirectiveAttributeIntermediateNode { OriginalAttributeName: { } originalAttributeName } && originalAttributeName.StartsWith(bindPrefix, StringComparison.Ordinal) && - originalAttributeName.AsSpan()[bindPrefix.Length..].Equals(attributeName.AsSpan(), StringComparison.Ordinal)) + originalAttributeName.AsSpan(start: bindPrefix.Length).Equals(attributeName.AsSpan(), StringComparison.Ordinal)) { return true; } + if (child is TagHelperDirectiveAttributeParameterIntermediateNode { OriginalAttributeName: { } originalName, AttributeNameWithoutParameter: { } nameWithoutParameter } && + originalName.StartsWith(bindPrefix, StringComparison.Ordinal) && + nameWithoutParameter.AsSpan(start: bindPrefix.Length - 1).Equals(attributeName.AsSpan(), StringComparison.Ordinal)) + { + // `@bind-Value:get` or `@bind-Value:set` is specified. + return true; + } } return false; diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/DiagnosticExtensions.cs b/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/DiagnosticExtensions.cs new file mode 100644 index 00000000000..54f800e718d --- /dev/null +++ b/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/DiagnosticExtensions.cs @@ -0,0 +1,20 @@ +// 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.Linq; +using Microsoft.AspNetCore.Razor.Language; +using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.NET.Sdk.Razor.SourceGenerators; + +namespace Microsoft.CodeAnalysis; + +public static class RazorDiagnosticExtensions +{ + public static void Verify( + this IEnumerable diagnostics, + params DiagnosticDescription[] expected) + { + diagnostics.Select(d => d.AsDiagnostic()).Verify(expected); + } +} From 281a6522175648c06fafb62ff0a8c12d6c290954 Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Wed, 17 Jul 2024 15:17:40 -0700 Subject: [PATCH 28/30] Remove binary test (#10639) Now that we're not writing to disk, we don't need to validate previously generated files. Fixes #10209 --- .../Resources/project.razor.bin | Bin 164158 -> 0 bytes .../Serialization/SerializerValidationTest.cs | 60 ------------------ ...spNetCore.Razor.Test.Common.Tooling.csproj | 1 - 3 files changed, 61 deletions(-) delete mode 100644 src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/Resources/project.razor.bin diff --git a/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/Resources/project.razor.bin b/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/Resources/project.razor.bin deleted file mode 100644 index 81821a1dbd87d063f73d84b74b7df7a6b3d62fd3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 164158 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 diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.ProjectEngineHost.Test/Serialization/SerializerValidationTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.ProjectEngineHost.Test/Serialization/SerializerValidationTest.cs index 1ff33741a4c..5cd9e7e4863 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.ProjectEngineHost.Test/Serialization/SerializerValidationTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.ProjectEngineHost.Test/Serialization/SerializerValidationTest.cs @@ -125,66 +125,6 @@ public void VerifyJson_TagHelpers(string resourceName, string? folderName = null Assert.Equal(originalTagHelpers, actualTagHelpers); } - ///

- /// Verifies that a previous generated file is able to be serialized. If this - /// fails make sure to do ONE of the following: - /// - /// 1. Update the deserializer to be lenient on the previous version - /// 2. Bump the Microsoft.AspNetCore.Razor.Serialization.MessagePack.SerializationFormat.Version - /// a. Generate a new file to replace project.razor.bin with the new format. This can be achieved by calling GenerateProjectRazorBin below. - /// b. This will require a dual insertion with Roslyn do use the correct version number - /// - [Theory] - [InlineData("project.razor.bin")] - public void VerifyMessagePack_DeserializeMessagePack(string resourceName) - { - // Arrange - var resourceBytes = RazorTestResources.GetResourceBytes(resourceName, "Benchmarking"); - var options = MessagePackSerializerOptions.Standard - .WithResolver(CompositeResolver.Create( - RazorProjectInfoResolver.Instance, - StandardResolver.Instance)); - - // Act - RazorProjectInfo actualProjectInfo; - try - { - actualProjectInfo = MessagePackConvert.Deserialize(resourceBytes, options); - } - catch (MessagePackSerializationException ex) when (ex.InnerException is RazorProjectInfoSerializationException) - { - Assert.Fail($"{typeof(MessagePackSerializationFormat).FullName}.{nameof(MessagePackSerializationFormat.Version)} has changed. Re-generate the project.razor.bin for this test."); - return; - } - - // Assert - Assert.NotNull(actualProjectInfo); - Assert.NotNull(actualProjectInfo.DisplayName); - } - -#pragma warning disable IDE0051 // Remove unused private members - private void GenerateProjectRazorBin() - { - var resourceBytes = RazorTestResources.GetResourceBytes("project.razor.json", "Benchmarking"); - var projectInfo = DeserializeProjectInfoFromJsonBytes(resourceBytes); - - var options = MessagePackSerializerOptions.Standard - .WithResolver(CompositeResolver.Create( - RazorProjectInfoResolver.Instance, - StandardResolver.Instance)); - - var filePath = Assembly.GetExecutingAssembly().Location; - var binFilePath = Path.Combine(Path.GetDirectoryName(filePath).AssumeNotNull(), "project.razor.bin"); - - using (var stream = File.OpenWrite(binFilePath)) - { - MessagePackSerializer.Serialize(stream, projectInfo, options); - } - - Logger.LogInformation($"New project.razor.bin written to '{binFilePath}'"); - } -#pragma warning restore IDE0051 // Remove unused private members - private static RazorProjectInfo DeserializeProjectInfoFromJsonBytes(byte[] resourceBytes) { using var stream = new MemoryStream(resourceBytes); diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/Microsoft.AspNetCore.Razor.Test.Common.Tooling.csproj b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/Microsoft.AspNetCore.Razor.Test.Common.Tooling.csproj index ce2bbfdb4f3..6caeb6b2982 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/Microsoft.AspNetCore.Razor.Test.Common.Tooling.csproj +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/Microsoft.AspNetCore.Razor.Test.Common.Tooling.csproj @@ -21,7 +21,6 @@ - From 8fd82a8268342f6c61a7a3f456e251ef5799e556 Mon Sep 17 00:00:00 2001 From: Phil Allen Date: Thu, 18 Jul 2024 08:09:03 -0700 Subject: [PATCH 29/30] Minor updates for Razor repo (#10576) * Update name away from now-former nation name * Update packages to modern versions * Change package version --- Directory.Packages.props | 4 ++-- NuGet.config | 4 ++-- src/Compiler/perf/Microbenchmarks/MSN.cshtml | 4 ++-- .../Microsoft.VisualStudio.Razor.IntegrationTests.csproj | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index ff2c1bc4ee8..4e2bdea802f 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -103,10 +103,10 @@ - + - + diff --git a/NuGet.config b/NuGet.config index 95636d05192..ad74bb028db 100644 --- a/NuGet.config +++ b/NuGet.config @@ -1,4 +1,4 @@ - + @@ -112,8 +112,8 @@ - + diff --git a/src/Compiler/perf/Microbenchmarks/MSN.cshtml b/src/Compiler/perf/Microbenchmarks/MSN.cshtml index 4867dc938dc..d7550b14140 100644 --- a/src/Compiler/perf/Microbenchmarks/MSN.cshtml +++ b/src/Compiler/perf/Microbenchmarks/MSN.cshtml @@ -772,12 +772,12 @@
  • -
    - Turkey detains 8 IS suspects 'posing as refugees' + Police detains 8 IS suspects 'posing as refugees' AFP diff --git a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/Microsoft.VisualStudio.Razor.IntegrationTests.csproj b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/Microsoft.VisualStudio.Razor.IntegrationTests.csproj index 24a8a0d572b..3a4cb87b365 100644 --- a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/Microsoft.VisualStudio.Razor.IntegrationTests.csproj +++ b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/Microsoft.VisualStudio.Razor.IntegrationTests.csproj @@ -37,7 +37,7 @@ - + From 9abb3a4c5299cf66d9e9ff1dbefd1263b88afad5 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Fri, 19 Jul 2024 06:54:19 +1000 Subject: [PATCH 30/30] Bump Roslyn to inserted version --- eng/Version.Details.xml | 76 ++++++++++++++++++++--------------------- eng/Versions.props | 38 ++++++++++----------- 2 files changed, 57 insertions(+), 57 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 9943d8bd7fa..11bb619b856 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -11,82 +11,82 @@ 9ae78a4e6412926d19ba97cfed159bf9de70b538 - + https://github.com/dotnet/roslyn - 5866fabb8ffe6cbdb17e46902d6b96900ab5937b + 30edd04fd41dec9e8f9f48e698ebd5b80d9f7677 - + https://github.com/dotnet/roslyn - 5866fabb8ffe6cbdb17e46902d6b96900ab5937b + 30edd04fd41dec9e8f9f48e698ebd5b80d9f7677 - + https://github.com/dotnet/roslyn - 5866fabb8ffe6cbdb17e46902d6b96900ab5937b + 30edd04fd41dec9e8f9f48e698ebd5b80d9f7677 - + https://github.com/dotnet/roslyn - 5866fabb8ffe6cbdb17e46902d6b96900ab5937b + 30edd04fd41dec9e8f9f48e698ebd5b80d9f7677 - + https://github.com/dotnet/roslyn - 5866fabb8ffe6cbdb17e46902d6b96900ab5937b + 30edd04fd41dec9e8f9f48e698ebd5b80d9f7677 - + https://github.com/dotnet/roslyn - 5866fabb8ffe6cbdb17e46902d6b96900ab5937b + 30edd04fd41dec9e8f9f48e698ebd5b80d9f7677 - + https://github.com/dotnet/roslyn - 5866fabb8ffe6cbdb17e46902d6b96900ab5937b + 30edd04fd41dec9e8f9f48e698ebd5b80d9f7677 - + https://github.com/dotnet/roslyn - 5866fabb8ffe6cbdb17e46902d6b96900ab5937b + 30edd04fd41dec9e8f9f48e698ebd5b80d9f7677 - + https://github.com/dotnet/roslyn - 5866fabb8ffe6cbdb17e46902d6b96900ab5937b + 30edd04fd41dec9e8f9f48e698ebd5b80d9f7677 - + https://github.com/dotnet/roslyn - 5866fabb8ffe6cbdb17e46902d6b96900ab5937b + 30edd04fd41dec9e8f9f48e698ebd5b80d9f7677 - + https://github.com/dotnet/roslyn - 5866fabb8ffe6cbdb17e46902d6b96900ab5937b + 30edd04fd41dec9e8f9f48e698ebd5b80d9f7677 - + https://github.com/dotnet/roslyn - 5866fabb8ffe6cbdb17e46902d6b96900ab5937b + 30edd04fd41dec9e8f9f48e698ebd5b80d9f7677 - + https://github.com/dotnet/roslyn - 5866fabb8ffe6cbdb17e46902d6b96900ab5937b + 30edd04fd41dec9e8f9f48e698ebd5b80d9f7677 - + https://github.com/dotnet/roslyn - 5866fabb8ffe6cbdb17e46902d6b96900ab5937b + 30edd04fd41dec9e8f9f48e698ebd5b80d9f7677 - + https://github.com/dotnet/roslyn - 5866fabb8ffe6cbdb17e46902d6b96900ab5937b + 30edd04fd41dec9e8f9f48e698ebd5b80d9f7677 - + https://github.com/dotnet/roslyn - 5866fabb8ffe6cbdb17e46902d6b96900ab5937b + 30edd04fd41dec9e8f9f48e698ebd5b80d9f7677 - + https://github.com/dotnet/roslyn - 5866fabb8ffe6cbdb17e46902d6b96900ab5937b + 30edd04fd41dec9e8f9f48e698ebd5b80d9f7677 - + https://github.com/dotnet/roslyn - 5866fabb8ffe6cbdb17e46902d6b96900ab5937b + 30edd04fd41dec9e8f9f48e698ebd5b80d9f7677 - + https://github.com/dotnet/roslyn - 5866fabb8ffe6cbdb17e46902d6b96900ab5937b + 30edd04fd41dec9e8f9f48e698ebd5b80d9f7677 diff --git a/eng/Versions.props b/eng/Versions.props index 898101a3ff1..6aa6e7cad14 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -53,25 +53,25 @@ 9.0.0-beta.24352.2 1.0.0-beta.23475.1 1.0.0-beta.23475.1 - 4.12.0-1.24366.3 - 4.12.0-1.24366.3 - 4.12.0-1.24366.3 - 4.12.0-1.24366.3 - 4.12.0-1.24366.3 - 4.12.0-1.24366.3 - 4.12.0-1.24366.3 - 4.12.0-1.24366.3 - 4.12.0-1.24366.3 - 4.12.0-1.24366.3 - 4.12.0-1.24366.3 - 4.12.0-1.24366.3 - 4.12.0-1.24366.3 - 4.12.0-1.24366.3 - 4.12.0-1.24366.3 - 4.12.0-1.24366.3 - 4.12.0-1.24366.3 - 4.12.0-1.24366.3 - 4.12.0-1.24366.3 + 4.12.0-1.24366.6 + 4.12.0-1.24366.6 + 4.12.0-1.24366.6 + 4.12.0-1.24366.6 + 4.12.0-1.24366.6 + 4.12.0-1.24366.6 + 4.12.0-1.24366.6 + 4.12.0-1.24366.6 + 4.12.0-1.24366.6 + 4.12.0-1.24366.6 + 4.12.0-1.24366.6 + 4.12.0-1.24366.6 + 4.12.0-1.24366.6 + 4.12.0-1.24366.6 + 4.12.0-1.24366.6 + 4.12.0-1.24366.6 + 4.12.0-1.24366.6 + 4.12.0-1.24366.6 + 4.12.0-1.24366.6