Skip to content

Commit

Permalink
Merge pull request #36965 from jasonmalinowski/workspace-nullable-ann…
Browse files Browse the repository at this point in the history
…otations

Add nullable annotations to parts of the Workspace API
  • Loading branch information
jasonmalinowski committed Jul 11, 2019
2 parents 6947c65 + fdf865a commit 582ca81
Show file tree
Hide file tree
Showing 10 changed files with 331 additions and 175 deletions.
Expand Up @@ -173,6 +173,7 @@
<Compile Include="..\..\..\Workspaces\Core\Portable\Utilities\IBidirectionalMap.cs" Link="InternalUtilities\IBidirectionalMap.cs" />
<Compile Include="..\..\..\Workspaces\Core\Portable\Utilities\LazyInitialization.cs" Link="Formatting\LazyInitialization.cs" />
<Compile Include="..\..\..\Workspaces\Core\Portable\Utilities\NonReentrantLock.cs" Link="Formatting\NonReentrantLock.cs" />
<Compile Include="..\..\..\Workspaces\Core\Portable\Utilities\NullableHelpers\NullableAttributes.cs" Link="Formatting\NullableAttributes.cs" />
<Compile Include="..\..\..\Workspaces\Core\Portable\Utilities\ObjectPools\Extensions.cs" Link="Formatting\Extensions.cs" />
<Compile Include="..\..\..\Workspaces\Core\Portable\Utilities\ObjectPools\PooledObject.cs" Link="Formatting\PooledObject.cs" />
<Compile Include="..\..\..\Workspaces\Core\Portable\Utilities\ObjectPools\SharedPools.cs" Link="Formatting\SharedPools.cs" />
Expand Down
Expand Up @@ -37,6 +37,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\Workspaces\Core\Portable\Utilities\Contract.cs" Link="Utilities\Contract.cs" />
<Compile Include="..\..\Workspaces\Core\Portable\Utilities\NullableHelpers\NullableAttributes.cs" Link="Utilities\NullableAttributes.cs" />
<Compile Include="..\..\Workspaces\Core\Portable\Utilities\TaskExtensions.cs" Link="Utilities\TaskExtensions.cs" />
<Compile Include="..\..\Workspaces\Core\Portable\Utilities\AsyncLazy`1.cs" Link="Utilities\AsyncLazy`1.cs" />
<Compile Include="..\..\Workspaces\Core\Portable\Utilities\NonReentrantLock.cs" Link="Utilities\NonReentrantLock.cs" />
Expand Down
11 changes: 8 additions & 3 deletions src/Workspaces/Core/Portable/Utilities/Contract.cs
@@ -1,7 +1,10 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

#nullable enable

using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;

