Skip to content
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

Add nullable annotations to System.DirectoryServices #48454

Merged
merged 3 commits into from
Mar 4, 2021

Conversation

krwq
Copy link
Member

@krwq krwq commented Feb 18, 2021

No description provided.

@dotnet-issue-labeler
Copy link

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, to please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

Copy link
Member

@buyaa-n buyaa-n left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @krwq i have 2 main comment:

  1. Most of the comments related to PropertyManager.GetPropertyValue(...) which if look closely does not return null so better to remove ? which saving many 1 in many files
  2. Most overrides of OnInsertComplete(int index, object? value), OnRemoveComplete(int index, object? value), OnSetComplete(int index, object? oldValue, object? newValue) are producing NRE if the value is null, i think we should suppress the warning instead making it nullable

Other than that overall LGTM.

@@ -144,7 +144,7 @@ public static AdamInstance GetAdamInstance(DirectoryContext context)
{
throw new ActiveDirectoryObjectNotFoundException(SR.Format(SR.AINotFound, context.Name), typeof(AdamInstance), context.Name);
}
dnsHostName = (string)PropertyManager.GetPropertyValue(context, rootDSE, PropertyManager.DnsHostName);
dnsHostName = (string)PropertyManager.GetPropertyValue(context, rootDSE, PropertyManager.DnsHostName)!;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and everywhere else PropertyManager.GetPropertyValue(context, rootDSE, PropertyManager.DnsHostName) is not returning null, instead it throws

Suggested change
dnsHostName = (string)PropertyManager.GetPropertyValue(context, rootDSE, PropertyManager.DnsHostName)!;
dnsHostName = (string)PropertyManager.GetPropertyValue(context, rootDSE, PropertyManager.DnsHostName);

Copy link
Member Author

@krwq krwq Feb 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note it will only throw if property value is not there at all but if null value is present it will not throw and return null instead. Also note that DirectoryEntry.Properties (which this helper accesses) is public and while there is a code path guarding against null in DirectoryEntry.Properties[anyName].Value (it's a no-op) then there is nothing guarding against DirectoryEntry.Properties[anyName].Add(null).

Copy link
Member

@buyaa-n buyaa-n Feb 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, the code is not guaranteeing against null, but somehow PropertyValueCollection[index] and PropertyValueCollection.Add(object value) both documented to be non null (it says it throws ANE for null value). Literally for all references we are suppressing the return value from (string)PropertyManager.GetPropertyValue(context, rootDSE, PropertyManager.DnsHostName)! which also illustrates the intention was non-null, there is something wrong here or we are missing something.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With that being said I am resolving this and all comments related to this, please take a look at other unrelated comments

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is nothing guarding against DirectoryEntry.Properties[anyName].Add(null)

Even it is allowing null I think setting it into null should be an error, not sure who is the expert in this area, I am OK leaving this nullable for this PR but i think we should file an issue for this

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -195,7 +196,7 @@ public TimeSpan ClientTimeout
/// Gets or sets the Lightweight Directory Access Protocol (LDAP) filter string format.
/// </devdoc>
[DefaultValue(defaultFilter)]
public string Filter
public string? Filter
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it shouldn't be nullable as it has defaulted to non-null and the setter is allowing null but also defaulting if the value was null, it will never be null unless set to null by constructor which might be mistake

Suggested change
public string? Filter
[AllowNull]
public string Filter

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've initially had it this way but this can currently be null (maybe due to bug, maybe intentional):

  • public DirectorySearcher(DirectoryEntry? searchRoot, string? filter, string[]? propertiesToLoad, SearchScope scope) is assigning filter directly and does not do any validation
  • in Utils.cs: internal static Hashtable GetValuesWithRangeRetrieval(DirectoryEntry searchRootEntry, string? filter, ArrayList propertiesWithRangeRetrieval, ArrayList propertiesWithoutRangeRetrieval, SearchScope searchScope) is being called with explicit null from DirectoryServer.cs GetParitions() (call goes through internal static Hashtable GetValuesWithRangeRetrieval(DirectoryEntry searchRootEntry, string? filter, ArrayList propertiesToLoad, SearchScope searchScope))

I'll leave this open and turn this into an issue

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(likely constructor should assign to property rather than field)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{
}

/// <devdoc>
/// Initializes a new instance of the <see cref='System.DirectoryServices.DirectorySearcher'/> class with the <see cref='System.DirectoryServices.DirectorySearcher.SearchRoot'/>, <see cref='System.DirectoryServices.DirectorySearcher.Filter'/>, <see cref='System.DirectoryServices.DirectorySearcher.PropertiesToLoad'/>, and <see cref='System.DirectoryServices.DirectorySearcher.SearchScope'/> properties set to the given
/// values.
/// </devdoc>
public DirectorySearcher(DirectoryEntry searchRoot, string filter, string[] propertiesToLoad, SearchScope scope)
public DirectorySearcher(DirectoryEntry? searchRoot, string? filter, string[]? propertiesToLoad, SearchScope scope)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filter seems not expected to be nullable, it defaults with nonnull when not provided, the setter is allowing null but also defaulting if the value was null, it might be an error if it is set to null by the constructor

Copy link
Member Author

@krwq krwq Feb 24, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add this info to the issue above once I create it but I'd say it should handle the null given this never threw anything and there is an obvious value which should be used in place of null

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@krwq
Copy link
Member Author

krwq commented Feb 24, 2021

I've rebased since there was recently some change in DS

Base automatically changed from master to main March 1, 2021 09:08
Copy link
Member

@stephentoub stephentoub left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only spot checked again, but what I saw LGTM.

@krwq
Copy link
Member Author

krwq commented Mar 4, 2021

(adjusted nullability of stuff deriving from CollectionBase based on offline conversation)

@krwq
Copy link
Member Author

krwq commented Mar 4, 2021

Given it's already signed off, CI is passing, all issues are resolved/turned into issues and only changes made after the sign-off are what @buyaa-n recommended in the comment and @stephentoub has also confirmed in the offline conversation as a right path I'm merging this.

@krwq krwq merged commit 9c628c3 into dotnet:main Mar 4, 2021
@ghost ghost locked as resolved and limited conversation to collaborators Apr 3, 2021
@karelz karelz added this to the 6.0.0 milestone May 20, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants