icsharpcode / nullabilityinference Goto Github PK
View Code? Open in Web Editor NEWGlobal type inference for C# 8 nullable reference types
License: MIT License
Global type inference for C# 8 nullable reference types
License: MIT License
The primary use-case (how I ended up starting this project) was trying to annotate ILSpy's decompiler engine, which quickly felt like a task that could be automated.
Timeline:
NotImplementedException
[NotNullWhen(true)]
-inference finally works correctly --> 722 warnings.In the remaining warnings, I see some categories of problems occurring repeatedly:
[AllowNullable]
yetT: notnull
constraints yetEven on .NET Core 3.1, not all system libraries are annotated.
For example, the System.Linq
methods are lacking annotations.
This means a call like collection.FirstOrDefault()
will:
This can cause significantly wrong annotations being inferred (and then accepted by the compiler without warning).
Maybe we should somehow include the .NET 5 annotations with the inference tool, so that it can produce useful results on projects targeting .NET Core 3 or even .NET Framework 4.x?
It would be more convenient to install and use this tool as dotnet tool.
Hello!
I tried running InferNull
on the SharpZipLib source and it failed with a duplicate key exception.
I added some debugging output to see what is going on, and it seems like it chokes on the documentation comment types:
Perhaps this is a known issue? I will try substituting the missing SyntaxType
s to continue testing.
The initial duplicate key exception was thrown in ICSharpCode.NullabilityInference.SyntaxToNodeMapping.CreateNewNode()
and the missing key was in ICSharpCode.NullabilityInference.SyntaxToNodeMapping[TypeSyntax syntax]
[return: NotNullIfNotNull(paramName)]
is a semi-common attribute to use, especially in some code bases that like to use:
if (input == null) return null;
at the start of many functions.
Unlike [NotNullWhen(bool)]
for out parameters, I don't see a clean way to infer NotNullIfNotNull
with our current algorithm.
But it would be valuable to figure something out, so I'm creating this issue to collect some cases of [NotNullIfNotNull]
methods and their constraint graphs.
Currently our inference re-uses Roslyn's flow-analysis.
However, this has some fundamental problems.
9: Dictionary<T, Node> mapping = new Dictionary<T, Node>();
11: Node? GetNode(T element)
{
13: Node? node;
14: if (!mapping.TryGetValue(element, out node))
15: {
16: node = new Node();
17: mapping.Add(element, node);
18: }
19: return node;
}
There's no edges created for line 16/17 because here Roslyn knows that node
is non-null.
Line 14 creates two edges: one from <nullable>
because TryGetValue
will assign null
when returning false
; the other from mapping!1#2
(the mapping field's Node
type argument) when TryGetValue
returns true.
The return
statement creates an edge from the variable's type, because Roslyn's flow analysis can't guarantee us that the variable is non-null -- our Roslyn code analysis runs under the pessimistic assumption that all types-to-be-inferred might end up nullable, so it considers mapping
to be Dictionary<T, Node?>
, which leaves open the possibility that GetNode
returns null.
However, after our inference decides that mapping!1#2
is non-null, it would be correct to also indicate that the GetNode
return value is non-null. After all, if no node exists yet, the function will create one.
The issue here is that Roslyn's flow analysis isn't aware of our types-to-be-inferred.
It would be better if, instead of using Roslyn's flow analysis, we had our own that keeps track of node
's nullability.
The idea would be to create additional "helper" graph nodes for the different flow states of a local variable of reference type.
After TryGetValue
initializes node
, it's flow-state would be (true: mapping!1#2
, false: <nullable>
). Within the if
body, the flow-state would initially be <nullable>
, but after line 16 would change to <nonnull>
.
After the if, the flow-state from both alternatives could be re-combined by creating a new node "node-after-line-18" and edges from the nodes from the two if-branches -- in this case <nonnull>
from the then-branch and mapping!1#2
from the else branch.
Then the return
statement would create an edge from this "node-after-line-18" instead of the node for the variable's declared type.
All flow-state nodes associated with a variable would have an edge to the variable's declared type node.
We'd end up with a graph somewhat like this:
Thus in the end, node
would be inferred as nullable, but the GetNode
return type would only depend on mapping!1#2
and thus can be inferred depending on whether there's a mapping.Add(x, null)
access somewhere else in the program.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.