提交 7179442d 编写于 作者: A AlekseyTs

Merge pull request #6962 from AlekseyTs/NullableReferenceTypes/3

Initial support for Nullable Reference Types.
Implicit type declarations:
- Properties of anonymous types are considered nullable if their type is a reference type.
- Element type of an array created by implicitly typed array creation expression is considered nullable if the type is a reference type.
- Type of a var local is considered nullable if the type is a reference type.
For the purpose of flow analysis, result of expression can have three states:
- Assumed to have not null value.
- Possibly has null value.
- Unknown nullability (no diagnostics is derived from this state). For example, this state can arise from usage of an API that hasn’t been annotated yet.
Tracking state of
- Locals
- Parameters
- Fields of structures
- Anonymous Type properties
The goal is to track state of the same entities, which are tracked by Definite Assignment, plus state of properties of Anonymous Types.
Warn about possible null reference on
- An assignment to a local statically typed as not-nullable.
- An assignment to a parameter statically typed as not-nullable.
- An assignment to a field statically typed as not-nullable.
- An assignment to a property statically typed as not-nullable.
- An assignment to an indexer statically typed as not-nullable.
- An assignment to an array element statically typed as not-nullable.
- A member initializer for a member statically typed as not-nullable.
- An argument passed to a parameter statically typed as not-nullable.
- An argument (local, parameter, field, array element) statically typed as not-nullable passed as ref/out to a parameter statically typed as nullable.
- A return expression if return type of the method is statically typed as not-nullable.
- A receiver of a method/field/property/indexer access.
- An array expression of an array access.
A local is considered to have a not null value if the local is statically typed as not-nullable, or the last value assigned to the field was not null.
A parameter is considered to have a not null value if the parameter is statically typed as not-nullable, or the last value assigned to the parameter was not null and the parameter is not ref/out.
A field is considered to have a not null value if the field is statically typed as not-nullable, or the field is tracked by the flow analysis and the last value assigned to the field was not null.
A property is considered to have a not null value if the property is statically typed as not-nullable, or the property is tracked by the flow analysis (readonly auto-property of a structure in its constructor, or a property of an Anonymous Type) and the last value assigned to the property was not null.
Result of an indexer access is considered to be a not null value if the indexer is statically typed as not-nullable.
Result of a method call is considered to be a not null value if its return type is statically typed as not-nullable.
Passing a tracked structure or a tracked Anonymous Type instance by reference invalidates accumulated tracking information for their members.
When a built-in or a user-defined operator ==/!= is used to compare an expression to a null value.
For the purpose of flow analysis, a trackable expression is considered to be not null when == evaluates to false, or != evaluates to true. If, according to flow analysis, the expression has a not null value before the operator is evaluated, a warning is reported that result of the comparison is always false (==), or always true (!=).
When expression is used as a left operand of a Null Coalescing Operator (??).
For the purpose of flow analysis, a trackable expression is considered to be null before the right operand is evaluated. If, according to flow analysis, the expression has a not null value before the operator is evaluated, a warning is reported that the operand is never null. Result of the operator is considered to be not null for the purpose of the flow analysis if either operand is considered to be not null.
When expression is used as a receiver of a conditional access (?./?[]).
For the purpose of flow analysis, a trackable expression is considered to be not null before the access is evaluated. If, according to flow analysis, the expression has a not null value before the receiver is evaluated, a warning is reported that the receiver is never null. Result of the operator is considered to be not null for the purpose of the flow analysis only if both, the receiver and the access are considered to be not null.
# Proposal: Static null checking in C#
Null reference exceptions are rampant in languages like C\#, where any reference type can reference a null value. Some type systems separate types into a `T` that cannot be null and an `Option<T>` (or similar) that can be null but cannot be dereferenced without an explicit null check that unpacks the non-null value.
This approach is attractive, but is difficult to add to a language where every type has a default value. For instance, a newly created array will contain all nulls. Also, such systems are notorious for problems around initialization and cyclic data structures, unless non-null types are permitted to at least temporarily contain null.
On top of these issues come challenges stemming from the fact that C\# already has null-unsafe types, that allow both null values and dereferencing. How can a safer approach be added to the language without breaking changes, without leaving the language more complex than necessary and with a natural experience for hardening existing code against null errors in your own time?
# Approach: Nullable reference types plus nullability warnings
The approach suggested here consists of a couple of elements:
* Add a notion of safe nullable reference types `T?` to the language, in addition to the current ones `T` which we will now call non-nullable.
* Detect and warn on cases where nullable types are dereferenced, or when null is values are assigned to (or left in) non-nullable variables.
* Offer opt-in means to deal with breaking behavior and compat across versions and assemblies.
The feature will not provide airtight guarantees, but should help find most nullability errors in code. It can be assumed that most values are intended *not* to be null, so this scheme provides the minimal annotation overhead.
Nullable reference types are helpful, in that they help find code that may dereference null and help guard it with null checks. Non-nullability warnings are helpful in that they help prevent variables from inadvertently containing an null value.
# Nullable and non-nullable reference types
For every non-nullable reference type `T`, there is now a corresponding nullable reference type `T?`.
Syntactically speaking, this doesn't add much, since nullable value types have the same syntax. However, a few syntactic corner cases are new, like `T[]?`.
From a semantic viewpoint, `T` and `T?` are equivalent in every way: they are the same type. The *only* way in which they differ is in the warnings caused by their use.
For type inference purposes `T` and `T?` are considered the same type. If a reference type `T` is the result of type inference, and at least one of the candidate reference types is nullable, then the result is nullable too.
If an expression is by declaration of type `T?`, it will still be considered to be of type `T` if it occurs in a context where by flow analysis we consider it known that it is not null.
## Warnings for nullable reference types
Values of nullable reference types should not be used in a connection where a) they are not known to (probably) contain a non-null value and b) the use would require them to be non-null. Such uses will be flagged with a warning.
a) means that a flow analysis has determined that they are very likely to not be null. There will be specific rules for this flow analysis, similar to those for definite assignment. It is an open question which variables are tracked by this analysis. Just locals and parameters? All dotted names?
b) means dereferencing (e.g. with dot or invocation) or implicitly converting to a non-nullable reference type.
## Warnings for non-nullable reference types
Variables of non-nullable reference types should not be assigned the literal `null` or `default(T)`; nor should nullable value types be boxed to them. Such uses will result in a warning.
Additionally, fields of non-nullable reference type must be protected by their constructor so that they are a) not used before they are assigned, and b) assigned before the constructor returns. Otherwise a warning is issued. (As an alternative to (a) we can consider allowing use before assignment, but in that case treating the variable as nullable.)
Note that there is no warning to prevent new arrays of non-nullable reference type from keeping the null elements they are initially created with. There is no good static way to ensure this. We *could* consider a requirement that *something* must be done to such an array before it can be read from, assigned or returned; e.g. there must be at least one element assignment to it, or it must be passed as an argument to something that could potentially initialize it. That would at least catch the situation where you simply forgot to initialize it. But it is doubtful that this has much value.
# Generics
Constraints can be both nullable and non-nullable. The default constraint for an unconstrained type parameter is `object?`.
A warning is issued if a type parameter with at least one non-nullable reference constraint is instantiated with a nullable reference type.
A type parameter with at least one non-nullable constraint is treated as a non-nullable type in terms of warnings given.
A type parameter with no non-nullable reference constraints is treated as *both* a nullable *and* a non-nullable reference type in terms of warnings given (since it could without warning have been instantiated with either). This means that *both* sets of warnings apply.
`?` is allowed to be applied to any type parameter `T`. For type parameters with the `struct` constraint it has the usual meaning. For all other type parameters it has this meaning, where `S` is the type with which `T` is instantiated:
* If `S` is a non-nullable reference type then `T?` refers to `S?`
* Otherwise, `S?` refers to `S`
Note: This rule is not elegant - in particular it is bad that the introduction of a `struct` constraint changes the meaning of `?`. But we believe we need it to faithfully express the type of common APIs such as `FirstOrDefault()`.
## Opting in and opting out
Some of the nullability warnings warn on code that exists without warnings today. There should be a way of opting out of those nullability warnings for compatibility purposes.
When opting *in*, assemblies generated should contain an assembly-level attribute with the purpose of signalling that nullable and non-nullable types in signatures should generate appropriate warnings in consuming code.
When consuming code references an assembly that does *not* have such a top-level attribute, the types in that assembly should be treated as *neither* nullable *nor* non-nullable. That is, neither set of warnings should apply to those types.
This mechanism exists such that code that was not written to work with nullability warnings, e.g. code from a previous version of C\#, does indeed not trigger such warnings. Only assemblies that opt in by having the compiler-produced attribute, will cause the nullability warnings to happen in consuming code accessing their signatures.
When warnings haven't been opted in to, the compiler should give some indication that there are likely bugs one would find by opting in. For instance, it could give (as an informational message, not a warning) a count of how many nullability warnings it would have given.
Even when a library has opted in, consuming code may be written with an earlier version of C\#, and may not recognize the nullability annotations. Such code will work without warning. To facilitate smooth upgrade of the consuming code, it should probably be possible to opt out of the warnings from *a given* library that will now start to occur. Again, such per-assebly opt-out could be accompanied by an informational message reminding that nullability bugs may be going unnoticed.
# Libraries and compatibility
An example: In my C\# client code, I use libraries A and B:
``` c#
// library A
public class A
{
public static string M(string s1, string s2);
}
// library B
public class B
{
public static object N(object o1, object o2);
}
// client C, referencing A and B
Console.WriteLine(A.M("a", null).Length);
Console.WriteLine(B.N("b", null).ToString());
```
Now library B upgrades to C\# 7, and starts using nullability annotations:
``` c#
// upgraded library B
public class B
{
public static object? N(object o1, object o2); // o1 and o2 not supposed to be null
}
```
It is clear that my client code probably has a bug: apparently it was not supposed to pass null to B.N. However, the C\# 6 compiler knows nothing of all this, and ignores the assembly-level attribute opting in to it.
Now I upgrade to C\# 7 and start getting warnings on my call to B.N: the second argument shouldn't be null, and I shouldn't dot into the return value without checking it for null. It may not be convenient for me to look at those potential bugs right now; I just want a painless upgrade. So I can opt out of getting nullability warnings at all, or for that specific assembly. On compile, I am informed that I may have nullability bugs, so I don't forget to turn it on later.
Eventually I do, I get my warnings and I fix my bugs:
``` c#
Console.WriteLine(B.N("b", "")?.ToString());
```
Passing the empty string instead of null, and using the null-conditional operator to test the result for null.
Now the owner of library A decides to add nullability annotations:
``` c#
// library A
public class A
{
public static string? M(string s1, string s2); // s1 and s2 shouldn't be null
}
```
As I compile against this new version, I get new nullability warnings in my code. Again, I may not be ready for this - I may have upgraded to the new version of the library just for bug fixes - and I may temporarily opt out for that assembly.
In my own time, I opt it in, get my warnings and fix my code. I am now completely in the new world. During the whole process I never got "broken" unless I asked for it with some explicit gesture (upgrade compiler or libraries), and was able to opt out if I wasn't ready. When I did opt in, the warnings told me that I used a library against its intentions, so fixing those places probably addressed a bug.
\ No newline at end of file
......@@ -80,7 +80,8 @@ private BoundExpression BindAnonymousObjectCreation(AnonymousObjectCreationExpre
// build anonymous type field descriptor
fieldSyntaxNodes[i] = (nameToken.Kind() == SyntaxKind.IdentifierToken) ? (CSharpSyntaxNode)nameToken.Parent : fieldInitializer;
fields[i] = new AnonymousTypeField(fieldName == null ? "$" + i.ToString() : fieldName, fieldSyntaxNodes[i].Location, TypeSymbolWithAnnotations.Create(fieldType));
fields[i] = new AnonymousTypeField(fieldName == null ? "$" + i.ToString() : fieldName, fieldSyntaxNodes[i].Location,
TypeSymbolWithAnnotations.Create(fieldType, makeNullableIfReferenceType: true));
// NOTE: ERR_InvalidAnonymousTypeMemberDeclarator (CS0746) would be generated by parser if needed
}
......
......@@ -2080,7 +2080,8 @@ private BoundExpression BindImplicitArrayCreationExpression(ImplicitArrayCreatio
Error(diagnostics, ErrorCode.ERR_ArrayElementCantBeRefAny, node, bestType);
}
var arrayType = ArrayTypeSymbol.CreateCSharpArray(Compilation.Assembly, TypeSymbolWithAnnotations.Create(bestType), rank);
// Default inferred reference types to a nullable state.
var arrayType = ArrayTypeSymbol.CreateCSharpArray(Compilation.Assembly, TypeSymbolWithAnnotations.Create(bestType, makeNullableIfReferenceType: true), rank);
return BindArrayCreationWithInitializer(diagnostics, node, initializer, arrayType,
sizes: ImmutableArray<BoundExpression>.Empty, boundInitExprOpt: boundInitializerExpressions);
}
......
......@@ -634,8 +634,8 @@ private BoundExpression MakePair(CSharpSyntaxNode node, string field1Name, Bound
AnonymousTypeDescriptor typeDescriptor = new AnonymousTypeDescriptor(
ImmutableArray.Create<AnonymousTypeField>(
new AnonymousTypeField(field1Name, field1Value.Syntax.Location, TypeSymbolWithAnnotations.Create(TypeOrError(field1Value))),
new AnonymousTypeField(field2Name, field2Value.Syntax.Location, TypeSymbolWithAnnotations.Create(TypeOrError(field2Value)))
new AnonymousTypeField(field1Name, field1Value.Syntax.Location, TypeSymbolWithAnnotations.Create(TypeOrError(field1Value), makeNullableIfReferenceType: true)),
new AnonymousTypeField(field2Name, field2Value.Syntax.Location, TypeSymbolWithAnnotations.Create(TypeOrError(field2Value), makeNullableIfReferenceType: true))
),
node.Location
);
......
......@@ -760,7 +760,8 @@ private TypeSymbolWithAnnotations BindVariableType(CSharpSyntaxNode declarationN
// If we got a good result then swap the inferred type for the "var"
if ((object)initializerOpt?.Type != null)
{
declTypeOpt = TypeSymbolWithAnnotations.Create(initializerOpt.Type);
// Default inferred reference types to a nullable state.
declTypeOpt = TypeSymbolWithAnnotations.Create(initializerOpt.Type, makeNullableIfReferenceType: true);
if (declTypeOpt.SpecialType == SpecialType.System_Void)
{
......
......@@ -224,14 +224,14 @@ internal NamespaceOrTypeOrAliasSymbolWithAnnotations BindTypeOrAlias(ExpressionS
var symbol = BindNamespaceOrTypeOrAliasSymbol(syntax, diagnostics, basesBeingResolved, basesBeingResolved != null);
// symbol must be a TypeSymbol or an Alias to a TypeSymbol
var result = UnwrapAliasNoDiagnostics(symbol.Symbol, basesBeingResolved) as TypeSymbol;
if ((object)result != null)
if (symbol.IsType ||
(symbol.IsAlias && UnwrapAliasNoDiagnostics(symbol.Symbol, basesBeingResolved) is TypeSymbol))
{
if ((object)result == (object)symbol.Symbol)
if (symbol.IsType)
{
// Obsolete alias targets are reported in UnwrapAlias, but if it was a type (not an
// alias to a type) we report the obsolete type here.
ReportDiagnosticsIfObsolete(diagnostics, result, syntax, hasBaseReceiver: false);
((TypeSymbolWithAnnotations)symbol).ReportDiagnosticsIfObsolete(this, syntax, diagnostics);
}
return symbol;
......@@ -291,16 +291,27 @@ internal NamespaceOrTypeOrAliasSymbolWithAnnotations BindNamespaceOrTypeOrAliasS
{
case SyntaxKind.NullableType:
{
NamedTypeSymbol nullableT = GetSpecialType(SpecialType.System_Nullable_T, diagnostics, syntax);
TypeSyntax typeArgumentSyntax = ((NullableTypeSyntax)syntax).ElementType;
TypeSymbolWithAnnotations typeArgument = BindType(typeArgumentSyntax, diagnostics, basesBeingResolved);
NamedTypeSymbol constructedType = nullableT.Construct(ImmutableArray.Create(typeArgument));
// TODO: NullableReferenceTypes - If nullable reference types aren't enabled, we can create Nullable<typeArgument> right here as an optimization.
// It is also safe to make a decision whether this is a nullable reference type or a Nullable<T> right here
// if ShouldCheckConstraints is true (we are checking constructedType.IsNullableType() right here anyway).
TypeSymbolWithAnnotations constructedType = typeArgument.AsNullableReferenceOrValueType(Compilation, syntax.GetReference());
if (ShouldCheckConstraints)
{
constructedType.CheckConstraints(this.Compilation, this.Conversions, syntax.Location, diagnostics);
if (constructedType.IsNullableType())
{
ReportUseSiteDiagnostics(constructedType.TypeSymbol.OriginalDefinition, diagnostics, syntax);
((NamedTypeSymbol)constructedType.TypeSymbol).CheckConstraints(this.Compilation, this.Conversions, syntax.Location, diagnostics);
}
}
else
{
diagnostics.Add(new LasyUseSiteDiagnosticsInfo(constructedType), syntax.GetLocation());
}
return TypeSymbolWithAnnotations.Create(constructedType);
return constructedType;
}
case SyntaxKind.PredefinedType:
......@@ -579,7 +590,7 @@ private static Symbol UnwrapAliasNoDiagnostics(Symbol symbol, ConsList<Symbol> b
private NamespaceOrTypeSymbolWithAnnotations UnwrapAlias(NamespaceOrTypeOrAliasSymbolWithAnnotations symbol, DiagnosticBag diagnostics, CSharpSyntaxNode syntax, ConsList<Symbol> basesBeingResolved = null)
{
if (symbol.Kind == SymbolKind.Alias)
if (symbol.IsAlias)
{
AliasSymbol discarded;
return NamespaceOrTypeSymbolWithAnnotations.Create((NamespaceOrTypeSymbol)UnwrapAlias(symbol.Symbol, out discarded, diagnostics, syntax, basesBeingResolved));
......@@ -590,7 +601,7 @@ private NamespaceOrTypeSymbolWithAnnotations UnwrapAlias(NamespaceOrTypeOrAliasS
private NamespaceOrTypeSymbolWithAnnotations UnwrapAlias(NamespaceOrTypeOrAliasSymbolWithAnnotations symbol, out AliasSymbol alias, DiagnosticBag diagnostics, CSharpSyntaxNode syntax, ConsList<Symbol> basesBeingResolved = null)
{
if (symbol.Kind == SymbolKind.Alias)
if (symbol.IsAlias)
{
return NamespaceOrTypeSymbolWithAnnotations.Create((NamespaceOrTypeSymbol)UnwrapAlias(symbol.Symbol, out alias, diagnostics, syntax, basesBeingResolved));
}
......
......@@ -345,6 +345,7 @@
<Compile Include="Errors\DiagnosticBagExtensions.cs" />
<Compile Include="Errors\DiagnosticInfoWithSymbols.cs" />
<Compile Include="Errors\ErrorFacts.cs" />
<Compile Include="Errors\LasyUseSiteDiagnosticsInfo.cs" />
<Compile Include="Errors\LazyObsoleteDiagnosticInfo.cs" />
<Compile Include="Errors\MessageID.cs" />
<Compile Include="Errors\MessageProvider.cs" />
......@@ -359,6 +360,7 @@
<Compile Include="FlowAnalysis\ControlFlowPass.cs" />
<Compile Include="FlowAnalysis\CSharpDataFlowAnalysis.cs" />
<Compile Include="FlowAnalysis\DataFlowPass.cs" />
<Compile Include="FlowAnalysis\DataFlowPass.ObjectCreationPlaceholderLocal.cs" />
<Compile Include="FlowAnalysis\DataFlowPass.VariableIdentifier.cs" />
<Compile Include="FlowAnalysis\DataFlowsInWalker.cs" />
<Compile Include="FlowAnalysis\DataFlowsOutWalker.cs" />
......
......@@ -9260,6 +9260,15 @@ internal class CSharpResources {
}
}
/// <summary>
/// Looks up a localized string similar to static null checking.
/// </summary>
internal static string IDS_FeatureStaticNullChecking {
get {
return ResourceManager.GetString("IDS_FeatureStaticNullChecking", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to switch on boolean type.
/// </summary>
......@@ -11483,6 +11492,24 @@ internal class CSharpResources {
}
}
/// <summary>
/// Looks up a localized string similar to Expression is probably never null..
/// </summary>
internal static string WRN_ExpressionIsProbablyNeverNull {
get {
return ResourceManager.GetString("WRN_ExpressionIsProbablyNeverNull", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Expression is probably never null..
/// </summary>
internal static string WRN_ExpressionIsProbablyNeverNull_Title {
get {
return ResourceManager.GetString("WRN_ExpressionIsProbablyNeverNull_Title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Constructor &apos;{0}&apos; is marked external.
/// </summary>
......@@ -12353,6 +12380,114 @@ internal class CSharpResources {
}
}
/// <summary>
/// Looks up a localized string similar to Result of the comparison is possibly always false..
/// </summary>
internal static string WRN_NullCheckIsProbablyAlwaysFalse {
get {
return ResourceManager.GetString("WRN_NullCheckIsProbablyAlwaysFalse", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Result of the comparison is possibly always false..
/// </summary>
internal static string WRN_NullCheckIsProbablyAlwaysFalse_Title {
get {
return ResourceManager.GetString("WRN_NullCheckIsProbablyAlwaysFalse_Title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Result of the comparison is possibly always true..
/// </summary>
internal static string WRN_NullCheckIsProbablyAlwaysTrue {
get {
return ResourceManager.GetString("WRN_NullCheckIsProbablyAlwaysTrue", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Result of the comparison is possibly always true..
/// </summary>
internal static string WRN_NullCheckIsProbablyAlwaysTrue_Title {
get {
return ResourceManager.GetString("WRN_NullCheckIsProbablyAlwaysTrue_Title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Possible null reference argument..
/// </summary>
internal static string WRN_NullReferenceArgument {
get {
return ResourceManager.GetString("WRN_NullReferenceArgument", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Possible null reference argument..
/// </summary>
internal static string WRN_NullReferenceArgument_Title {
get {
return ResourceManager.GetString("WRN_NullReferenceArgument_Title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Possible null reference assignment..
/// </summary>
internal static string WRN_NullReferenceAssignment {
get {
return ResourceManager.GetString("WRN_NullReferenceAssignment", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Possible null reference assignment..
/// </summary>
internal static string WRN_NullReferenceAssignment_Title {
get {
return ResourceManager.GetString("WRN_NullReferenceAssignment_Title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Possible dereference of a null reference..
/// </summary>
internal static string WRN_NullReferenceReceiver {
get {
return ResourceManager.GetString("WRN_NullReferenceReceiver", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Possible dereference of a null reference..
/// </summary>
internal static string WRN_NullReferenceReceiver_Title {
get {
return ResourceManager.GetString("WRN_NullReferenceReceiver_Title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Possible null reference return..
/// </summary>
internal static string WRN_NullReferenceReturn {
get {
return ResourceManager.GetString("WRN_NullReferenceReturn", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Possible null reference return..
/// </summary>
internal static string WRN_NullReferenceReturn_Title {
get {
return ResourceManager.GetString("WRN_NullReferenceReturn_Title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Obsolete member &apos;{0}&apos; overrides non-obsolete member &apos;{1}&apos;.
/// </summary>
......
......@@ -4687,4 +4687,49 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_CantInferVoid" xml:space="preserve">
<value>Cannot infer the type of '{0}' as it does not return a value.</value>
</data>
</root>
<data name="IDS_FeatureStaticNullChecking" xml:space="preserve">
<value>static null checking</value>
</data>
<data name="WRN_NullReferenceAssignment" xml:space="preserve">
<value>Possible null reference assignment.</value>
</data>
<data name="WRN_NullReferenceAssignment_Title" xml:space="preserve">
<value>Possible null reference assignment.</value>
</data>
<data name="WRN_NullReferenceReceiver" xml:space="preserve">
<value>Possible dereference of a null reference.</value>
</data>
<data name="WRN_NullReferenceReceiver_Title" xml:space="preserve">
<value>Possible dereference of a null reference.</value>
</data>
<data name="WRN_NullReferenceReturn" xml:space="preserve">
<value>Possible null reference return.</value>
</data>
<data name="WRN_NullReferenceReturn_Title" xml:space="preserve">
<value>Possible null reference return.</value>
</data>
<data name="WRN_NullReferenceArgument" xml:space="preserve">
<value>Possible null reference argument.</value>
</data>
<data name="WRN_NullReferenceArgument_Title" xml:space="preserve">
<value>Possible null reference argument.</value>
</data>
<data name="WRN_NullCheckIsProbablyAlwaysTrue" xml:space="preserve">
<value>Result of the comparison is possibly always true.</value>
</data>
<data name="WRN_NullCheckIsProbablyAlwaysTrue_Title" xml:space="preserve">
<value>Result of the comparison is possibly always true.</value>
</data>
<data name="WRN_NullCheckIsProbablyAlwaysFalse" xml:space="preserve">
<value>Result of the comparison is possibly always false.</value>
</data>
<data name="WRN_NullCheckIsProbablyAlwaysFalse_Title" xml:space="preserve">
<value>Result of the comparison is possibly always false.</value>
</data>
<data name="WRN_ExpressionIsProbablyNeverNull" xml:space="preserve">
<value>Expression is probably never null.</value>
</data>
<data name="WRN_ExpressionIsProbablyNeverNull_Title" xml:space="preserve">
<value>Expression is probably never null.</value>
</data>
</root>
\ No newline at end of file
......@@ -1321,6 +1321,14 @@ internal enum ErrorCode
ERR_ExpressionTreeContainsLocalFunction = 8103,
ERR_ReturnTypesDontMatch = 8104,
ERR_DynamicLocalFunctionParameter = 8105,
ERR_CantInferVoid = 8106
ERR_CantInferVoid = 8106,
WRN_NullReferenceAssignment = 8201,
WRN_NullReferenceReceiver = 8202,
WRN_NullReferenceReturn = 8203,
WRN_NullReferenceArgument = 8204,
WRN_NullCheckIsProbablyAlwaysTrue = 8205,
WRN_NullCheckIsProbablyAlwaysFalse = 8206,
WRN_ExpressionIsProbablyNeverNull = 8207,
}
}
......@@ -315,6 +315,13 @@ internal static int GetWarningLevel(ErrorCode code)
case ErrorCode.WRN_IdentifierOrNumericLiteralExpected:
case ErrorCode.WRN_ReferencedAssemblyDoesNotHaveStrongName:
case ErrorCode.WRN_AlignmentMagnitude:
case ErrorCode.WRN_NullReferenceAssignment:
case ErrorCode.WRN_NullReferenceReceiver:
case ErrorCode.WRN_NullReferenceReturn:
case ErrorCode.WRN_NullReferenceArgument:
case ErrorCode.WRN_NullCheckIsProbablyAlwaysTrue:
case ErrorCode.WRN_NullCheckIsProbablyAlwaysFalse:
case ErrorCode.WRN_ExpressionIsProbablyNeverNull:
return 1;
default:
return 0;
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Diagnostics;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Symbols;
namespace Microsoft.CodeAnalysis.CSharp
{
internal sealed class LasyUseSiteDiagnosticsInfo : DiagnosticInfo
{
private DiagnosticInfo _lazyActualObsoleteDiagnostic;
private readonly TypeSymbolWithAnnotations _possiblyNullableTypeSymbol;
internal LasyUseSiteDiagnosticsInfo(TypeSymbolWithAnnotations possiblyNullableTypeSymbol)
: base(CSharp.MessageProvider.Instance, (int)ErrorCode.Unknown)
{
_possiblyNullableTypeSymbol = possiblyNullableTypeSymbol;
}
internal override DiagnosticInfo GetResolvedInfo()
{
if (_lazyActualObsoleteDiagnostic == null)
{
if (_possiblyNullableTypeSymbol.IsNullableType())
{
var info = _possiblyNullableTypeSymbol.TypeSymbol.OriginalDefinition.GetUseSiteDiagnostic();
if (info != null)
{
Interlocked.CompareExchange(ref _lazyActualObsoleteDiagnostic, info, null);
return _lazyActualObsoleteDiagnostic;
}
}
// Make this a Void diagnostic.
Interlocked.CompareExchange(ref _lazyActualObsoleteDiagnostic, CSDiagnosticInfo.VoidDiagnosticInfo, null);
}
return _lazyActualObsoleteDiagnostic;
}
}
}
......@@ -10,14 +10,23 @@ internal sealed class LazyObsoleteDiagnosticInfo : DiagnosticInfo
{
private DiagnosticInfo _lazyActualObsoleteDiagnostic;
private readonly Symbol _symbol;
private readonly object _symbolOrSymbolWithAnnotations;
private readonly Symbol _containingSymbol;
private readonly BinderFlags _binderFlags;
internal LazyObsoleteDiagnosticInfo(Symbol symbol, Symbol containingSymbol, BinderFlags binderFlags)
: base(CSharp.MessageProvider.Instance, (int)ErrorCode.Unknown)
{
_symbol = symbol;
_symbolOrSymbolWithAnnotations = symbol;
_containingSymbol = containingSymbol;
_binderFlags = binderFlags;
_lazyActualObsoleteDiagnostic = null;
}
internal LazyObsoleteDiagnosticInfo(SymbolWithAnnotations symbol, Symbol containingSymbol, BinderFlags binderFlags)
: base(CSharp.MessageProvider.Instance, (int)ErrorCode.Unknown)
{
_symbolOrSymbolWithAnnotations = symbol;
_containingSymbol = containingSymbol;
_binderFlags = binderFlags;
_lazyActualObsoleteDiagnostic = null;
......@@ -29,16 +38,17 @@ internal override DiagnosticInfo GetResolvedInfo()
{
// A symbol's Obsoleteness may not have been calculated yet if the symbol is coming
// from a different compilation's source. In that case, force completion of attributes.
_symbol.ForceCompleteObsoleteAttribute();
var symbol = (_symbolOrSymbolWithAnnotations as Symbol) ?? ((SymbolWithAnnotations)_symbolOrSymbolWithAnnotations).Symbol;
symbol.ForceCompleteObsoleteAttribute();
if (_symbol.ObsoleteState == ThreeState.True)
if (symbol.ObsoleteState == ThreeState.True)
{
var inObsoleteContext = ObsoleteAttributeHelpers.GetObsoleteContextState(_containingSymbol, forceComplete: true);
Debug.Assert(inObsoleteContext != ThreeState.Unknown);
if (inObsoleteContext == ThreeState.False)
{
DiagnosticInfo info = ObsoleteAttributeHelpers.CreateObsoleteDiagnostic(_symbol, _binderFlags);
DiagnosticInfo info = ObsoleteAttributeHelpers.CreateObsoleteDiagnostic(symbol, _binderFlags);
if (info != null)
{
Interlocked.CompareExchange(ref _lazyActualObsoleteDiagnostic, info, null);
......
......@@ -115,6 +115,7 @@ internal enum MessageID
IDS_FeatureBinaryLiteral = MessageBase + 12706,
IDS_FeatureDigitSeparator = MessageBase + 12707,
IDS_FeatureLocalFunctions = MessageBase + 12708,
IDS_FeatureStaticNullChecking = MessageBase + 12709,
}
// Message IDs may refer to strings that need to be localized.
......@@ -164,7 +165,8 @@ internal static string RequiredFeature(this MessageID feature)
return "digitSeparators";
case MessageID.IDS_FeatureLocalFunctions:
return "localFunctions";
case MessageID.IDS_FeatureStaticNullChecking:
return "staticNullChecking";
default:
return null;
}
......
......@@ -65,12 +65,12 @@ private new List<Symbol> Analyze(ref bool badRegion)
return result;
}
protected override void WriteArgument(BoundExpression arg, RefKind refKind, MethodSymbol method)
protected override void WriteArgument(BoundExpression arg, RefKind refKind, MethodSymbol method, ParameterSymbol parameter)
{
// ref parameter does not "always" assign.
if (refKind == RefKind.Out)
{
Assign(arg, value: null);
Assign(arg, value: null, valueIsNotNull: null);
}
}
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp
{
internal partial class DataFlowPass
{
/// <summary>
/// A symbol to represent a placeholder for an instance being constructed by
/// <see cref="BoundObjectCreationExpression"/>. It is used to track the state
/// of members being initialized.
/// </summary>
private class ObjectCreationPlaceholderLocal : LocalSymbol
{
private readonly Symbol _containingSymbol;
private readonly TypeSymbolWithAnnotations _type;
public readonly BoundExpression ObjectCreationExpression;
public ObjectCreationPlaceholderLocal(Symbol containingSymbol, BoundExpression objectCreationExpression)
{
_containingSymbol = containingSymbol;
_type = TypeSymbolWithAnnotations.Create(objectCreationExpression.Type);
ObjectCreationExpression = objectCreationExpression;
}
public override bool Equals(object obj)
{
if ((object)this == obj)
{
return true;
}
var other = obj as ObjectCreationPlaceholderLocal;
return (object)other != null && (object)ObjectCreationExpression == other.ObjectCreationExpression;
}
public override int GetHashCode()
{
return ObjectCreationExpression.GetHashCode();
}
public override Symbol ContainingSymbol
{
get
{
return _containingSymbol;
}
}
public override ImmutableArray<SyntaxReference> DeclaringSyntaxReferences
{
get
{
return ImmutableArray<SyntaxReference>.Empty;
}
}
public override ImmutableArray<Location> Locations
{
get
{
return ImmutableArray<Location>.Empty;
}
}
public override TypeSymbolWithAnnotations Type
{
get
{
return _type;
}
}
internal override LocalDeclarationKind DeclarationKind
{
get
{
return LocalDeclarationKind.None;
}
}
internal override SyntaxToken IdentifierToken
{
get
{
throw ExceptionUtilities.Unreachable;
}
}
internal override bool IsCompilerGenerated
{
get
{
return true;
}
}
internal override bool IsImportedFromMetadata
{
get
{
return false;
}
}
internal override bool IsPinned
{
get
{
return false;
}
}
internal override RefKind RefKind
{
get
{
return RefKind.None;
}
}
internal override SynthesizedLocalKind SynthesizedKind
{
get
{
throw ExceptionUtilities.Unreachable;
}
}
internal override ConstantValue GetConstantValue(SyntaxNode node, LocalSymbol inProgress, DiagnosticBag diagnostics = null)
{
return null;
}
internal override ImmutableArray<Diagnostic> GetConstantValueDiagnostics(BoundExpression boundInitValue)
{
return ImmutableArray<Diagnostic>.Empty;
}
internal override SyntaxNode GetDeclaratorSyntax()
{
throw ExceptionUtilities.Unreachable;
}
internal override LocalSymbol WithSynthesizedLocalKindAndSyntax(SynthesizedLocalKind kind, SyntaxNode syntax)
{
throw ExceptionUtilities.Unreachable;
}
}
}
}
\ No newline at end of file
......@@ -16,7 +16,8 @@ protected struct VariableIdentifier : IEquatable<VariableIdentifier>
public VariableIdentifier(Symbol symbol, int containingSlot = 0)
{
Debug.Assert(symbol.Kind == SymbolKind.Local || symbol.Kind == SymbolKind.Field || symbol.Kind == SymbolKind.Parameter ||
(symbol as MethodSymbol)?.MethodKind == MethodKind.LocalFunction);
(symbol as MethodSymbol)?.MethodKind == MethodKind.LocalFunction ||
(symbol.Kind == SymbolKind.Property && symbol.ContainingType.IsAnonymousType));
Symbol = symbol;
ContainingSlot = containingSlot;
}
......
......@@ -123,7 +123,7 @@ protected override void ReportUnassigned(FieldSymbol fieldSymbol, int unassigned
{
// if the field access is reported as unassigned it should mean the original local
// or parameter flows in, so we should get the symbol associated with the expression
_dataFlowsIn.Add(GetNonFieldSymbol(unassignedSlot));
_dataFlowsIn.Add(GetNonMemberSymbol(unassignedSlot));
}
base.ReportUnassigned(fieldSymbol, unassignedSlot, node);
......
......@@ -184,7 +184,7 @@ private Symbol GetNodeSymbol(BoundNode node)
}
#endif
protected override void AssignImpl(BoundNode node, BoundExpression value, RefKind refKind, bool written, bool read)
protected override void AssignImpl(BoundNode node, BoundExpression value, bool? valueIsNotNull, RefKind refKind, bool written, bool read)
{
if (IsInside)
{
......@@ -210,7 +210,7 @@ protected override void AssignImpl(BoundNode node, BoundExpression value, RefKin
}
}
base.AssignImpl(node, value, refKind, written, read);
base.AssignImpl(node, value, valueIsNotNull, refKind, written, read);
}
private bool FlowsOut(ParameterSymbol param)
......@@ -257,7 +257,7 @@ protected override void ReportUnassigned(FieldSymbol fieldSymbol, int unassigned
{
// if the field access is reported as unassigned it should mean the original local
// or parameter flows out, so we should get the symbol associated with the expression
var symbol = GetNonFieldSymbol(unassignedSlot);
var symbol = GetNonMemberSymbol(unassignedSlot);
if (!_dataFlowsOut.Contains(symbol))
{
_dataFlowsOut.Add(symbol);
......
......@@ -30,7 +30,7 @@ internal abstract partial class PreciseAbstractFlowPass<LocalState> : BoundTreeV
/// 'MethodParameters', 'MethodThisParameter' and 'AnalyzeOutParameters(...)' should be used
/// instead.
/// </summary>
private readonly Symbol _member;
protected readonly Symbol _member;
/// <summary>
/// The bound node of the method or initializer being analyzed.
......@@ -72,7 +72,7 @@ internal abstract partial class PreciseAbstractFlowPass<LocalState> : BoundTreeV
/// If we are tracking exceptions, then by convention the first entry in the pending branches
/// buffer contains a summary of the states that can arise from exceptions.
/// </summary>
private readonly bool _trackExceptions;
protected readonly bool _trackExceptions;
/// <summary>
/// Pending escapes generated in the current scope (or more deeply nested scopes). When jump
......@@ -905,7 +905,7 @@ public override BoundNode VisitAttribute(BoundAttribute node)
public override BoundNode VisitDynamicObjectCreationExpression(BoundDynamicObjectCreationExpression node)
{
VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, null);
VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, null, default(ImmutableArray<int>), false);
VisitRvalue(node.InitializerExpressionOpt);
if (_trackExceptions) NotePossibleException(node);
return null;
......@@ -914,7 +914,7 @@ public override BoundNode VisitDynamicObjectCreationExpression(BoundDynamicObjec
public override BoundNode VisitDynamicIndexerAccess(BoundDynamicIndexerAccess node)
{
VisitRvalue(node.ReceiverOpt);
VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, null);
VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, null, default(ImmutableArray<int>), false);
if (_trackExceptions) NotePossibleException(node);
return null;
}
......@@ -929,7 +929,7 @@ public override BoundNode VisitDynamicMemberAccess(BoundDynamicMemberAccess node
public override BoundNode VisitDynamicInvocation(BoundDynamicInvocation node)
{
VisitRvalue(node.Expression);
VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, null);
VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, null, default(ImmutableArray<int>), false);
if (_trackExceptions) NotePossibleException(node);
return null;
}
......@@ -961,7 +961,7 @@ public override BoundNode VisitArgList(BoundArgList node)
public override BoundNode VisitArgListOperator(BoundArgListOperator node)
{
// When we have M(__arglist(x, y, z)) we must visit x, y and z.
VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, null);
VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, null, default(ImmutableArray<int>), false);
return null;
}
......@@ -1055,7 +1055,7 @@ public override BoundNode VisitCall(BoundCall node)
}
VisitReceiverBeforeCall(node.ReceiverOpt, node.Method);
VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, node.Method);
VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, node.Method, node.ArgsToParamsOpt, node.Expanded);
UpdateStateForCall(node);
VisitReceiverAfterCall(node.ReceiverOpt, node.Method);
......@@ -1072,7 +1072,7 @@ protected virtual void UpdateStateForCall(BoundCall node)
if (_trackExceptions) NotePossibleException(node);
}
private void VisitReceiverBeforeCall(BoundExpression receiverOpt, MethodSymbol method)
protected virtual void VisitReceiverBeforeCall(BoundExpression receiverOpt, MethodSymbol method)
{
if ((object)method == null || method.MethodKind != MethodKind.Constructor)
{
......@@ -1085,7 +1085,7 @@ private void VisitReceiverAfterCall(BoundExpression receiverOpt, MethodSymbol me
NamedTypeSymbol containingType;
if (receiverOpt != null && ((object)method == null || method.MethodKind == MethodKind.Constructor || (object)(containingType = method.ContainingType) != null && !method.IsStatic && !containingType.IsReferenceType && !TypeIsImmutable(containingType)))
{
WriteArgument(receiverOpt, (object)method != null && method.MethodKind == MethodKind.Constructor ? RefKind.Out : RefKind.Ref, method);
WriteArgument(receiverOpt, (object)method != null && method.MethodKind == MethodKind.Constructor ? RefKind.Out : RefKind.Ref, method, parameter: null);
}
}
......@@ -1122,9 +1122,9 @@ private static bool TypeIsImmutable(TypeSymbol t)
public override BoundNode VisitIndexerAccess(BoundIndexerAccess node)
{
VisitRvalue(node.ReceiverOpt);
var method = node.Indexer.GetOwnOrInheritedGetMethod() ?? node.Indexer.SetMethod;
VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, method);
VisitReceiverBeforeCall(node.ReceiverOpt, method);
VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, method, node.ArgsToParamsOpt, node.Expanded);
if (_trackExceptions && (object)method != null) NotePossibleException(node);
if ((object)method != null) VisitReceiverAfterCall(node.ReceiverOpt, method);
return null;
......@@ -1138,13 +1138,13 @@ public override BoundNode VisitEventAssignmentOperator(BoundEventAssignmentOpera
return null;
}
private void VisitArguments(ImmutableArray<BoundExpression> arguments, ImmutableArray<RefKind> refKindsOpt, MethodSymbol method)
private void VisitArguments(ImmutableArray<BoundExpression> arguments, ImmutableArray<RefKind> refKindsOpt, MethodSymbol method, ImmutableArray<int> argsToParamsOpt, bool expanded)
{
if (refKindsOpt.IsDefault)
{
for (int i = 0; i < arguments.Length; i++)
{
VisitRvalue(arguments[i]);
VisitArgumentAsRvalue(arguments, i, method, argsToParamsOpt, expanded);
}
}
else
......@@ -1155,7 +1155,7 @@ private void VisitArguments(ImmutableArray<BoundExpression> arguments, Immutable
RefKind refKind = refKindsOpt.Length <= i ? RefKind.None : refKindsOpt[i];
if (refKind != RefKind.Out)
{
VisitRvalue(arguments[i]);
VisitArgumentAsRvalue(arguments, i, method, argsToParamsOpt, expanded);
}
else
{
......@@ -1168,13 +1168,80 @@ private void VisitArguments(ImmutableArray<BoundExpression> arguments, Immutable
RefKind refKind = refKindsOpt.Length <= i ? RefKind.None : refKindsOpt[i];
if (refKind != RefKind.None)
{
WriteArgument(arguments[i], refKind, method);
WriteArgument(arguments, i, refKind, method, argsToParamsOpt, expanded);
}
}
}
}
protected virtual void WriteArgument(BoundExpression arg, RefKind refKind, MethodSymbol method)
private void VisitArgumentAsRvalue(ImmutableArray<BoundExpression> arguments, int i, MethodSymbol method, ImmutableArray<int> argsToParamsOpt, bool expanded)
{
ParameterSymbol parameter = GetCorrespondingParameter(i, method, argsToParamsOpt, ref expanded);
VisitArgumentAsRvalue(arguments[i], parameter, expanded);
}
private static ParameterSymbol GetCorrespondingParameter(int argumentOrdinal, MethodSymbol method, ImmutableArray<int> argsToParamsOpt, ref bool expanded)
{
ParameterSymbol parameter;
if ((object)method != null)
{
if (argsToParamsOpt.IsDefault)
{
if (argumentOrdinal < method.ParameterCount)
{
parameter = method.Parameters[argumentOrdinal];
}
else if (expanded)
{
parameter = method.Parameters[method.ParameterCount - 1];
}
else
{
parameter = null;
}
}
else
{
int parameterOrdinal = argsToParamsOpt[argumentOrdinal];
if (parameterOrdinal < method.ParameterCount)
{
parameter = method.Parameters[parameterOrdinal];
}
else
{
parameter = null;
expanded = false;
}
}
Debug.Assert((object)parameter != null || !expanded);
if (expanded && (parameter.Ordinal < method.ParameterCount - 1 || !parameter.Type.IsSZArray()))
{
expanded = false;
}
}
else
{
parameter = null;
expanded = false;
}
return parameter;
}
protected virtual void VisitArgumentAsRvalue(BoundExpression argument, ParameterSymbol parameter, bool expanded)
{
VisitRvalue(argument);
}
private void WriteArgument(ImmutableArray<BoundExpression> arguments, int i, RefKind refKind, MethodSymbol method, ImmutableArray<int> argsToParamsOpt, bool expanded)
{
ParameterSymbol parameter = GetCorrespondingParameter(i, method, argsToParamsOpt, ref expanded);
WriteArgument(arguments[i], refKind, method, parameter);
}
protected virtual void WriteArgument(BoundExpression arg, RefKind refKind, MethodSymbol method, ParameterSymbol parameter)
{
}
......@@ -1203,16 +1270,6 @@ public override BoundNode VisitBadStatement(BoundBadStatement node)
return null;
}
public override BoundNode VisitArrayInitialization(BoundArrayInitialization node)
{
foreach (var child in node.Initializers)
{
VisitRvalue(child);
}
return null;
}
public override BoundNode VisitDelegateCreationExpression(BoundDelegateCreationExpression node)
{
var methodGroup = node.Argument as BoundMethodGroup;
......@@ -1395,9 +1452,14 @@ public override BoundNode VisitTryStatement(BoundTryStatement node)
public override BoundNode VisitReturnStatement(BoundReturnStatement node)
{
var result = VisitRvalue(node.ExpressionOpt);
AdjustStateAfterReturnStatement(node);
return result;
}
protected void AdjustStateAfterReturnStatement(BoundReturnStatement node)
{
_pendingBranches.Add(new PendingBranch(node, this.State));
SetUnreachable();
return result;
}
public override BoundNode VisitThisReference(BoundThisReference node)
......@@ -1428,7 +1490,7 @@ public override BoundNode VisitObjectCreationExpression(BoundObjectCreationExpre
{
if (_trackExceptions) NotePossibleException(node);
VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, node.Constructor);
VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, node.Constructor, node.ArgsToParamsOpt, node.Expanded);
VisitRvalue(node.InitializerExpressionOpt);
return null;
}
......@@ -1530,10 +1592,15 @@ private void VisitFieldAccessInternal(BoundExpression receiverOpt, FieldSymbol f
}
else
{
VisitRvalue(receiverOpt);
VisitFieldReceiverAsRvalue(receiverOpt, fieldSymbol);
}
}
protected virtual void VisitFieldReceiverAsRvalue(BoundExpression receiverOpt, FieldSymbol fieldSymbol)
{
VisitRvalue(receiverOpt);
}
public override BoundNode VisitFieldInfo(BoundFieldInfo node)
{
return null;
......@@ -1551,6 +1618,8 @@ protected static bool MayRequireTracking(BoundExpression receiverOpt, FieldSymbo
receiverOpt != null &&
!fieldSymbol.IsStatic &&
!fieldSymbol.IsFixed &&
receiverOpt.Kind != BoundKind.TypeExpression &&
receiverOpt.Type != null &&
receiverOpt.Type.IsStructType() &&
!receiverOpt.Type.IsPrimitiveRecursiveStruct();
}
......@@ -1732,7 +1801,7 @@ public override BoundNode VisitSwitchSection(BoundSwitchSection node)
public override BoundNode VisitArrayAccess(BoundArrayAccess node)
{
VisitRvalue(node.Expression);
VisitArrayAccessTargetAsRvalue(node);
foreach (var i in node.Indices)
{
VisitRvalue(i);
......@@ -1742,6 +1811,11 @@ public override BoundNode VisitArrayAccess(BoundArrayAccess node)
return null;
}
protected virtual void VisitArrayAccessTargetAsRvalue(BoundArrayAccess node)
{
VisitRvalue(node.Expression);
}
public override BoundNode VisitBinaryOperator(BoundBinaryOperator node)
{
if (node.OperatorKind.IsLogical())
......@@ -1907,25 +1981,39 @@ private void VisitBinaryOperatorChildren(BoundBinaryOperator node)
while (true)
{
binary = stack.Pop();
VisitRvalue(binary.Right);
if (_trackExceptions && binary.HasExpressionSymbols())
{
NotePossibleException(binary);
}
AfterLeftChildHasBeenVisited(binary);
if (stack.Count == 0)
{
break;
}
Unsplit(); // VisitRvalue does this
AfterNestedBinaryOperatorHasBeenVisited(binary);
}
Debug.Assert((object)binary == node);
stack.Free();
}
protected virtual void AfterLeftChildHasBeenVisited(BoundBinaryOperator binary)
{
VisitRvalue(binary.Right);
AfterBinaryOperatorChildrenHaveBeenVisited(binary);
}
protected void AfterBinaryOperatorChildrenHaveBeenVisited(BoundBinaryOperator binary)
{
if (_trackExceptions && binary.HasExpressionSymbols())
{
NotePossibleException(binary);
}
}
protected virtual void AfterNestedBinaryOperatorHasBeenVisited(BoundBinaryOperator binary)
{
Unsplit(); // VisitRvalue does this
}
public override BoundNode VisitUnaryOperator(BoundUnaryOperator node)
{
if (node.OperatorKind == UnaryOperatorKind.BoolLogicalNegation)
......@@ -1982,14 +2070,44 @@ public override BoundNode VisitArrayCreation(BoundArrayCreation node)
if (node.InitializerOpt != null && !node.InitializerOpt.Initializers.IsDefault)
{
foreach (var element in node.InitializerOpt.Initializers)
VisitRvalue(element);
VisitArrayInitializationInternal(node, node.InitializerOpt);
}
if (_trackExceptions) NotePossibleException(node);
return null;
}
private void VisitArrayInitializationInternal(BoundArrayCreation arrayCreation, BoundArrayInitialization node)
{
foreach (var child in node.Initializers)
{
if (child.Kind == BoundKind.ArrayInitialization)
{
VisitArrayInitializationInternal(arrayCreation, (BoundArrayInitialization)child);
}
else
{
VisitArrayElementInitializer(arrayCreation, child);
}
}
}
protected virtual BoundNode VisitArrayElementInitializer(BoundArrayCreation arrayCreation, BoundExpression child)
{
return VisitRvalue(child);
}
// Can be called as part of a bad expression.
public sealed override BoundNode VisitArrayInitialization(BoundArrayInitialization node)
{
foreach (var child in node.Initializers)
{
VisitRvalue(child);
}
return null;
}
public override BoundNode VisitForStatement(BoundForStatement node)
{
if (node.Initializer != null)
......@@ -2436,7 +2554,7 @@ protected void VisitAddressOfOperator(BoundAddressOfOperator node, bool shouldRe
this.VisitLvalue(operand);
}
this.WriteArgument(operand, RefKind.Out, null); //Out because we know it will definitely be assigned.
this.WriteArgument(operand, RefKind.Out, null, null); //Out because we know it will definitely be assigned.
}
public override BoundNode VisitPointerIndirectionOperator(BoundPointerIndirectionOperator node)
......@@ -2466,7 +2584,12 @@ public override BoundNode VisitStackAllocArrayCreation(BoundStackAllocArrayCreat
public override BoundNode VisitAnonymousObjectCreationExpression(BoundAnonymousObjectCreationExpression node)
{
// visit arguments as r-values
VisitArguments(node.Arguments, default(ImmutableArray<RefKind>), node.Constructor);
var arguments = node.Arguments;
var constructor = node.Constructor;
for (int i = 0; i < arguments.Length; i++)
{
VisitArgumentAsRvalue(arguments[i], constructor.Parameters[i], expanded: false);
}
// ignore declarations
//node.Declarations
......@@ -2510,6 +2633,7 @@ private BoundNode VisitObjectOrCollectionInitializerExpression(ImmutableArray<Bo
public override BoundNode VisitObjectInitializerMember(BoundObjectInitializerMember node)
{
// TODO: StaticNullChecking - Should we use VisitArguments to properly handle arguments?
var arguments = node.Arguments;
if (!arguments.IsDefaultOrEmpty)
{
......@@ -2538,13 +2662,13 @@ public override BoundNode VisitCollectionElementInitializer(BoundCollectionEleme
LocalState savedState = savedState = this.State.Clone();
SetUnreachable();
VisitArguments(node.Arguments, default(ImmutableArray<RefKind>), node.AddMethod);
VisitArguments(node.Arguments, default(ImmutableArray<RefKind>), node.AddMethod, node.ArgsToParamsOpt, node.Expanded);
this.State = savedState;
}
else
{
VisitArguments(node.Arguments, default(ImmutableArray<RefKind>), node.AddMethod);
VisitArguments(node.Arguments, default(ImmutableArray<RefKind>), node.AddMethod, node.ArgsToParamsOpt, node.Expanded);
}
if (_trackExceptions) NotePossibleException(node);
......@@ -2553,7 +2677,7 @@ public override BoundNode VisitCollectionElementInitializer(BoundCollectionEleme
public override BoundNode VisitDynamicCollectionElementInitializer(BoundDynamicCollectionElementInitializer node)
{
VisitArguments(node.Arguments, default(ImmutableArray<RefKind>), method: null);
VisitArguments(node.Arguments, default(ImmutableArray<RefKind>), method: null, argsToParamsOpt: default(ImmutableArray<int>), expanded: false);
if (_trackExceptions) NotePossibleException(node);
return null;
}
......
......@@ -172,7 +172,7 @@ private void NoteReceiverReadOrWritten(BoundFieldAccess expr, HashSet<Symbol> re
}
}
protected override void AssignImpl(BoundNode node, BoundExpression value, RefKind refKind, bool written, bool read)
protected override void AssignImpl(BoundNode node, BoundExpression value, bool? valueIsNotNull, RefKind refKind, bool written, bool read)
{
switch (node.Kind)
{
......@@ -182,7 +182,7 @@ protected override void AssignImpl(BoundNode node, BoundExpression value, RefKin
case BoundKind.QueryClause:
{
base.AssignImpl(node, value, refKind, written, read);
base.AssignImpl(node, value, valueIsNotNull, refKind, written, read);
var symbol = ((BoundQueryClause)node).DefinedSymbol;
if ((object)symbol != null)
{
......@@ -193,7 +193,7 @@ protected override void AssignImpl(BoundNode node, BoundExpression value, RefKin
case BoundKind.FieldAccess:
{
base.AssignImpl(node, value, refKind, written, read);
base.AssignImpl(node, value, valueIsNotNull, refKind, written, read);
var fieldAccess = node as BoundFieldAccess;
if (!IsInside && node.Syntax != null && node.Syntax.Span.Contains(RegionSpan))
{
......@@ -203,7 +203,7 @@ protected override void AssignImpl(BoundNode node, BoundExpression value, RefKin
break;
default:
base.AssignImpl(node, value, refKind, written, read);
base.AssignImpl(node, value, valueIsNotNull, refKind, written, read);
break;
}
}
......@@ -219,7 +219,7 @@ public override void VisitForEachIterationVariable(BoundForEachStatement node)
if ((object)local != null)
{
GetOrCreateSlot(local);
Assign(node, value: null);
Assign(node, value: null, valueIsNotNull: null);
}
}
......@@ -256,7 +256,7 @@ private ParameterSymbol GetRangeVariableUnderlyingParameter(BoundNode underlying
public override BoundNode VisitQueryClause(BoundQueryClause node)
{
Assign(node, value: null);
Assign(node, value: null, valueIsNotNull: null);
return base.VisitQueryClause(node);
}
}
......
......@@ -65,7 +65,7 @@ protected override void ReportUnassignedOutParameter(ParameterSymbol parameter,
protected override void ReportUnassigned(FieldSymbol fieldSymbol, int unassignedSlot, CSharpSyntaxNode node)
{
Symbol variable = GetNonFieldSymbol(unassignedSlot);
Symbol variable = GetNonMemberSymbol(unassignedSlot);
if ((object)variable != null) _result.Add(variable);
base.ReportUnassigned(fieldSymbol, unassignedSlot, node);
}
......
......@@ -234,7 +234,7 @@ protected override LocalState UnreachableState()
protected override void ReportUnassigned(FieldSymbol fieldSymbol, int unassignedSlot, CSharpSyntaxNode node)
{
CaptureVariable(GetNonFieldSymbol(unassignedSlot), node);
CaptureVariable(GetNonMemberSymbol(unassignedSlot), node);
}
protected override void VisitLvalueParameter(BoundParameter node)
......
......@@ -589,8 +589,8 @@ public BoundCall Call(BoundExpression receiver, MethodSymbol method, ImmutableAr
Debug.Assert(method.ParameterCount == args.Length);
return new BoundCall(
Syntax, receiver, method, args,
ImmutableArray<String>.Empty, ImmutableArray<RefKind>.Empty, false, false, false,
ImmutableArray<int>.Empty, LookupResultKind.Viable, method.ReturnType.TypeSymbol)
default(ImmutableArray<String>), default(ImmutableArray<RefKind>), false, false, false,
default(ImmutableArray<int>), LookupResultKind.Viable, method.ReturnType.TypeSymbol)
{ WasCompilerGenerated = true };
}
......
......@@ -33,7 +33,7 @@ internal AnonymousTypeConstructorSymbol(NamedTypeSymbol container, ImmutableArra
for (int index = 0; index < fieldsCount; index++)
{
PropertySymbol property = properties[index];
paramsArr[index] = new SynthesizedParameterSymbol(this, property.Type.TypeSymbol, index, RefKind.None, property.Name);
paramsArr[index] = new SynthesizedParameterSymbol(this, property.Type, index, RefKind.None, property.Name);
}
_parameters = paramsArr.AsImmutableOrNull();
}
......
......@@ -442,8 +442,8 @@ private static bool HaveSameReturnTypes(Symbol member1, TypeMap typeMap1, Symbol
TypeSymbolWithAnnotations unsubstitutedReturnType2 = member2.GetTypeOrReturnType();
// short-circuit type map building in the easiest cases
var isVoid1 = unsubstitutedReturnType1.SpecialType == SpecialType.System_Void;
var isVoid2 = unsubstitutedReturnType2.SpecialType == SpecialType.System_Void;
var isVoid1 = unsubstitutedReturnType1.IsVoid;
var isVoid2 = unsubstitutedReturnType2.IsVoid;
if (isVoid1 != isVoid2)
{
......
......@@ -124,10 +124,12 @@ private void ComputeParameters()
SyntaxToken arglistToken;
_parameters = ParameterHelpers.MakeParameters(_binder, this, _syntax.ParameterList, true, out arglistToken, diagnostics, true);
_isVararg = (arglistToken.Kind() == SyntaxKind.ArgListKeyword);
if (diagnostics.IsEmptyWithoutResolution)
if (!diagnostics.HasAnyResolvedErrors())
{
SourceMemberMethodSymbol.ReportAsyncParameterErrors(this, diagnostics, this.Locations[0]);
}
AddDiagnostics(diagnostics.ToReadOnlyAndFree());
}
......
......@@ -105,7 +105,6 @@ internal static class ParameterHelpers
int firstDefault,
DiagnosticBag diagnostics)
{
TypeSymbol parameterType = parameter.Type.TypeSymbol;
int parameterIndex = parameter.Ordinal;
bool isDefault = parameterSyntax.Default != null;
SyntaxToken thisKeyword = parameterSyntax.Modifiers.FirstOrDefault(SyntaxKind.ThisKeyword);
......@@ -123,7 +122,7 @@ internal static class ParameterHelpers
// error CS1670: params is not valid in this context
diagnostics.Add(ErrorCode.ERR_IllegalParams, parameterSyntax.Modifiers.First(t => t.Kind() == SyntaxKind.ParamsKeyword).GetLocation());
}
else if (parameter.IsParams && !parameterType.IsSZArray())
else if (parameter.IsParams && !parameter.Type.IsSZArray())
{
// error CS0225: The params parameter must be a single dimensional array
diagnostics.Add(ErrorCode.ERR_ParamsMustBeArray, parameterSyntax.Modifiers.First(t => t.Kind() == SyntaxKind.ParamsKeyword).GetLocation());
......
......@@ -661,7 +661,7 @@ internal ImmutableHashSet<SourceFieldSymbolWithSyntaxReference> GetConstantValue
(value != null) &&
!value.IsBad &&
(value != Microsoft.CodeAnalysis.ConstantValue.Unset) &&
diagnostics.IsEmptyWithoutResolution)
!diagnostics.HasAnyResolvedErrors())
{
this.SetLazyConstantValue(
value,
......
......@@ -204,7 +204,8 @@ private TypeSymbolWithAnnotations GetTypeSymbol()
if ((object)inferredType != null &&
inferredType.SpecialType != SpecialType.System_Void)
{
declType = TypeSymbolWithAnnotations.Create(inferredType);
// Default inferred reference types to a nullable state.
declType = TypeSymbolWithAnnotations.Create(inferredType, makeNullableIfReferenceType: true);
}
else
{
......
......@@ -420,7 +420,7 @@ protected sealed override void LazyAsyncMethodChecks(CancellationToken cancellat
diagnostics.Add(ErrorCode.ERR_SynchronizedAsyncMethod, errorLocation);
}
if (diagnostics.IsEmptyWithoutResolution)
if (!diagnostics.HasAnyResolvedErrors())
{
ReportAsyncParameterErrors(this, diagnostics, errorLocation);
}
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Roslyn.Utilities;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Threading;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
......@@ -101,6 +103,10 @@ public static NamespaceOrTypeOrAliasSymbolWithAnnotations Create(Symbol symbol)
return TypeSymbolWithAnnotations.Create((TypeSymbol)symbol);
}
}
public abstract bool IsAlias { get; }
public abstract bool IsNamespace { get; }
public abstract bool IsType { get; }
}
internal sealed class AliasSymbolWithAnnotations : NamespaceOrTypeOrAliasSymbolWithAnnotations
......@@ -115,6 +121,10 @@ public AliasSymbolWithAnnotations(AliasSymbol aliasSymbol)
public sealed override Symbol Symbol => _aliasSymbol;
public AliasSymbol AliasSymbol => _aliasSymbol;
public override bool IsAlias => true;
public override bool IsNamespace => false;
public override bool IsType => false;
}
internal abstract class NamespaceOrTypeSymbolWithAnnotations : NamespaceOrTypeOrAliasSymbolWithAnnotations
......@@ -131,6 +141,8 @@ public static NamespaceOrTypeSymbolWithAnnotations Create(NamespaceOrTypeSymbol
return TypeSymbolWithAnnotations.Create((TypeSymbol)symbol);
}
}
public override bool IsAlias => false;
}
internal sealed class NamespaceSymbolWithAnnotations : NamespaceOrTypeSymbolWithAnnotations
......@@ -146,6 +158,9 @@ public NamespaceSymbolWithAnnotations(NamespaceSymbol namespaceSymbol)
public sealed override Symbol Symbol => _namespaceSymbol;
public NamespaceSymbol NamespaceSymbol => _namespaceSymbol;
public sealed override NamespaceOrTypeSymbol NamespaceOrTypeSymbol => _namespaceSymbol;
public override bool IsNamespace => true;
public override bool IsType => false;
}
/// <summary>
......@@ -169,11 +184,34 @@ public static TypeSymbolWithAnnotations Create(TypeSymbol typeSymbol, ImmutableA
return new WithCustomModifiers(typeSymbol, customModifiers);
}
public static TypeSymbolWithAnnotations Create(TypeSymbol typeSymbol, bool makeNullableIfReferenceType)
{
if (!makeNullableIfReferenceType || !typeSymbol.IsReferenceType)
{
return Create(typeSymbol);
}
return new NullableReferenceTypeWithoutCustomModifiers(typeSymbol);
}
public TypeSymbolWithAnnotations AsNullableReferenceOrValueType(CSharpCompilation compilation, SyntaxReference nullableTypeSyntax)
{
return new LazyNullableType(compilation, nullableTypeSyntax, this);
}
/// <summary>
/// Adjust types in signatures coming from metadata.
/// </summary>
public abstract TypeSymbolWithAnnotations AsNullableReferenceType();
public abstract TypeSymbolWithAnnotations WithModifiers(ImmutableArray<CustomModifier> customModifiers);
public sealed override Symbol Symbol => TypeSymbol;
public sealed override NamespaceOrTypeSymbol NamespaceOrTypeSymbol => TypeSymbol;
public override bool IsNamespace => false;
public override bool IsType => true;
public abstract TypeSymbol TypeSymbol { get; }
/// <summary>
......@@ -206,8 +244,10 @@ public static TypeSymbolWithAnnotations Create(TypeSymbol typeSymbol, ImmutableA
public bool IsRestrictedType() => TypeSymbol.IsRestrictedType();
public bool IsPointerType() => TypeSymbol.IsPointerType();
public bool IsUnsafe() => TypeSymbol.IsUnsafe();
public bool IsStatic => TypeSymbol.IsStatic;
public virtual bool IsStatic => TypeSymbol.IsStatic;
public bool IsNullableTypeOrTypeParameter() => TypeSymbol.IsNullableTypeOrTypeParameter();
public virtual bool IsVoid => TypeSymbol.SpecialType == SpecialType.System_Void;
public virtual bool IsSZArray() => TypeSymbol.IsSZArray();
public bool GetUnificationUseSiteDiagnosticRecursive(ref DiagnosticInfo result, Symbol owner, ref HashSet<TypeSymbol> checkedTypes)
......@@ -226,7 +266,7 @@ public virtual bool IsAtLeastAsVisibleAs(Symbol sym, ref HashSet<DiagnosticInfo>
return TypeSymbol.IsAtLeastAsVisibleAs(sym, ref useSiteDiagnostics);
}
public TypeSymbolWithAnnotations SubstituteType(AbstractTypeMap typeMap)
public virtual TypeSymbolWithAnnotations SubstituteType(AbstractTypeMap typeMap)
{
var newCustomModifiers = typeMap.SubstituteCustomModifiers(this.CustomModifiers);
var newTypeWithModifiers = typeMap.SubstituteType(this.TypeSymbol);
......@@ -240,6 +280,11 @@ public TypeSymbolWithAnnotations SubstituteType(AbstractTypeMap typeMap)
}
}
public virtual void ReportDiagnosticsIfObsolete(Binder binder, SyntaxNode syntax, DiagnosticBag diagnostics)
{
binder.ReportDiagnosticsIfObsolete(diagnostics, TypeSymbol, syntax, hasBaseReceiver: false);
}
/// <summary>
/// Extract type under assumption that there should be no custom modifiers or annotations.
/// The method asserts otherwise.
......@@ -261,10 +306,7 @@ public TypeSymbolWithAnnotations Update(TypeSymbol typeSymbol, ImmutableArray<Cu
return this;
}
protected virtual TypeSymbolWithAnnotations DoUpdate(TypeSymbol typeSymbol, ImmutableArray<CustomModifier> customModifiers)
{
return TypeSymbolWithAnnotations.Create(typeSymbol, customModifiers);
}
protected abstract TypeSymbolWithAnnotations DoUpdate(TypeSymbol typeSymbol, ImmutableArray<CustomModifier> customModifiers);
private class WithoutCustomModifiers : TypeSymbolWithAnnotations
{
......@@ -290,14 +332,245 @@ public override TypeSymbolWithAnnotations WithModifiers(ImmutableArray<CustomMod
return new WithCustomModifiers(_typeSymbol, customModifiers);
}
public override TypeSymbol AsTypeSymbolOnly() => _typeSymbol;
public override bool Is(TypeSymbol other) => _typeSymbol == other;
protected sealed override TypeSymbolWithAnnotations DoUpdate(TypeSymbol typeSymbol, ImmutableArray<CustomModifier> customModifiers)
{
return TypeSymbolWithAnnotations.Create(typeSymbol, customModifiers);
}
public override TypeSymbolWithAnnotations AsNullableReferenceType()
{
if (_typeSymbol.IsNullableType())
{
return this;
}
return new NullableReferenceTypeWithoutCustomModifiers(_typeSymbol);
}
}
private class NullableReferenceTypeWithoutCustomModifiers : TypeSymbolWithAnnotations
{
protected readonly TypeSymbol _typeSymbol;
public NullableReferenceTypeWithoutCustomModifiers(TypeSymbol typeSymbol)
{
Debug.Assert((object)typeSymbol != null);
Debug.Assert(!typeSymbol.IsNullableType());
_typeSymbol = typeSymbol;
}
public sealed override TypeSymbol TypeSymbol => _typeSymbol;
public sealed override bool IsNullable => true;
public override ImmutableArray<CustomModifier> CustomModifiers => ImmutableArray<CustomModifier>.Empty;
public override TypeSymbol AsTypeSymbolOnly() => _typeSymbol;
public sealed override bool Is(TypeSymbol other) => false; // It has nullable annotation.
public override TypeSymbolWithAnnotations WithModifiers(ImmutableArray<CustomModifier> customModifiers)
{
if (customModifiers.IsDefaultOrEmpty)
{
return this;
}
return new NullableReferenceTypeWithCustomModifiers(_typeSymbol, customModifiers);
}
protected sealed override TypeSymbolWithAnnotations DoUpdate(TypeSymbol typeSymbol, ImmutableArray<CustomModifier> customModifiers)
{
if (customModifiers.IsDefaultOrEmpty)
{
return new NullableReferenceTypeWithoutCustomModifiers(typeSymbol);
}
return new NullableReferenceTypeWithCustomModifiers(typeSymbol, customModifiers);
}
public sealed override TypeSymbolWithAnnotations AsNullableReferenceType()
{
return this;
}
}
private class LazyNullableType : TypeSymbolWithAnnotations
{
private readonly CSharpCompilation _compilation;
private readonly SyntaxReference _nullableTypeSyntax;
private readonly TypeSymbolWithAnnotations _underlying;
private TypeSymbol _resolved;
public LazyNullableType(CSharpCompilation compilation, SyntaxReference nullableTypeSyntax, TypeSymbolWithAnnotations underlying)
{
Debug.Assert(!underlying.IsNullable);
_compilation = compilation;
_nullableTypeSyntax = nullableTypeSyntax;
_underlying = underlying;
}
public override bool IsNullable => true;
public override bool IsVoid
{
get
{
if ((object)_resolved != null)
{
return _resolved.SpecialType == SpecialType.System_Void;
}
if (_underlying.SpecialType != SpecialType.System_Void)
{
return false;
}
// It should be fine to force resolution if underlying type is void.
return TypeSymbol.SpecialType == SpecialType.System_Void;
}
}
public override bool IsSZArray()
{
if ((object)_resolved != null)
{
return _resolved.IsSZArray();
}
if (!_underlying.IsSZArray())
{
return false;
}
// It should be fine to force resolution if underlying type is array.
return TypeSymbol.IsSZArray();
}
public override bool IsStatic
{
get
{
if ((object)_resolved != null)
{
return _resolved.IsStatic;
}
if (!_underlying.IsStatic)
{
// It should be safe to assume that Nullable<T> is not static.
return false;
}
// It should be fine to force resolution if underlying type is static.
return TypeSymbol.IsStatic;
}
}
public override TypeSymbol TypeSymbol
{
get
{
if ((object)_resolved == null)
{
if (_underlying.IsReferenceType && ((CSharpParseOptions)_nullableTypeSyntax.SyntaxTree.Options).IsFeatureEnabled(MessageID.IDS_FeatureStaticNullChecking))
{
_resolved = _underlying.TypeSymbol;
}
else
{
Interlocked.CompareExchange(ref _resolved,
_compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(
ImmutableArray.Create(_underlying)),
null);
}
}
return _resolved;
}
}
public override TypeSymbol AsTypeSymbolOnly()
{
return _typeSymbol;
return TypeSymbol;
}
public override bool Is(TypeSymbol other)
{
return _typeSymbol == other;
return TypeSymbol == other;
}
public override ImmutableArray<CustomModifier> CustomModifiers => _underlying.CustomModifiers;
public override TypeSymbolWithAnnotations WithModifiers(ImmutableArray<CustomModifier> customModifiers)
{
if (customModifiers.IsDefaultOrEmpty)
{
return this;
}
// Optimize for resolved case
if ((object)_resolved != null)
{
if (_resolved.IsNullableType())
{
return TypeSymbolWithAnnotations.Create(_resolved, customModifiers);
}
return new NullableReferenceTypeWithCustomModifiers(_resolved, customModifiers);
}
return new LazyNullableType(_compilation, _nullableTypeSyntax, _underlying.WithModifiers(customModifiers));
}
protected override TypeSymbolWithAnnotations DoUpdate(TypeSymbol typeSymbol, ImmutableArray<CustomModifier> customModifiers)
{
if (typeSymbol.IsNullableType())
{
return TypeSymbolWithAnnotations.Create(typeSymbol, customModifiers);
}
if (customModifiers.IsDefaultOrEmpty)
{
return new NullableReferenceTypeWithoutCustomModifiers(typeSymbol);
}
return new NullableReferenceTypeWithCustomModifiers(typeSymbol, customModifiers);
}
public override TypeSymbolWithAnnotations AsNullableReferenceType()
{
return this;
}
public override TypeSymbolWithAnnotations SubstituteType(AbstractTypeMap typeMap)
{
if ((object)_resolved != null)
{
return base.SubstituteType(typeMap);
}
var newUnderlying = typeMap.SubstituteType(this._underlying);
if ((object)newUnderlying != this._underlying)
{
return new LazyNullableType(_compilation, _nullableTypeSyntax, newUnderlying);
}
else
{
return this; // substitution had no effect on the type or modifiers
}
}
public override void ReportDiagnosticsIfObsolete(Binder binder, SyntaxNode syntax, DiagnosticBag diagnostics)
{
if ((object)_resolved != null)
{
base.ReportDiagnosticsIfObsolete(binder, syntax, diagnostics);
}
else
{
diagnostics.Add(new LazyObsoleteDiagnosticInfo(this, binder.ContainingMemberOrLambda, binder.Flags), syntax.GetLocation());
}
}
}
......@@ -334,6 +607,46 @@ public override bool Is(TypeSymbol other)
{
return false; // have custom modifiers
}
public override TypeSymbolWithAnnotations AsNullableReferenceType()
{
if (_typeSymbol.IsNullableType())
{
return this;
}
return new NullableReferenceTypeWithCustomModifiers(_typeSymbol, _customModifiers);
}
}
private class NullableReferenceTypeWithCustomModifiers : NullableReferenceTypeWithoutCustomModifiers
{
private readonly ImmutableArray<CustomModifier> _customModifiers;
public NullableReferenceTypeWithCustomModifiers(TypeSymbol typeSymbol, ImmutableArray<CustomModifier> customModifiers)
: base(typeSymbol)
{
Debug.Assert(!customModifiers.IsDefaultOrEmpty);
_customModifiers = customModifiers;
}
public override TypeSymbolWithAnnotations WithModifiers(ImmutableArray<CustomModifier> customModifiers)
{
if (customModifiers.IsDefaultOrEmpty)
{
return this;
}
return new NullableReferenceTypeWithCustomModifiers(_typeSymbol, _customModifiers.Concat(customModifiers));
}
public override ImmutableArray<CustomModifier> CustomModifiers => _customModifiers;
public override TypeSymbol AsTypeSymbolOnly()
{
Debug.Assert(this.CustomModifiers.IsEmpty);
return _typeSymbol;
}
}
}
}
......@@ -29,13 +29,23 @@ internal class SynthesizedParameterSymbol : ParameterSymbol
string name = "",
ImmutableArray<CustomModifier> customModifiers = default(ImmutableArray<CustomModifier>),
ushort countOfCustomModifiersPrecedingByRef = 0)
: this(container, TypeSymbolWithAnnotations.Create(type, customModifiers.NullToEmpty()), ordinal, refKind, name, countOfCustomModifiersPrecedingByRef)
{}
public SynthesizedParameterSymbol(
MethodSymbol container,
TypeSymbolWithAnnotations type,
int ordinal,
RefKind refKind,
string name = "",
ushort countOfCustomModifiersPrecedingByRef = 0)
{
Debug.Assert((object)type != null);
Debug.Assert(name != null);
Debug.Assert(ordinal >= 0);
_container = container;
_type = TypeSymbolWithAnnotations.Create(type, customModifiers.NullToEmpty());
_type = type;
_ordinal = ordinal;
_refKind = refKind;
_name = name;
......
......@@ -121,6 +121,7 @@
<Compile Include="Semantics\ScriptSemanticsTests.cs" />
<Compile Include="Semantics\SemanticAnalyzerTests.cs" />
<Compile Include="Semantics\SemanticErrorTests.cs" />
<Compile Include="Semantics\StaticNullChecking.cs" />
<Compile Include="Semantics\StructsTests.cs" />
<Compile Include="Semantics\SuppressAccessibilityChecksTests.cs" />
<Compile Include="Semantics\SwitchTests.cs" />
......
......@@ -214,6 +214,13 @@ public void WarningLevel_2()
case ErrorCode.WRN_UnableToLoadAnalyzer:
case ErrorCode.WRN_ReferencedAssemblyDoesNotHaveStrongName:
case ErrorCode.WRN_AlignmentMagnitude:
case ErrorCode.WRN_NullReferenceAssignment:
case ErrorCode.WRN_NullReferenceReceiver:
case ErrorCode.WRN_NullReferenceReturn:
case ErrorCode.WRN_NullReferenceArgument:
case ErrorCode.WRN_NullCheckIsProbablyAlwaysTrue:
case ErrorCode.WRN_NullCheckIsProbablyAlwaysFalse:
case ErrorCode.WRN_ExpressionIsProbablyNeverNull:
Assert.Equal(1, ErrorFacts.GetWarningLevel(errorCode));
break;
default:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册