New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Annotate most of Microsoft.VisualStudio.ProjectSystem.Managed #5003
Conversation
@@ -41,6 +40,7 @@ internal static class ImmutableCollectionLinqExtensions | |||
return false; | |||
} | |||
|
|||
[return: MaybeNull] | |||
public static T FirstOrDefault<T, TArg>(this ImmutableArray<T> immutableArray, Func<T, TArg, bool> predicate, TArg arg) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the recommended approach for indicating that an output with unconstrained generic type can be null.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ugh - that is not great.
@@ -49,7 +49,7 @@ internal static class ImmutableCollectionLinqExtensions | |||
return obj; | |||
} | |||
|
|||
return default; | |||
return default!; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately the attribute above doesn't influence the member body, so we need to !
force this here. Tracked by dotnet/roslyn#36039.
private IWorkspaceProjectContext _context; | ||
private ExportLifetimeContext<IWorkspaceContextHandler>[] _handlers; | ||
private IWorkspaceProjectContext? _context; | ||
private ExportLifetimeContext<IWorkspaceContextHandler>[] _handlers = Array.Empty<ExportLifetimeContext<IWorkspaceContextHandler>>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In some cases it is easier to use singleton empty collections rather than null
. Doing so can avoid lots of null checks or !
, especially when there's an implicit relationship between being initialised and non-null (as happens a lot in this repo).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree in some cases, but in this case it would be a bug that this is null that is no longer caught.
} | ||
} | ||
|
||
private void ProcessProjectBuildFailure(IProjectRuleSnapshot snapshot) | ||
{ | ||
Assumes.NotNull(_context); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately these guard methods are not annotated to indicate that the argument is not null after this point. There is a DoesNotReturnIf(bool)
attribute, but not a DoesNotReturnIfNull(string parameterName)
attribute. It seems we have to !
the first usage after this point. I filed dotnet/roslyn#37071 to see if we could just !
the argument to the guard method directly, which would be nicer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we file an issue on the VSSDK folks for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can do, but there's nothing they can do until the compiler supports it. As soon as there's compiler support, I'll send them a PR if they don't get to it first.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually it's possible to do this with the current attributes:
void AssertNotNull([NotNull] object? o) { }
string M(object? o)
{
AssertNotNull(o);
return o.ToString(); // no diagnostic here
}
namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices | ||
{ | ||
internal class BuildOptions | ||
{ | ||
private static readonly PropertyInfo s_analyzerConfigPathsProperty = typeof(CommandLineArguments).GetProperty("AnalyzerConfigPaths"); | ||
private static readonly PropertyInfo? s_analyzerConfigPathsProperty = typeof(CommandLineArguments).GetProperty("AnalyzerConfigPaths"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type.GetProperty
is not annotated. I saw a later null
test and annotated this. If the ?
here was removed, it would still compile without warning.
I mention this as there are probably other places like this in the repo.
@@ -49,7 +46,7 @@ public Task OnProjectFactoryCompleted() | |||
} | |||
else | |||
{ | |||
_projectLoadedInHost.TrySetResult(false); | |||
_projectLoadedInHost.TrySetResult(null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We were boxing a bool as an object here.
@@ -69,7 +67,7 @@ internal Enumerator(in LazyStringSplit split) | |||
_index = 0; | |||
_input = split._input; | |||
_delimiter = split._delimiter; | |||
Current = null; | |||
Current = null!; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this makes sense for enumerators. Technically Current
can be null, but 99.99% of consumers are foreach
, in which case you won't see that state of the enumerator. You can still see nulls if the sequence has null elements though (which it won't here).
@@ -75,7 +75,7 @@ void HandleChangesForRule(bool resolved, IProjectChangeDescription projectChange | |||
foreach (string removedItem in projectChange.Difference.RemovedItems) | |||
{ | |||
string dependencyId = resolved | |||
? projectChange.Before.GetProjectItemProperties(removedItem).GetStringProperty(ResolvedAssemblyReference.OriginalItemSpecProperty) ?? removedItem | |||
? projectChange.Before.GetProjectItemProperties(removedItem)!.GetStringProperty(ResolvedAssemblyReference.OriginalItemSpecProperty) ?? removedItem |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assuming the changed item has properties here, hence !
.
|
||
string originalItemSpec = resolved | ||
? properties.GetStringProperty(ResolvedAssemblyReference.OriginalItemSpecProperty) | ||
? properties.GetStringProperty(ResolvedAssemblyReference.OriginalItemSpecProperty) ?? itemSpec |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Behaviour change. if somehow the original item spec property was null, use the item spec.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably change these methods to return empty to behave identiical to MSBuild; if a property doesn't exist its "empty". Probably need to rationalize these extension methods with this ProjectRuleSnapshotExtensions
.
@@ -37,7 +36,7 @@ internal static class MetadataExtensions | |||
/// <param name="key">The key that identifies the property to look up.</param> | |||
/// <param name="stringValue">The value of the string if found and non-empty, otherwise <see langword="null"/>.</param> | |||
/// <returns><see langword="true"/> if the property was found with a non-empty value, otherwise <see langword="false"/>.</returns> | |||
public static bool TryGetStringProperty(this IImmutableDictionary<string, string> properties, string key, out string stringValue) | |||
public static bool TryGetStringProperty(this IImmutableDictionary<string, string> properties, string key, [NotNullWhen(returnValue: true)] out string? stringValue) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TryGet*
pattern.
src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/FaultExtensions.cs
Outdated
Show resolved
Hide resolved
...rojectSystem.Managed/ProjectSystem/LanguageServices/FSharp/FSharpCommandLineParserService.cs
Outdated
Show resolved
Hide resolved
...isualStudio.ProjectSystem.Managed/ProjectSystem/OnceInitializedOnceDisposedUnderLockAsync.cs
Show resolved
Hide resolved
...ctSystem/LanguageServices/WorkspaceProjectContextHost.WorkspaceProjectContextHostInstance.cs
Show resolved
Hide resolved
This allows marking the fields as non-nullable.
...rojectSystem.Managed/ProjectSystem/LanguageServices/FSharp/FSharpCommandLineParserService.cs
Outdated
Show resolved
Hide resolved
Potentially masks bugs when the import fails.
We're want to merge this in quickly to avoid it fall behind. @jmarolf Are you good with the change? |
Yep, lets not hold this one up |
No description provided.