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

SE: Do not learn Null constraint from NotNullWhenAttribute #6159

Merged
merged 3 commits into from
Oct 4, 2022

Conversation

pavel-mikula-sonarsource
Copy link
Contributor

@pavel-mikula-sonarsource pavel-mikula-sonarsource commented Oct 3, 2022

Fixes some Peach issues:

Issue
Line 67

            var managerType = typeof(SignInManager<>).MakeGenericType(builder.UserType);
            var customType = typeof(TSignInManager);
            if (!managerType.IsAssignableFrom(customType))
            {
                throw new InvalidOperationException(Resources.FormatInvalidManagerType(customType.Name, "SignInManager", builder.UserType.Name));

Issue
Line 423

Uri uriGrammar;
bool hasUri = Uri.TryCreate(grammarName, UriKind.Absolute, out uriGrammar);
int posDll = grammarName.IndexOf(".dll", StringComparison.OrdinalIgnoreCase);

Issue
Line 61

foreach (SyntaxNode leaf in node.ChildNodes())
            {
                if (leaf.IsKind(SyntaxKind.ArgumentList) == false && leaf.ToString().EndsWith("ToDictionary"))

..and few other similar cases

@sonarcloud
Copy link

sonarcloud bot commented Oct 3, 2022

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

No Coverage information No Coverage information
No Duplication information No Duplication information

@sonarcloud
Copy link

sonarcloud bot commented Oct 3, 2022

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

100.0% 100.0% Coverage
0.0% 0.0% Duplication

@martin-strecker-sonarsource
Copy link
Contributor

I think the root cause of the issues is not [NotNullWhen] handling in all three cases:

typeof

Line 67

Should be fixed by: 6157

Line 423

Multiple attributes

grammarName has the NotNull constraint before the call to TryCreate and therefore the state should not explode. I think the root cause is the wrong handling of multiple "NotNullWhen" attributes in conjunction with the last parameter which is out and has special handling in our logic:

public static bool TryCreate(
  [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true), System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("Uri")] string? uriString,
  in System.UriCreationOptions creationOptions,
  [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Uri? result) { throw null; }

Source

IsKind extension method

IsKind is tested for ==false and therefore it might be null after the &&. We did not learn that leaf is NotNull before the IsKind call. The only way to learn that leaf is not null there is to also learn from the FlowState of Roslyn which would prevent this kind of FPs.

We could suppress these kinds of FPs with this PR, but please add issues for each root cause, so we can tackle them the next time we come back to SE.

Copy link
Contributor

Choose a reason for hiding this comment

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

This kills some FPs and some TPs. I'm not sure if we want to do this, because most FPs have a different root cause.

@@ -58,7 +58,7 @@ protected override ProgramState[] Process(SymbolicContext context, IInvocationOp
_ when invocation.TargetMethod.ContainingType.IsAny(KnownType.System_Linq_Enumerable, KnownType.System_Linq_Queryable) => ProcessLinqEnumerableAndQueryable(context, invocation),
_ when invocation.TargetMethod.Name == nameof(object.Equals) => ProcessEquals(context, invocation),
_ when invocation.TargetMethod.IsAny(KnownType.System_String, nameof(string.IsNullOrEmpty), nameof(string.IsNullOrWhiteSpace)) =>
ProcessIsNotNullWhen(state, invocation.WrappedOperation, invocation.Arguments[0].ToArgument(), false),
ProcessIsNotNullWhen(state, invocation.WrappedOperation, invocation.Arguments[0].ToArgument(), false, true),

Choose a reason for hiding this comment

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

Suggested change
ProcessIsNotNullWhen(state, invocation.WrappedOperation, invocation.Arguments[0].ToArgument(), false, true),
ProcessIsNotNullWhen(state, invocation.WrappedOperation, invocation.Arguments[0].ToArgument(), when: false, learnNull: true),

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'd consider that in case there are 3 of them

@@ -70,13 +70,13 @@ private static ProgramState[] ProcessIsNotNullWhen(ProgramState state, IInvocati
if (argument.Parameter?.GetAttributes().FirstOrDefault(x => x.HasName("NotNullWhenAttribute")) is { } attribute
&& attribute.TryGetAttributeValue<bool>("returnValue", out var returnValue))
{
return ProcessIsNotNullWhen(state, invocation.WrappedOperation, argument, returnValue);
return ProcessIsNotNullWhen(state, invocation.WrappedOperation, argument, returnValue, false);

Choose a reason for hiding this comment

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

named argument for "false"?

@pavel-mikula-sonarsource
Copy link
Contributor Author

We strongly prefer FNs over FPs.
grammarName doesn't have NotNull, I've created #6158 yesterday to build it in the future.
The IsKind was a motivation for this PR. It just creates too many FPs.

Copy link
Contributor

Choose a reason for hiding this comment

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

LGTM

grammarName doesn't have NotNull, I've created #6158 yesterday to build it in the future.

Ah, I missed the re-assignment part. It makes sense to add support for string methods.

The IsKind was a motivation for this PR. It just creates too many FPs.

That makes sense. I think we can re-add the constraint when we also take the "NullableFlowState" into account. If Roslyn thinks, that the value "MayBeNull" we can also add the "Null" constraint case. In the case of "None" (aka no nullable analysis performed by Roslyn) or "NotNull" we should not add the null constraint case.

Best Kanban automation moved this from Review in progress to Review approved Oct 4, 2022
@martin-strecker-sonarsource martin-strecker-sonarsource merged commit 5b1e9cf into master Oct 4, 2022
Best Kanban automation moved this from Review approved to Validate Peach Oct 4, 2022
@martin-strecker-sonarsource martin-strecker-sonarsource deleted the Pavel/SE/Type_IsAssignableFrom branch October 4, 2022 14:16
@pavel-mikula-sonarsource pavel-mikula-sonarsource moved this from Validate Peach to Done in Best Kanban Oct 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Best Kanban
  
Done
Development

Successfully merging this pull request may close these issues.

None yet

2 participants