Skip to content

Commit

Permalink
Remove dependency on all Roslyn assemblies from build host (#73497)
Browse files Browse the repository at this point in the history
  • Loading branch information
tmat authored May 20, 2024
1 parent dd1d653 commit 5a5e802
Show file tree
Hide file tree
Showing 41 changed files with 208 additions and 244 deletions.
2 changes: 2 additions & 0 deletions Roslyn.sln
Original file line number Diff line number Diff line change
Expand Up @@ -1698,6 +1698,8 @@ Global
src\ExpressionEvaluator\Core\Source\ResultProvider\ResultProvider.projitems*{abdbac1e-350e-4dc3-bb45-3504404545ee}*SharedItemsImports = 5
src\ExpressionEvaluator\VisualBasic\Source\ResultProvider\BasicResultProvider.projitems*{ace53515-482c-4c6a-e2d2-4242a687dfee}*SharedItemsImports = 5
src\Compilers\CSharp\csc\CscCommandLine.projitems*{b021ccbc-b2af-4560-af28-ed055f0ed696}*SharedItemsImports = 13
src\Dependencies\Collections\Microsoft.CodeAnalysis.Collections.projitems*{b1481d94-682e-46ec-adbe-a16eb46feee9}*SharedItemsImports = 5
src\Dependencies\PooledObjects\Microsoft.CodeAnalysis.PooledObjects.projitems*{b1481d94-682e-46ec-adbe-a16eb46feee9}*SharedItemsImports = 5
src\Compilers\CSharp\CSharpAnalyzerDriver\CSharpAnalyzerDriver.projitems*{b501a547-c911-4a05-ac6e-274a50dff30e}*SharedItemsImports = 5
src\ExpressionEvaluator\Core\Source\ResultProvider\ResultProvider.projitems*{bb3ca047-5d00-48d4-b7d3-233c1265c065}*SharedItemsImports = 13
src\ExpressionEvaluator\CSharp\Source\ResultProvider\CSharpResultProvider.projitems*{bf9dac1e-3a5e-4dc3-bb44-9a64e0d4e9d4}*SharedItemsImports = 5
Expand Down
7 changes: 6 additions & 1 deletion src/Compilers/Core/Portable/CaseInsensitiveComparison.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ namespace Microsoft.CodeAnalysis
/// <summary>
/// Case-insensitive operations (mostly comparison) on unicode strings.
/// </summary>
public static class CaseInsensitiveComparison
#if COMPILERCORE
public
#else
internal
#endif
static class CaseInsensitiveComparison
{
// PERF: Cache a TextInfo for Unicode ToLower since this will be accessed very frequently
private static readonly TextInfo s_unicodeCultureTextInfo = GetUnicodeCulture().TextInfo;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -764,19 +764,6 @@ public static ImmutableArray<T> Distinct<T>(this ImmutableArray<T> array, IEqual
return result;
}

internal static bool HasAnyErrors<T>(this ImmutableArray<T> diagnostics) where T : Diagnostic
{
foreach (var diagnostic in diagnostics)
{
if (diagnostic.Severity == DiagnosticSeverity.Error)
{
return true;
}
}

return false;
}

// In DEBUG, swap the first and last elements of a read-only array, yielding a new read only array.
// This helps to avoid depending on accidentally sorted arrays.
internal static ImmutableArray<T> ConditionallyDeOrder<T>(this ImmutableArray<T> array)
Expand Down Expand Up @@ -1034,8 +1021,8 @@ internal static void CreateNameToMembersMap<TKey, TNamespaceOrTypeSymbol, TNamed
where TNamedTypeSymbol : class, TNamespaceOrTypeSymbol
where TNamespaceSymbol : class, TNamespaceOrTypeSymbol
{
foreach (var (name, value) in dictionary)
result.Add(name, createMembers(value));
foreach (var entry in dictionary)
result.Add(entry.Key, createMembers(entry.Value));

return;

Expand Down Expand Up @@ -1076,11 +1063,11 @@ internal static Dictionary<TKey, ImmutableArray<TNamedTypeSymbol>> GetTypesFromM

var dictionary = new Dictionary<TKey, ImmutableArray<TNamedTypeSymbol>>(capacity, comparer);

foreach (var (name, members) in map)
foreach (var entry in map)
{
var namedTypes = getOrCreateNamedTypes(members);
var namedTypes = getOrCreateNamedTypes(entry.Value);
if (namedTypes.Length > 0)
dictionary.Add(name, namedTypes);
dictionary.Add(entry.Key, namedTypes);
}

return dictionary;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Immutable;

namespace Microsoft.CodeAnalysis;

internal static class DiagnosticArrayExtensions
{
internal static bool HasAnyErrors<T>(this ImmutableArray<T> diagnostics) where T : Diagnostic
{
foreach (var diagnostic in diagnostics)
{
if (diagnostic.Severity == DiagnosticSeverity.Error)
{
return true;
}
}

return false;
}
}
2 changes: 1 addition & 1 deletion src/Compilers/Core/Portable/EncodedStringText.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ private static SourceText Decode(
data.Seek(0, SeekOrigin.Begin);

// For small streams, see if we can read the byte buffer directly.
if (encoding.GetMaxCharCountOrThrowIfHuge(data) < LargeObjectHeapLimitInChars)
if (encoding.TryGetMaxCharCount(data.Length, out int maxCharCount) && maxCharCount < LargeObjectHeapLimitInChars)
{
if (TryGetBytesFromStream(data, out ArraySegment<byte> bytes) && bytes.Offset == 0 && bytes.Array is object)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,13 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Microsoft.CodeAnalysis;
using System;
using System.Diagnostics;
using System.IO;
using System.Text;

namespace Microsoft.CodeAnalysis
{
internal static partial class EncodingExtensions
{
/// <summary>
/// Get maximum char count needed to decode the entire stream.
/// </summary>
/// <exception cref="IOException">Stream is so big that max char count can't fit in <see cref="int"/>.</exception>
internal static int GetMaxCharCountOrThrowIfHuge(this Encoding encoding, Stream stream)
{
Debug.Assert(stream.CanSeek);
long length = stream.Length;

if (encoding.TryGetMaxCharCount(length, out int maxCharCount))
{
return maxCharCount;
}

#if CODE_STYLE
throw new IOException(CodeStyleResources.Stream_is_too_long);
#elif WORKSPACE
throw new IOException(WorkspacesResources.Stream_is_too_long);
#else
throw new IOException(CodeAnalysisResources.StreamIsTooLong);
#endif
}

internal static bool TryGetMaxCharCount(this Encoding encoding, long length, out int maxCharCount)
{
maxCharCount = 0;
Expand Down
39 changes: 0 additions & 39 deletions src/Compilers/Core/Portable/InternalUtilities/EnumUtilties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,45 +11,6 @@ namespace Roslyn.Utilities
{
internal static class EnumUtilities
{
/// <summary>
/// Convert a boxed primitive (generally of the backing type of an enum) into a ulong.
/// </summary>
/// <remarks>
/// </remarks>
internal static ulong ConvertEnumUnderlyingTypeToUInt64(object value, SpecialType specialType)
{
RoslynDebug.Assert(value != null);
Debug.Assert(value.GetType().GetTypeInfo().IsPrimitive);

unchecked
{
switch (specialType)
{
case SpecialType.System_SByte:
return (ulong)(sbyte)value;
case SpecialType.System_Int16:
return (ulong)(short)value;
case SpecialType.System_Int32:
return (ulong)(int)value;
case SpecialType.System_Int64:
return (ulong)(long)value;
case SpecialType.System_Byte:
return (byte)value;
case SpecialType.System_UInt16:
return (ushort)value;
case SpecialType.System_UInt32:
return (uint)value;
case SpecialType.System_UInt64:
return (ulong)value;

default:
// not using ExceptionUtilities.UnexpectedValue() because this is used by the Services layer
// which doesn't have those utilities.
throw new InvalidOperationException(string.Format("{0} is not a valid underlying type for an enum", specialType));
}
}
}

internal static T[] GetValues<T>() where T : struct
{
return (T[])Enum.GetValues(typeof(T));
Expand Down
10 changes: 1 addition & 9 deletions src/Compilers/Core/Portable/InternalUtilities/TextKeyedCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,8 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using System.Threading;
using Microsoft.CodeAnalysis.PooledObjects;

namespace Roslyn.Utilities
{
Expand Down
27 changes: 27 additions & 0 deletions src/Compilers/Core/Portable/SpecialTypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Diagnostics;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis
Expand Down Expand Up @@ -364,5 +365,31 @@ public static SpecialType FromRuntimeTypeOfLiteralValue(object value)
/// </summary>
public static bool CanOptimizeBehavior(this SpecialType specialType)
=> specialType is >= SpecialType.System_Object and <= SpecialType.System_Runtime_CompilerServices_InlineArrayAttribute;

/// <summary>
/// Convert a boxed primitive (generally of the backing type of an enum) into a ulong.
/// </summary>
internal static ulong ConvertUnderlyingValueToUInt64(this SpecialType enumUnderlyingType, object value)
{
RoslynDebug.Assert(value != null);
Debug.Assert(value.GetType().IsPrimitive);

unchecked
{
return enumUnderlyingType switch
{
SpecialType.System_SByte => (ulong)(sbyte)value,
SpecialType.System_Int16 => (ulong)(short)value,
SpecialType.System_Int32 => (ulong)(int)value,
SpecialType.System_Int64 => (ulong)(long)value,
SpecialType.System_Byte => (byte)value,
SpecialType.System_UInt16 => (ushort)value,
SpecialType.System_UInt32 => (uint)value,
SpecialType.System_UInt64 => (ulong)value,
_ => throw ExceptionUtilities.UnexpectedValue(enumUnderlyingType),
};
}
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ private void AddFlagsEnumConstantValue(
{
Debug.Assert(enumType.EnumUnderlyingType is not null);
var underlyingSpecialType = enumType.EnumUnderlyingType.SpecialType;
var constantValueULong = EnumUtilities.ConvertEnumUnderlyingTypeToUInt64(constantValue, underlyingSpecialType);
var constantValueULong = underlyingSpecialType.ConvertUnderlyingValueToUInt64(constantValue);

var result = constantValueULong;

Expand Down Expand Up @@ -321,7 +321,7 @@ private static void GetSortedEnumFields(
var field = (IFieldSymbol)member;
if (field.HasConstantValue)
{
var enumField = new EnumField(field.Name, EnumUtilities.ConvertEnumUnderlyingTypeToUInt64(field.ConstantValue, underlyingSpecialType), field);
var enumField = new EnumField(field.Name, underlyingSpecialType.ConvertUnderlyingValueToUInt64(field.ConstantValue), field);
enumFields.Add(enumField);
}
}
Expand All @@ -334,7 +334,7 @@ private void AddNonFlagsEnumConstantValue(INamedTypeSymbol enumType, object cons
{
Debug.Assert(enumType.EnumUnderlyingType is not null);
var underlyingSpecialType = enumType.EnumUnderlyingType.SpecialType;
var constantValueULong = EnumUtilities.ConvertEnumUnderlyingTypeToUInt64(constantValue, underlyingSpecialType);
var constantValueULong = underlyingSpecialType.ConvertUnderlyingValueToUInt64(constantValue);

var enumFields = ArrayBuilder<EnumField>.GetInstance();
GetSortedEnumFields(enumType, enumFields);
Expand Down
7 changes: 6 additions & 1 deletion src/Compilers/Core/Portable/Symbols/LanguageNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ namespace Microsoft.CodeAnalysis
/// <summary>
/// A class that provides constants for common language names.
/// </summary>
public static class LanguageNames
#if COMPILERCORE
public
#else
internal
#endif
static class LanguageNames
{
/// <summary>
/// The common name used for the C# language.
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/Core/Portable/Text/LargeText.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ internal static SourceText Decode(Stream stream, Encoding encoding, SourceHashAl
return SourceText.From(string.Empty, encoding, checksumAlgorithm);
}

var maxCharRemainingGuess = encoding.GetMaxCharCountOrThrowIfHuge(stream);
var maxCharRemainingGuess = GetMaxCharCountOrThrowIfHuge(encoding, stream);
Debug.Assert(longLength > 0 && longLength <= int.MaxValue); // GetMaxCharCountOrThrowIfHuge should have thrown.
int length = (int)longLength;

Expand Down
18 changes: 17 additions & 1 deletion src/Compilers/Core/Portable/Text/SourceText.cs
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ public static SourceText From(
if (stream.CanSeek)
{
// If the resulting string would end up on the large object heap, then use LargeEncodedText.
if (encoding.GetMaxCharCountOrThrowIfHuge(stream) >= LargeObjectHeapLimitInChars)
if (GetMaxCharCountOrThrowIfHuge(encoding, stream) >= LargeObjectHeapLimitInChars)
{
return LargeText.Decode(stream, encoding, checksumAlgorithm, throwIfBinaryDetected, canBeEmbedded);
}
Expand Down Expand Up @@ -1260,5 +1260,21 @@ public override event EventHandler<TextChangeEventArgs> TextChanged
}
}
}

/// <summary>
/// Get maximum char count needed to decode the entire stream.
/// </summary>
/// <exception cref="IOException">Stream is so big that max char count can't fit in <see cref="int"/>.</exception>
internal static int GetMaxCharCountOrThrowIfHuge(Encoding encoding, Stream stream)
{
Debug.Assert(stream.CanSeek);

if (encoding.TryGetMaxCharCount(stream.Length, out int maxCharCount))
{
return maxCharCount;
}

throw new IOException(CodeAnalysisResources.StreamIsTooLong);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ private async Task<bool> LoadOrReloadProjectAsync(ProjectToLoad projectToLoad, T

var loadedFile = await buildHost.LoadProjectFileAsync(projectPath, languageName, cancellationToken);
var diagnosticLogItems = await loadedFile.GetDiagnosticLogItemsAsync(cancellationToken);
if (diagnosticLogItems.Any(item => item.Kind is WorkspaceDiagnosticKind.Failure))
if (diagnosticLogItems.Any(item => item.Kind is DiagnosticLogItemKind.Error))
{
await LogDiagnosticsAsync(diagnosticLogItems);
// We have total failures in evaluation, no point in continuing.
Expand Down Expand Up @@ -299,7 +299,7 @@ private async Task<bool> LoadOrReloadProjectAsync(ProjectToLoad projectToLoad, T
{
// Since our LogDiagnosticsAsync helper takes DiagnosticLogItems, let's just make one for this
var message = string.Format(LanguageServerResources.Exception_thrown_0, e);
var diagnosticLogItem = new DiagnosticLogItem(WorkspaceDiagnosticKind.Failure, message, projectPath);
var diagnosticLogItem = new DiagnosticLogItem(DiagnosticLogItemKind.Error, message, projectPath);
await LogDiagnosticsAsync([diagnosticLogItem]);

return false;
Expand All @@ -310,10 +310,10 @@ async Task LogDiagnosticsAsync(ImmutableArray<DiagnosticLogItem> diagnosticLogIt
foreach (var logItem in diagnosticLogItems)
{
var projectName = Path.GetFileName(projectPath);
_logger.Log(logItem.Kind is WorkspaceDiagnosticKind.Failure ? LogLevel.Error : LogLevel.Warning, $"{logItem.Kind} while loading {logItem.ProjectFilePath}: {logItem.Message}");
_logger.Log(logItem.Kind is DiagnosticLogItemKind.Error ? LogLevel.Error : LogLevel.Warning, $"{logItem.Kind} while loading {logItem.ProjectFilePath}: {logItem.Message}");
}

var worstLspMessageKind = diagnosticLogItems.Any(logItem => logItem.Kind is WorkspaceDiagnosticKind.Failure) ? LSP.MessageType.Error : LSP.MessageType.Warning;
var worstLspMessageKind = diagnosticLogItems.Any(logItem => logItem.Kind is DiagnosticLogItemKind.Error) ? LSP.MessageType.Error : LSP.MessageType.Warning;

string message;

Expand Down
10 changes: 3 additions & 7 deletions src/Workspaces/Core/MSBuild.BuildHost/BuildHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

extern alias workspaces;
using System.Collections.Immutable;
using System.IO;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -101,11 +101,7 @@ private bool TryEnsureMSBuildLoaded(string projectOrSolutionFilePath)
}
}

#if NET472 || NET6_0 // If we're compiling against net472 or net6.0, we get our MemberNotNull from the workspaces assembly. It has it in the net6.0 case since we're consuming the netstandard2.0 version of Workspaces.
[workspaces::System.Diagnostics.CodeAnalysis.MemberNotNull(nameof(_buildManager))]
#else // If we're compiling against net7.0 or higher, then we're getting it staright from the framework.
[System.Diagnostics.CodeAnalysis.MemberNotNull(nameof(_buildManager))]
#endif
[MemberNotNull(nameof(_buildManager))]
[MethodImpl(MethodImplOptions.NoInlining)] // Do not inline this, since this creates MSBuild types which are being loaded by the caller
private void CreateBuildManager()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,6 @@ public CSharpProjectFile(CSharpProjectFileLoader loader, MSB.Evaluation.Project?
{
}

protected override SourceCodeKind GetSourceCodeKind(string documentFileName)
=> SourceCodeKind.Regular;

public override string GetDocumentExtension(SourceCodeKind sourceCodeKind)
=> ".cs";

protected override IEnumerable<MSB.Framework.ITaskItem> GetCompilerCommandLineArgs(MSB.Execution.ProjectInstance executedProject)
=> executedProject.GetItems(ItemNames.CscCommandLineArgs);

Expand Down
Loading

0 comments on commit 5a5e802

Please sign in to comment.