namespace Roslyn.Utilities
{
Expand All @@ -11,7 +14,7 @@ internal static class Contract
/// Throws a non-accessible exception if the provided value is null. This method executes in
/// all builds
/// </summary>
public static void ThrowIfNull<T>(T value, string message = null) where T : class
public static void ThrowIfNull<T>([NotNull] T value, string? message = null) where T : class?
{
if (value == null)
{
Expand All @@ -24,7 +27,7 @@ internal static class Contract
/// Throws a non-accessible exception if the provided value is false. This method executes
/// in all builds
/// </summary>
public static void ThrowIfFalse(bool condition, string message = null)
public static void ThrowIfFalse([DoesNotReturnIf(parameterValue: false)] bool condition, string? message = null)
{
if (!condition)
{
Expand All @@ -37,7 +40,7 @@ public static void ThrowIfFalse(bool condition, string message = null)
/// Throws a non-accessible exception if the provided value is true. This method executes in
/// all builds.
/// </summary>
public static void ThrowIfTrue(bool condition, string message = null)
public static void ThrowIfTrue([DoesNotReturnIf(parameterValue: true)] bool condition, string? message = null)
{
if (condition)
{
Expand All @@ -47,12 +50,14 @@ public static void ThrowIfTrue(bool condition, string message = null)
}

[DebuggerHidden]
[DoesNotReturn]
public static void Fail(string message = "Unexpected")
{
throw new InvalidOperationException(message);
}

[DebuggerHidden]
[DoesNotReturn]
public static T FailWithReturn<T>(string message = "Unexpected")
{
throw new InvalidOperationException(message);
Expand Down
@@ -0,0 +1,86 @@
// 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.

// This was copied from https://github.com/dotnet/coreclr/blob/60f1e6265bd1039f023a82e0643b524d6aaf7845/src/System.Private.CoreLib/shared/System/Diagnostics/CodeAnalysis/NullableAttributes.cs
// and updated to have the scope of the attributes be internal.

namespace System.Diagnostics.CodeAnalysis
{
/// <summary>Specifies that null is allowed as an input even if the corresponding type disallows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)]
internal sealed class AllowNullAttribute : Attribute { }

/// <summary>Specifies that null is disallowed as an input even if the corresponding type allows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)]
internal sealed class DisallowNullAttribute : Attribute { }

/// <summary>Specifies that an output may be null even if the corresponding type disallows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
internal sealed class MaybeNullAttribute : Attribute { }

/// <summary>Specifies that an output will not be null even if the corresponding type allows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
internal sealed class NotNullAttribute : Attribute { }

/// <summary>Specifies that when a method returns <see cref="ReturnValue"/>, the parameter may be null even if the corresponding type disallows it.</summary>
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
internal sealed class MaybeNullWhenAttribute : Attribute
{
/// <summary>Initializes the attribute with the specified return value condition.</summary>
/// <param name="returnValue">
/// The return value condition. If the method returns this value, the associated parameter may be null.
/// </param>
public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;

/// <summary>Gets the return value condition.</summary>
public bool ReturnValue { get; }
}

/// <summary>Specifies that when a method returns <see cref="ReturnValue"/>, the parameter will not be null even if the corresponding type allows it.</summary>
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
internal sealed class NotNullWhenAttribute : Attribute
{
/// <summary>Initializes the attribute with the specified return value condition.</summary>
/// <param name="returnValue">
/// The return value condition. If the method returns this value, the associated parameter will not be null.
/// </param>
public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;

/// <summary>Gets the return value condition.</summary>
public bool ReturnValue { get; }
}

/// <summary>Specifies that the output will be non-null if the named parameter is non-null.</summary>
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)]
internal sealed class NotNullIfNotNullAttribute : Attribute
{
/// <summary>Initializes the attribute with the associated parameter name.</summary>
/// <param name="parameterName">
/// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null.
/// </param>
public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName;

/// <summary>Gets the associated parameter name.</summary>
public string ParameterName { get; }
}

/// <summary>Applied to a method that will never return under any circumstance.</summary>
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
internal sealed class DoesNotReturnAttribute : Attribute { }

/// <summary>Specifies that the method will not return if the associated Boolean parameter is passed the specified value.</summary>
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
internal sealed class DoesNotReturnIfAttribute : Attribute
{
/// <summary>Initializes the attribute with the specified parameter value.</summary>
/// <param name="parameterValue">
/// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to
/// the associated parameter matches this value.
/// </param>
public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue;

/// <summary>Gets the condition parameter value.</summary>
public bool ParameterValue { get; }
}
}
@@ -1,6 +1,9 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

#nullable enable

using System;
using System.Diagnostics.CodeAnalysis;

namespace Microsoft.CodeAnalysis.Host
{
Expand All @@ -23,12 +26,14 @@ public abstract class HostLanguageServices
/// Gets a language specific service provided by the host identified by the service type.
/// If the host does not provide the service, this method returns null.
/// </summary>
[return: MaybeNull]
public abstract TLanguageService GetService<TLanguageService>() where TLanguageService : ILanguageService;

/// <summary>
/// Gets a language specific service provided by the host identified by the service type.
/// If the host does not provide the service, this method returns throws <see cref="InvalidOperationException"/>.
/// </summary>
[return: NotNull]
public TLanguageService GetRequiredService<TLanguageService>() where TLanguageService : ILanguageService
{
var service = GetService<TLanguageService>();
Expand All @@ -45,13 +50,13 @@ public abstract class HostLanguageServices
/// <summary>
/// A factory for creating compilations instances.
/// </summary>
internal virtual ICompilationFactoryService CompilationFactory
internal virtual ICompilationFactoryService? CompilationFactory
{
get { return this.GetService<ICompilationFactoryService>(); }
}

// needs some work on the interface before it can be public
internal virtual ISyntaxTreeFactoryService SyntaxTreeFactory
internal virtual ISyntaxTreeFactoryService? SyntaxTreeFactory
{
get { return this.GetService<ISyntaxTreeFactoryService>(); }
}
Expand Down
@@ -1,7 +1,10 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

#nullable enable

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;

Expand All @@ -27,13 +30,15 @@ public abstract class HostWorkspaceServices
/// Gets a workspace specific service provided by the host identified by the service type.
/// If the host does not provide the service, this method returns null.
/// </summary>
[return: MaybeNull]
public abstract TWorkspaceService GetService<TWorkspaceService>() where TWorkspaceService : IWorkspaceService;

/// <summary>
/// Gets a workspace specific service provided by the host identified by the service type.
/// If the host does not provide the service, this method throws <see cref="InvalidOperationException"/>.
/// </summary>
/// <exception cref="InvalidOperationException">The host does not provide the service.</exception>
[return: NotNull]
public TWorkspaceService GetRequiredService<TWorkspaceService>() where TWorkspaceService : IWorkspaceService
{
var service = GetService<TWorkspaceService>();
Expand Down Expand Up @@ -88,6 +93,7 @@ public virtual bool IsSupported(string languageName)
/// <summary>
/// Gets the <see cref="HostLanguageServices"/> for the language name.
/// </summary>
/// <exception cref="NotSupportedException">Thrown if the language isn't supported.</exception>
public virtual HostLanguageServices GetLanguageServices(string languageName)
{
throw new NotSupportedException(string.Format(WorkspacesResources.The_language_0_is_not_supported, languageName));
Expand Down

0 comments on commit 582ca81

Please sign in to comment.