diff --git a/azure-pipelines-integration.yml b/azure-pipelines-integration.yml index b705079cced43..b8f44f46690e6 100644 --- a/azure-pipelines-integration.yml +++ b/azure-pipelines-integration.yml @@ -1,3 +1,13 @@ +# Branches that trigger a build on commit +trigger: +- master +- master-vs-deps + +# Branches that trigger builds on PR +pr: +- master +- master-vs-deps + jobs: - job: Windows_VisualStudio_Integration_Tests pool: dotnet-external-temp-vs2017 diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 3c3c64a413399..2ee4175e92079 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,3 +1,20 @@ +# Branches that trigger a build on commit +trigger: +- master +- master-vs-deps + +# Branches that trigger builds on PR +pr: +- master +- master-vs-deps +- features/NullableReferenceTypes +- features/recursive-patterns +- features/enhanced-using +- features/nullabledogfood +- features/unmanaged-constructed-types +- features/editorconfig-in-compiler +- features/NegatedConditionStatements + jobs: - job: Windows_Desktop_Unit_Tests pool: dotnet-external-temp diff --git a/docs/contributing/Building, Debugging, and Testing on Windows.md b/docs/contributing/Building, Debugging, and Testing on Windows.md index cf84fb0987c98..29fc3dfa76202 100644 --- a/docs/contributing/Building, Debugging, and Testing on Windows.md +++ b/docs/contributing/Building, Debugging, and Testing on Windows.md @@ -5,15 +5,15 @@ Using the command line Roslyn can be developed using the following pattern: 1. Clone https://github.com/dotnet/roslyn -1. Run Restore.cmd +1. Run Restore.cmd 1. Run Build.cmd 1. Run Test.cmd ## Recommended version of .NET Framework -The minimal required version of .NET Framework is 4.6, however 4.7.2 is recommended for best developer experience. +The minimal required version of .NET Framework is 4.6, however 4.7.2 is recommended for best developer experience. -The projects in this repository are configured to build with Portable PDBs, which are supported in stack traces starting with .NET Framework 4.7.2. +The projects in this repository are configured to build with Portable PDBs, which are supported in stack traces starting with .NET Framework 4.7.2. If a stack trace is displayed on .NET Framework older than 4.7.2 (e.g. by xUnit when a test fails) it won't contain source and line information. .NET Framework 4.7.2 is included in [Windows 10 April 2018 Update](https://blogs.windows.com/windowsexperience/2018/04/30/how-to-get-the-windows-10-april-2018-update/). It can also be installed from the [Microsoft Download Center](https://www.microsoft.com/net/download/dotnet-framework-runtime). @@ -33,7 +33,7 @@ do the following: - Run the Visual Studio Installer from your start menu. You can just search for "Visual Studio Installer". If you can't find it, it's typically located at "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vs_installer.exe" - The Visual Studio installation will be listed under the Installed section -- Click on the menu icon (three horizontal lines), click Modify +- Click on the menu icon (three horizontal lines), click Modify - Choose the workloads listed above and click Modify ## Running Tests @@ -42,13 +42,13 @@ There are a number of options for running the core Roslyn unit tests: ### Command Line -The Test.cmd script will run our unit test on already built binaries. It can be passed the -build arguments to force a new build before running tests. +The Test.cmd script will run our unit test on already built binaries. It can be passed the -build arguments to force a new build before running tests. 1. Run the "Developer Command Prompt for VS2017" from your start menu. 2. Navigate to the directory of your Git clone. 3. Run `msbuild /v:m /m /nodereuse:false BuildAndTest.proj` in the command prompt. -### Test Explorer +### Test Explorer Tests can be run and debugged from the Test Explorer window. For best performance, we recommend the following: @@ -71,20 +71,19 @@ give it a try. ## Trying Your Changes in Visual Studio The Rosyln solution is designed to support easy debugging via F5. Several of our -projects produce VSIX which deploy into Visual Studio during build. The F5 operation +projects produce VSIX which deploy into Visual Studio during build. The F5 operation will start a new Visual Studio instance using those VSIX which override our installed binaries. This means trying out a change to the language, IDE or debugger is as simple as hitting F5. -The startup project needs to be set to VisualStudioSetup. This should be +The startup project needs to be set to `RoslynDeployment`. This should be the default but in same cases will need to be set explicitly. Here are what is deployed with each extension, by project that builds it. If you're working on a particular area, you probably want to set the appropriate -project as your startup project to ensure the right things are built and -deployed. +project as your startup project to optimize building and deploying only the relevant bits. -- **VisualStudioSetup**: this project can be found inside the VisualStudio folder +- **Roslyn.VisualStudio.Setup**: this project can be found inside the VisualStudio folder from the Solution Explorer, and builds Roslyn.VisualStudio.Setup.vsix. It contains the core language services that provide C# and VB editing. It also contains the copy of the compiler that is used to drive IntelliSense and @@ -93,7 +92,10 @@ deployed. compiler used to actually produce your final .exe or .dll when you do a build. If you're working on fixing an IDE bug, this is the project you want to use. -- **CompilerExtension**: this project can be found inside the Compilers folder +- **Roslyn.VisualStudio.InteractiveComponents**: this project can be found in the + Interactive\Setup folder from the Solution Explorer, and builds + Roslyn.VisualStudio.InteractiveComponents.vsix. +- **Roslyn.Compilers.Extension**: this project can be found inside the Compilers\Packages folder from the Solution Explorer, and builds Roslyn.Compilers.Extension.vsix. This deploys a copy of the command line compilers that are used to do actual builds in the IDE. It only affects builds triggered from the Visual Studio diff --git a/docs/features/nullable-reference-types.md b/docs/features/nullable-reference-types.md index 6c50606297cba..1a5418a2cd5ef 100644 --- a/docs/features/nullable-reference-types.md +++ b/docs/features/nullable-reference-types.md @@ -6,10 +6,10 @@ Reference types may be nullable, non-nullable, or null-oblivious (abbreviated he Project level nullable context can be set by using "nullable" command line switch: -nullable[+|-] Specify nullable context option enable|disable. --nullable:{enable|disable|safeonly} Specify nullable context option enable|disable|safeonly. +-nullable:{enable|disable|safeonly|warnings|safeonlywarnings} Specify nullable context option enable|disable|safeonly|warnings|safeonlywarnings. Through msbuild the context could be set by supplying an argument for a "NullableContextOptions" parameter of Csc build task. -Accepted values are "enable", "disable", "safeonly", or null (for the default nullable context according to the compiler). +Accepted values are "enable", "disable", "safeonly", "warnings", "safeonlywarnings", or null (for the default nullable context according to the compiler). The Microsoft.CSharp.Core.targets passes value of msbuild property named "NullableContextOptions" for that parameter. ## Annotations diff --git a/docs/infrastructure/feature branches azure.md b/docs/infrastructure/feature branches azure.md new file mode 100644 index 0000000000000..e2352eaa23818 --- /dev/null +++ b/docs/infrastructure/feature branches azure.md @@ -0,0 +1,32 @@ +# Creating Feature Branches +This document describes the process for setting up CI on a feature branch of roslyn. + +## Push the branch +The first step is to create the branch seeded with the initial change on roslyn. This branch should have the name `features/`. For example: `features/mono` for working on mono work. + +Assuming the branch should start with the contents of `master` the branch can be created by doing the following: + +Note: these steps assume the remote `origin` points to the official [roslyn repository](https://github.com/dotnet/roslyn). + +``` cmd +> git fetch origin +> git checkout -B init origin/master +> git push origin init:features/mono +``` + +## Adding branch to Azure Pipelines +The following files need to be edited in order for GitHub to trigger Azure Pipelines Test runs on PRs: + +- [azure-pipelines.yml](https://github.com/dotnet/roslyn/blob/master/azure-pipelines.yml) +- [azure-pipelines-integration.yml](https://github.com/dotnet/roslyn/blob/master/azure-pipelines-integration.yml) + +Under the `pr` section in the file add your branch name. + +``` yaml +pr: +- master +- master-vs-deps +- ... +- features/mono +``` + diff --git a/docs/infrastructure/feature branches jenkins.md b/docs/infrastructure/feature branches jenkins.md deleted file mode 100644 index 6d92f9419bc2a..0000000000000 --- a/docs/infrastructure/feature branches jenkins.md +++ /dev/null @@ -1,50 +0,0 @@ -# Jenkins in Feature Branches -This document describes the process for setting up CI on a feature branch of roslyn. - -## Push the branch -The first step is to create the branch seeded with the initial change on roslyn. This branch should have the name `features/`. For example: `features/mono` for working on mono work. - -Assuming the branch should start with the contents of `master` the branch can be created by doing the following: - -Note: these steps assume the remote `origin` points to the official [roslyn repository](https://github.com/dotnet/roslyn). - -``` cmd -> git fetch origin -> git checkout -B init origin/master -> git push origin init:features/mono -``` - -## Adding branch to Jenkins -Our Jenkins server manages branches on an opt-in bases. The set of branches that it monitors is kept in the [repolist.txt](https://github.com/dotnet/dotnet-ci/blob/master/data/repolist.txt) file in the [dotnet-ci](https://github.com/dotnet/dotnet-ci) repositiory. To add a branch do the following: - -- Check out the repolist.txt file on your local machine -- Add a line for your branch: `dotnet/roslyn branch=features/mono server=dotnet-ci` -- Send a PR to update this file. CC @jaredpar, @mmitche, @jasonmalinowski and one of us will get it merged. - -Once that is merged Jenkins will schedule a task to add the new branches into the system. This can take up to 30 minutes to complete if left on it's own. Generally you want to force this to happen immediately by doing the following: - -- Navgiate to https://ci.dot.net/job/dotnet_dotnet-ci_generator/ -- Hit "Login" in the top right corner. This will use Oauth and GitHub to log you in. -- Click the "Build with Parameters" link -- Click the "Build" button - -Once that job completes the branch folder will now be listed under the roslyn folder in Jenkins. - -https://ci.dot.net/job/dotnet_roslyn/ - -## Changing the netci.groovy file -This step is necessary both at the point the branch is initially created and for any future changes to the netci.groovy file in our repo. - -Jenkins will monitor branches for changes and anytime it sees a change to netci.groovy it will schedule a change to re-generate all of the PR jobs. This scheduling can take a considerable amount of time to complete. Often you want to trigger the re-generation of jobs manually in order to make rapid progress here. - -To regenerate the jobs do the following: - -- Navigate to the Roslyn folder in Jenkins: https://ci.dot.net/job/dotnet_roslyn/ -- Click on the feature folder. Example https://ci.dot.net/job/dotnet_roslyn/job/features_mono -- Click on the generator job link -- Click the "Build with Parameters" link (that link only appears if you are logged in) -- Click the "Build" button - -Once that job completes new PRs into dotnet/roslyn will reflect the changes to the netci.groovy script. - - diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index ca58df424ba38..972f35804c2c9 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -363,7 +363,7 @@ internal NamespaceOrTypeOrAliasSymbolWithAnnotations BindNamespaceOrTypeOrAliasS var conversions = this.Conversions.WithNullability(includeNullability: true); type.CheckConstraints(this.Compilation, conversions, location, diagnostics); } - else if (constructedType.TypeSymbol.IsUnconstrainedTypeParameter()) + else if (constructedType.TypeSymbol.IsTypeParameterDisallowingAnnotation()) { diagnostics.Add(ErrorCode.ERR_NullableUnconstrainedTypeParameter, syntax.Location); } diff --git a/src/Compilers/CSharp/Portable/Binder/BuckStopsHereBinder.cs b/src/Compilers/CSharp/Portable/Binder/BuckStopsHereBinder.cs index 1a2d92dba96bc..527b0e1afded4 100644 --- a/src/Compilers/CSharp/Portable/Binder/BuckStopsHereBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/BuckStopsHereBinder.cs @@ -159,7 +159,20 @@ internal override Symbol ContainingMemberOrLambda internal override bool IsNullableGloballyEnabled() { - return Compilation.Options.NullableContextOptions != NullableContextOptions.Disable; + switch (Compilation.Options.NullableContextOptions) + { + case NullableContextOptions.Enable: + case NullableContextOptions.SafeOnly: + return true; + + case NullableContextOptions.Disable: + case NullableContextOptions.Warnings: + case NullableContextOptions.SafeOnlyWarnings: + return false; + + default: + throw ExceptionUtilities.UnexpectedValue(Compilation.Options.NullableContextOptions); + } } internal override Binder GetBinder(SyntaxNode node) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/BestTypeInferrer.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/BestTypeInferrer.cs index 76e13ee4cb0a5..04cf52ef55baf 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/BestTypeInferrer.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/BestTypeInferrer.cs @@ -10,65 +10,18 @@ namespace Microsoft.CodeAnalysis.CSharp { internal static class BestTypeInferrer { - public static NullableAnnotation GetNullableAnnotation(TypeSymbol bestType, ArrayBuilder types) + public static NullableAnnotation GetNullableAnnotation(ArrayBuilder types) { - bool bestTypeIsPossiblyNullableReferenceTypeTypeParameter = bestType.IsPossiblyNullableReferenceTypeTypeParameter(); - NullableAnnotation? result = null; + NullableAnnotation result = NullableAnnotation.NotAnnotated; foreach (var type in types) { - if (type.IsNull) - { - // https://github.com/dotnet/roslyn/issues/27961 Should ignore untyped - // expressions such as unbound lambdas and typeless tuples. - result = NullableAnnotation.Nullable; - continue; - } - - if (!type.IsReferenceType && !type.TypeSymbol.IsPossiblyNullableReferenceTypeTypeParameter()) - { - return NullableAnnotation.Unknown; - } - - NullableAnnotation nullableAnnotation; - - if (type.IsPossiblyNullableReferenceTypeTypeParameter() && !bestTypeIsPossiblyNullableReferenceTypeTypeParameter) - { - nullableAnnotation = NullableAnnotation.Nullable; - } - else - { - nullableAnnotation = type.NullableAnnotation; - } - - if (nullableAnnotation == NullableAnnotation.Unknown) - { - if (result?.IsAnyNotNullable() != false) - { - result = NullableAnnotation.Unknown; - } - } - else if (nullableAnnotation.IsAnyNullable()) - { - if (result?.IsAnyNullable() != true) - { - result = nullableAnnotation; - } - else if (result != nullableAnnotation) - { - result = NullableAnnotation.Annotated; - } - } - else if (result == null) - { - result = nullableAnnotation; - } - else if (result.GetValueOrDefault() == NullableAnnotation.NotNullable && nullableAnnotation == NullableAnnotation.NotAnnotated) - { - result = NullableAnnotation.NotAnnotated; - } + Debug.Assert(!type.IsNull); + Debug.Assert(type.Equals(types[0], TypeCompareKind.AllIgnoreOptions)); + // This uses the covariant merging rules. + result = result.JoinForFixingLowerBounds(type.AsSpeakable().NullableAnnotation); } - return result ?? NullableAnnotation.NotAnnotated; + return result; } /// @@ -106,6 +59,10 @@ public static TypeSymbol InferBestType( return type; } + if (conversions.IncludeNullability) + { + type = type.SetSpeakableNullabilityForReferenceTypes(); + } candidateTypes.Add(type); } } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs index 34187e58e3948..fd17d2a439721 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs @@ -1915,7 +1915,8 @@ private Conversion GetImplicitTupleLiteralConversion(BoundTupleLiteral source, T destination, ref useSiteDiagnostics, ConversionKind.ImplicitTupleLiteral, - (ConversionsBase conversions, BoundExpression s, TypeSymbolWithAnnotations d, ref HashSet u, bool a) => conversions.ClassifyImplicitConversionFromExpression(s, d.TypeSymbol, ref u), + (ConversionsBase conversions, BoundExpression s, TypeSymbolWithAnnotations d, ref HashSet u, bool a) + => conversions.ClassifyImplicitConversionFromExpression(s, d.TypeSymbol, ref u), arg: false); } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs index ff316f59056e9..144c2f414ca9e 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs @@ -91,7 +91,7 @@ private enum Dependency private readonly ImmutableArray _formalParameterTypes; private readonly ImmutableArray _formalParameterRefKinds; private readonly ImmutableArray _arguments; - private readonly Func _getNullableAnnotationOpt; + private readonly Func _getTypeWithAnnotationOpt; private readonly TypeSymbolWithAnnotations[] _fixedResults; private readonly HashSet[] _exactBounds; @@ -216,7 +216,7 @@ public static MethodTypeInferenceResult Infer( // no arguments per se we cons up some fake arguments. out bool hadNullabilityMismatch, ref HashSet useSiteDiagnostics, - Func getNullableAnnotationOpt = null) + Func getTypeWithAnnotationOpt = null) { Debug.Assert(!methodTypeParameters.IsDefault); Debug.Assert(methodTypeParameters.Length > 0); @@ -242,7 +242,7 @@ public static MethodTypeInferenceResult Infer( formalParameterTypes, formalParameterRefKinds, arguments, - getNullableAnnotationOpt); + getTypeWithAnnotationOpt); return inferrer.InferTypeArgs(binder, out hadNullabilityMismatch, ref useSiteDiagnostics); } @@ -262,7 +262,7 @@ private MethodTypeInferrer( ImmutableArray formalParameterTypes, ImmutableArray formalParameterRefKinds, ImmutableArray arguments, - Func getNullableAnnotationOpt) + Func getTypeWithAnnotationOpt) { _conversions = conversions; _methodTypeParameters = methodTypeParameters; @@ -270,7 +270,7 @@ private MethodTypeInferrer( _formalParameterTypes = formalParameterTypes; _formalParameterRefKinds = formalParameterRefKinds; _arguments = arguments; - _getNullableAnnotationOpt = getNullableAnnotationOpt; + _getTypeWithAnnotationOpt = getTypeWithAnnotationOpt; _fixedResults = new TypeSymbolWithAnnotations[methodTypeParameters.Length]; _exactBounds = new HashSet[methodTypeParameters.Length]; _upperBounds = new HashSet[methodTypeParameters.Length]; @@ -444,6 +444,7 @@ private bool AllFixed() private void AddBound(TypeSymbolWithAnnotations addedBound, HashSet[] collectedBounds, TypeSymbolWithAnnotations methodTypeParameterWithAnnotations) { Debug.Assert(IsUnfixedTypeParameter(methodTypeParameterWithAnnotations)); + Debug.Assert(addedBound.NullableAnnotation.IsSpeakable()); var methodTypeParameter = (TypeParameterSymbol)methodTypeParameterWithAnnotations.TypeSymbol; int methodTypeParameterIndex = methodTypeParameter.Ordinal; @@ -541,8 +542,6 @@ private void MakeExplicitParameterTypeInferences(Binder binder, BoundExpression // SPEC: * Otherwise, no inference is made for this argument - var source = argument.Type; - if (argument.Kind == BoundKind.UnboundLambda) { ExplicitParameterTypeInference(argument, target, ref useSiteDiagnostics); @@ -551,10 +550,9 @@ private void MakeExplicitParameterTypeInferences(Binder binder, BoundExpression !MakeExplicitParameterTypeInferences(binder, (BoundTupleLiteral)argument, target, kind, ref useSiteDiagnostics)) { // Either the argument is not a tuple literal, or we were unable to do the inference from its elements, let's try to infer from argument type - if (IsReallyAType(source)) + if (IsReallyAType(argument.Type)) { - var annotation = GetNullableAnnotation(argument); - ExactOrBoundsInference(kind, TypeSymbolWithAnnotations.Create(source, annotation), target, ref useSiteDiagnostics); + ExactOrBoundsInference(kind, GetTypeWithAnnotations(argument), target, ref useSiteDiagnostics); } } } @@ -1197,7 +1195,7 @@ private void OutputTypeInference(Binder binder, BoundExpression expression, Type } // SPEC: * Otherwise, if E is an expression with type U then a lower-bound // SPEC: inference is made from U to T. - var sourceType = TypeSymbolWithAnnotations.Create(expression.Type, GetNullableAnnotation(expression)); + var sourceType = GetTypeWithAnnotations(expression); if (!sourceType.IsNull) { LowerBoundInference(sourceType, target, ref useSiteDiagnostics); @@ -1505,7 +1503,7 @@ private bool ExactOrBoundsNullableInference(ExactOrBoundsKind kind, TypeSymbolWi return true; } - if (isNullableOnly(source) && isNullableOnly(target)) + if (s_isNullableOnly(source) && s_isNullableOnly(target)) { ExactOrBoundsInference(kind, source.AsNotNullableReferenceType(), target.AsNotNullableReferenceType(), ref useSiteDiagnostics); return true; @@ -1514,7 +1512,8 @@ private bool ExactOrBoundsNullableInference(ExactOrBoundsKind kind, TypeSymbolWi return false; // True if the type is nullable but not an unconstrained type parameter. - bool isNullableOnly(TypeSymbolWithAnnotations type) => type.NullableAnnotation.IsAnyNullable() && !type.TypeSymbol.IsUnconstrainedTypeParameter(); + bool s_isNullableOnly (TypeSymbolWithAnnotations type) + => type.NullableAnnotation.IsAnyNullable() && !type.TypeSymbol.IsTypeParameterDisallowingAnnotation(); } private bool ExactNullableInference(TypeSymbolWithAnnotations source, TypeSymbolWithAnnotations target, ref HashSet useSiteDiagnostics) @@ -2443,13 +2442,13 @@ private static TypeSymbolWithAnnotations Fix( return best; } - private NullableAnnotation GetNullableAnnotation(BoundExpression expr) + private TypeSymbolWithAnnotations GetTypeWithAnnotations(BoundExpression expr) { - if (!_conversions.IncludeNullability) + if (_conversions.IncludeNullability && _getTypeWithAnnotationOpt != null) { - return NullableAnnotation.Unknown; + return _getTypeWithAnnotationOpt(expr); } - return _getNullableAnnotationOpt?.Invoke(expr) ?? NullableAnnotation.Unknown; + return TypeSymbolWithAnnotations.Create(expr.Type); } internal static TypeSymbolWithAnnotations Merge(TypeSymbolWithAnnotations first, TypeSymbolWithAnnotations second, VarianceKind variance, ConversionsBase conversions, out bool hadNullabilityMismatch) @@ -2712,7 +2711,7 @@ public static ImmutableArray InferTypeArgumentsFromFi constructedFromMethod.GetParameterTypes(), constructedFromMethod.ParameterRefKinds, arguments, - getNullableAnnotationOpt: null); + getTypeWithAnnotationOpt: null); if (!inferrer.InferTypeArgumentsFromFirstArgument(ref useSiteDiagnostics)) { @@ -2738,8 +2737,7 @@ private bool InferTypeArgumentsFromFirstArgument(ref HashSet use { return false; } - var annotation = GetNullableAnnotation(argument); - LowerBoundInference(TypeSymbolWithAnnotations.Create(source, annotation), dest, ref useSiteDiagnostics); + LowerBoundInference(GetTypeWithAnnotations(argument), dest, ref useSiteDiagnostics); // Now check to see that every type parameter used by the first // formal parameter type was successfully inferred. for (int iParam = 0; iParam < _methodTypeParameters.Length; ++iParam) diff --git a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs index c22f6bb96ec5e..af19ce3f72ca3 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs @@ -99,7 +99,7 @@ public TypeSymbolWithAnnotations GetInferredReturnType(ConversionsBase conversio } else { - var returnTypes = ArrayBuilder<(RefKind, TypeSymbolWithAnnotations)>.GetInstance(); + var returnTypes = ArrayBuilder<(BoundReturnStatement, TypeSymbolWithAnnotations)>.GetInstance(); // Diagnostics from NullableWalker.Analyze can be dropped here since Analyze // will be called again from NullableWalker.ApplyConversion when the // BoundLambda is converted to an anonymous function. @@ -110,7 +110,7 @@ public TypeSymbolWithAnnotations GetInferredReturnType(ConversionsBase conversio var compilation = Binder.Compilation; NullableWalker.Analyze(compilation, lambda: this, diagnostics, delegateInvokeMethod: delegateType?.DelegateInvokeMethod, returnTypes: returnTypes, initialState: nullableState); diagnostics.Free(); - var inferredReturnType = InferReturnType(returnTypes, compilation, conversions, delegateType, Symbol.IsAsync); + var inferredReturnType = InferReturnType(returnTypes, node: this, compilation, conversions, delegateType, Symbol.IsAsync); returnTypes.Free(); return inferredReturnType.Type; } @@ -124,28 +124,32 @@ public TypeSymbolWithAnnotations GetInferredReturnType(ConversionsBase conversio /// /// Behavior of this function should be kept aligned with . /// - internal static InferredLambdaReturnType InferReturnType(ArrayBuilder<(RefKind, TypeSymbolWithAnnotations)> returnTypes, CSharpCompilation compilation, ConversionsBase conversions, TypeSymbol delegateType, bool isAsync) + internal static InferredLambdaReturnType InferReturnType(ArrayBuilder<(BoundReturnStatement, TypeSymbolWithAnnotations)> returnTypes, + BoundNode node, CSharpCompilation compilation, ConversionsBase conversions, TypeSymbol delegateType, bool isAsync) { - var types = ArrayBuilder.GetInstance(); + var types = ArrayBuilder<(BoundExpression, TypeSymbolWithAnnotations)>.GetInstance(); bool hasReturnWithoutArgument = false; RefKind refKind = RefKind.None; - foreach (var (rk, type) in returnTypes) + foreach (var (returnStatement, type) in returnTypes) { + RefKind rk = returnStatement.RefKind; if (rk != RefKind.None) { refKind = rk; } + if ((object)type.TypeSymbol == NoReturnExpression) { hasReturnWithoutArgument = true; } else { - types.Add(type); + types.Add((returnStatement.ExpressionOpt, type)); } } + HashSet useSiteDiagnostics = null; - var bestType = CalculateReturnType(compilation, conversions, delegateType, types, isAsync, ref useSiteDiagnostics); + var bestType = CalculateReturnType(compilation, conversions, delegateType, types, isAsync, node, ref useSiteDiagnostics); int numberOfDistinctReturns = types.Count + (hasReturnWithoutArgument ? 1 : 0); return new InferredLambdaReturnType(numberOfDistinctReturns < 2, refKind, bestType, useSiteDiagnostics.AsImmutableOrEmpty()); } @@ -154,35 +158,42 @@ private static TypeSymbolWithAnnotations CalculateReturnType( CSharpCompilation compilation, ConversionsBase conversions, TypeSymbol delegateType, - ArrayBuilder resultTypes, + ArrayBuilder<(BoundExpression, TypeSymbolWithAnnotations resultType)> returns, bool isAsync, + BoundNode node, ref HashSet useSiteDiagnostics) { TypeSymbolWithAnnotations bestResultType; - int n = resultTypes.Count; + int n = returns.Count; switch (n) { case 0: bestResultType = default; break; case 1: - bestResultType = resultTypes[0]; + bestResultType = returns[0].resultType; + if (conversions.IncludeNullability) + { + bestResultType = bestResultType.SetSpeakableNullabilityForReferenceTypes(); + } break; default: - var typesOnly = ArrayBuilder.GetInstance(n); - foreach (var resultType in resultTypes) + // Need to handle ref returns. See https://github.com/dotnet/roslyn/issues/30432 + if (conversions.IncludeNullability) { - typesOnly.Add(resultType.TypeSymbol); + bestResultType = NullableWalker.BestTypeForLambdaReturns(returns, compilation, node); + } + else + { + var typesOnly = ArrayBuilder.GetInstance(n); + foreach (var (_, resultType) in returns) + { + typesOnly.Add(resultType.TypeSymbol); + } + var bestType = BestTypeInferrer.GetBestType(typesOnly, conversions, hadNullabilityMismatch: out _, ref useSiteDiagnostics); + bestResultType = bestType is null ? default : TypeSymbolWithAnnotations.Create(bestType); + typesOnly.Free(); } - bool hadNullabilityMismatch; - var bestType = BestTypeInferrer.GetBestType(typesOnly, conversions, out hadNullabilityMismatch, ref useSiteDiagnostics); - // https://github.com/dotnet/roslyn/issues/30480: Should return `bestType` even if - // there was a nullability mismatch, and `hadNullabilityMismatch` should be available - // to the caller, and up through MethodTypeInferrer.Infer. - bestResultType = bestType is null || hadNullabilityMismatch ? - default : - TypeSymbolWithAnnotations.Create(bestType, BestTypeInferrer.GetNullableAnnotation(bestType, resultTypes)); - typesOnly.Free(); break; } @@ -232,14 +243,14 @@ private static TypeSymbolWithAnnotations CalculateReturnType( internal sealed class BlockReturns : BoundTreeWalker { - private readonly ArrayBuilder<(RefKind, TypeSymbolWithAnnotations)> _builder; + private readonly ArrayBuilder<(BoundReturnStatement, TypeSymbolWithAnnotations)> _builder; - private BlockReturns(ArrayBuilder<(RefKind, TypeSymbolWithAnnotations)> builder) + private BlockReturns(ArrayBuilder<(BoundReturnStatement, TypeSymbolWithAnnotations)> builder) { _builder = builder; } - public static void GetReturnTypes(ArrayBuilder<(RefKind, TypeSymbolWithAnnotations)> builder, BoundBlock block) + public static void GetReturnTypes(ArrayBuilder<(BoundReturnStatement, TypeSymbolWithAnnotations)> builder, BoundBlock block) { var visitor = new BoundLambda.BlockReturns(builder); visitor.Visit(block); @@ -272,7 +283,7 @@ public override BoundNode VisitReturnStatement(BoundReturnStatement node) var type = (expression is null) ? NoReturnExpression : expression.Type?.SetUnknownNullabilityForReferenceTypes(); - _builder.Add((node.RefKind, TypeSymbolWithAnnotations.Create(type))); + _builder.Add((node, TypeSymbolWithAnnotations.Create(type))); return null; } } @@ -316,7 +327,9 @@ internal UnboundLambda WithNullableState(Binder binder, NullableWalker.VariableS public bool HasSignature { get { return Data.HasSignature; } } public bool HasExplicitlyTypedParameterList { get { return Data.HasExplicitlyTypedParameterList; } } public int ParameterCount { get { return Data.ParameterCount; } } - public TypeSymbolWithAnnotations InferReturnType(ConversionsBase conversions, NamedTypeSymbol delegateType, ref HashSet useSiteDiagnostics) { return BindForReturnTypeInference(delegateType).GetInferredReturnType(conversions, _nullableState, ref useSiteDiagnostics); } + public TypeSymbolWithAnnotations InferReturnType(ConversionsBase conversions, NamedTypeSymbol delegateType, ref HashSet useSiteDiagnostics) + => BindForReturnTypeInference(delegateType).GetInferredReturnType(conversions, _nullableState, ref useSiteDiagnostics); + public RefKind RefKind(int index) { return Data.RefKind(index); } public void GenerateAnonymousFunctionConversionError(DiagnosticBag diagnostics, TypeSymbol targetType) { Data.GenerateAnonymousFunctionConversionError(diagnostics, targetType); } public bool GenerateSummaryErrors(DiagnosticBag diagnostics) { return Data.GenerateSummaryErrors(diagnostics); } @@ -593,9 +606,9 @@ private BoundLambda ReallyInferReturnType(NamedTypeSymbol delegateType, Immutabl diagnostics: diagnostics); Binder lambdaBodyBinder = new ExecutableCodeBinder(_unboundLambda.Syntax, lambdaSymbol, ParameterBinder(lambdaSymbol, binder)); var block = BindLambdaBody(lambdaSymbol, lambdaBodyBinder, diagnostics); - var returnTypes = ArrayBuilder<(RefKind, TypeSymbolWithAnnotations)>.GetInstance(); + var returnTypes = ArrayBuilder<(BoundReturnStatement, TypeSymbolWithAnnotations)>.GetInstance(); BoundLambda.BlockReturns.GetReturnTypes(returnTypes, block); - var inferredReturnType = BoundLambda.InferReturnType(returnTypes, lambdaBodyBinder.Compilation, lambdaBodyBinder.Conversions, delegateType, lambdaSymbol.IsAsync); + var inferredReturnType = BoundLambda.InferReturnType(returnTypes, _unboundLambda, lambdaBodyBinder.Compilation, lambdaBodyBinder.Conversions, delegateType, lambdaSymbol.IsAsync); returnTypes.Free(); var result = new BoundLambda(_unboundLambda.Syntax, _unboundLambda, block, diagnostics.ToReadOnlyAndFree(), lambdaBodyBinder, delegateType, inferredReturnType) { WasCompilerGenerated = _unboundLambda.WasCompilerGenerated }; diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index e0118a94d4c91..9bb500c8a4809 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -1843,7 +1843,7 @@ internal static string ERR_BadNonTrailingNamedArgument { } /// - /// Looks up a localized string similar to Invalid option '{0}' for /nullable; must be 'disable', 'enable' or 'safeonly'. + /// Looks up a localized string similar to Invalid option '{0}' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings'. /// internal static string ERR_BadNullableContextOption { get { diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index f9f214da8e03c..b74a0158e2de0 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -4603,8 +4603,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ `latest` (latest version, including minor versions), or specific versions like `6` or `7.1` -nullable[+|-] Specify nullable context option enable|disable. --nullable:{enable|disable|safeonly} - Specify nullable context option enable|disable|safeonly. +-nullable:{enable|disable|safeonly|warnings|safeonlywarnings} + Specify nullable context option enable|disable|safeonly|warnings|safeonlywarnings. - SECURITY - -delaysign[+|-] Delay-sign the assembly using only the public @@ -5689,7 +5689,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ An expression tree may not contain a null coalescing assignment - Invalid option '{0}' for /nullable; must be 'disable', 'enable' or 'safeonly' + Invalid option '{0}' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' Parentheses are required around the switch governing expression. diff --git a/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs b/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs index 7426d3d0f5c2e..d7bdce04c49ff 100644 --- a/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs +++ b/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs @@ -347,6 +347,14 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar Debug.Assert(loweredValue == nameof(NullableContextOptions.SafeOnly).ToLower()); nullableContextOptions = NullableContextOptions.SafeOnly; break; + case "warnings": + Debug.Assert(loweredValue == nameof(NullableContextOptions.Warnings).ToLower()); + nullableContextOptions = NullableContextOptions.Warnings; + break; + case "safeonlywarnings": + Debug.Assert(loweredValue == nameof(NullableContextOptions.SafeOnlyWarnings).ToLower()); + nullableContextOptions = NullableContextOptions.SafeOnlyWarnings; + break; default: AddDiagnostic(diagnostics, ErrorCode.ERR_BadNullableContextOption, value); break; diff --git a/src/Compilers/CSharp/Portable/Compilation/AwaitExpressionInfo.cs b/src/Compilers/CSharp/Portable/Compilation/AwaitExpressionInfo.cs index 43ed80212011c..6657db6aab8b7 100644 --- a/src/Compilers/CSharp/Portable/Compilation/AwaitExpressionInfo.cs +++ b/src/Compilers/CSharp/Portable/Compilation/AwaitExpressionInfo.cs @@ -13,13 +13,13 @@ public struct AwaitExpressionInfo : IEquatable { private readonly AwaitableInfo _awaitableInfo; - public IMethodSymbol GetAwaiterMethod => _awaitableInfo.GetAwaiter; + public IMethodSymbol GetAwaiterMethod => _awaitableInfo?.GetAwaiter; - public IPropertySymbol IsCompletedProperty => _awaitableInfo.IsCompleted; + public IPropertySymbol IsCompletedProperty => _awaitableInfo?.IsCompleted; - public IMethodSymbol GetResultMethod => _awaitableInfo.GetResult; + public IMethodSymbol GetResultMethod => _awaitableInfo?.GetResult; - public bool IsDynamic => _awaitableInfo.IsDynamic; + public bool IsDynamic => _awaitableInfo?.IsDynamic == true; internal AwaitExpressionInfo(AwaitableInfo awaitableInfo) { @@ -29,7 +29,7 @@ internal AwaitExpressionInfo(AwaitableInfo awaitableInfo) public override bool Equals(object obj) { - return obj is AwaitExpressionInfo && Equals((AwaitExpressionInfo)obj); + return obj is AwaitExpressionInfo otherAwait && Equals(otherAwait); } public bool Equals(AwaitExpressionInfo other) @@ -41,6 +41,10 @@ public bool Equals(AwaitExpressionInfo other) public override int GetHashCode() { + if (_awaitableInfo is null) + { + return 0; + } return Hash.Combine(GetAwaiterMethod, Hash.Combine(IsCompletedProperty, GetResultMethod.GetHashCode())); } } diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpDiagnosticFilter.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpDiagnosticFilter.cs index a3ed6c03baf5c..8cac850d94c18 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpDiagnosticFilter.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpDiagnosticFilter.cs @@ -187,11 +187,19 @@ internal static ReportDiagnostic GetDiagnosticReport( break; case NullableContextOptions.SafeOnly: + case NullableContextOptions.SafeOnlyWarnings: if (isNullableFlowAnalysisNonSafetyWarning) { return ReportDiagnostic.Suppress; } break; + + case NullableContextOptions.Enable: + case NullableContextOptions.Warnings: + break; + + default: + throw ExceptionUtilities.UnexpectedValue(nullableOption); } // Unless specific warning options are defined (/warnaserror[+|-]: or /nowarn:, diff --git a/src/Compilers/CSharp/Portable/Errors/LazyUseSiteDiagnosticsInfoForNullableType.cs b/src/Compilers/CSharp/Portable/Errors/LazyUseSiteDiagnosticsInfoForNullableType.cs index 708675bf679d8..259344edd53a6 100644 --- a/src/Compilers/CSharp/Portable/Errors/LazyUseSiteDiagnosticsInfoForNullableType.cs +++ b/src/Compilers/CSharp/Portable/Errors/LazyUseSiteDiagnosticsInfoForNullableType.cs @@ -19,7 +19,7 @@ protected override DiagnosticInfo ResolveInfo() { return _possiblyNullableTypeSymbol.TypeSymbol.OriginalDefinition.GetUseSiteDiagnostic(); } - else if (_possiblyNullableTypeSymbol.TypeSymbol.IsUnconstrainedTypeParameter()) + else if (_possiblyNullableTypeSymbol.TypeSymbol.IsTypeParameterDisallowingAnnotation()) { return new CSDiagnosticInfo(ErrorCode.ERR_NullableUnconstrainedTypeParameter); } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 0adb4665468da..ec17d8fda626b 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -77,9 +77,9 @@ internal VariableState( private readonly MethodSymbol _methodSignatureOpt; /// - /// Types from return expressions. Used when inferring lambda return type in MethodTypeInferrer. + /// Return statements and the result types from analyzing the returned expressions. Used when inferring lambda return type in MethodTypeInferrer. /// - private readonly ArrayBuilder<(RefKind, TypeSymbolWithAnnotations)> _returnTypesOpt; + private readonly ArrayBuilder<(BoundReturnStatement, TypeSymbolWithAnnotations)> _returnTypesOpt; /// /// An optional callback for callers to receive notification of the inferred type and nullability @@ -128,7 +128,7 @@ private NullableWalker( bool useMethodSignatureParameterTypes, MethodSymbol methodSignatureOpt, BoundNode node, - ArrayBuilder<(RefKind, TypeSymbolWithAnnotations)> returnTypesOpt, + ArrayBuilder<(BoundReturnStatement, TypeSymbolWithAnnotations)> returnTypesOpt, VariableState initialState, Action callbackOpt) : base(compilation, method, node, new EmptyStructTypeCache(compilation, dev12CompilerCompatibility: false), trackUnassignments: true) @@ -205,7 +205,7 @@ internal static void Analyze( BoundLambda lambda, DiagnosticBag diagnostics, MethodSymbol delegateInvokeMethod, - ArrayBuilder<(RefKind, TypeSymbolWithAnnotations)> returnTypes, + ArrayBuilder<(BoundReturnStatement, TypeSymbolWithAnnotations)> returnTypes, VariableState initialState) { Analyze( @@ -228,7 +228,7 @@ private static void Analyze( bool useMethodSignatureReturnType, bool useMethodSignatureParameterTypes, MethodSymbol methodSignatureOpt, - ArrayBuilder<(RefKind, TypeSymbolWithAnnotations)> returnTypes, + ArrayBuilder<(BoundReturnStatement, TypeSymbolWithAnnotations)> returnTypes, VariableState initialState, Action callbackOpt) { @@ -614,7 +614,7 @@ bool isDefaultOfUnconstrainedTypeParameter(BoundExpression expr) isDefaultOfUnconstrainedTypeParameter(conversion.Operand); } case BoundKind.DefaultExpression: - return IsUnconstrainedTypeParameter(expr.Type); + return IsTypeParameterDisallowingAnnotation(expr.Type); default: return false; } @@ -801,7 +801,7 @@ private void InheritNullableStateOfMember(int targetContainerSlot, int valueCont // https://github.com/dotnet/roslyn/issues/29968 Remove isByRefTarget check? if (isByRefTarget) { - // This is a member access through a by ref entity and it isn't considered declared as not-nullable. + // This is a member access through a by ref entity and it isn't considered declared as not-nullable. // Since reference can point to the heap, we cannot assume the member doesn't have null value // after this assignment, regardless of what value is being assigned. } @@ -1060,7 +1060,7 @@ protected override BoundNode VisitReturnStatementNoAdjust(BoundReturnStatement n { // Inferring return type. Should not convert to method return type. TypeSymbolWithAnnotations result = VisitRvalueWithResult(expr); - _returnTypesOpt.Add((node.RefKind, result)); + _returnTypesOpt.Add((node, result)); return null; } @@ -1082,9 +1082,9 @@ private TypeSymbolWithAnnotations GetReturnType() return returnType; } - private static bool IsUnconstrainedTypeParameter(TypeSymbol typeOpt) + private static bool IsTypeParameterDisallowingAnnotation(TypeSymbol typeOpt) { - return typeOpt?.IsUnconstrainedTypeParameter() == true; + return typeOpt?.IsTypeParameterDisallowingAnnotation() == true; } public override BoundNode VisitLocal(BoundLocal node) @@ -1366,78 +1366,158 @@ public override BoundNode VisitArrayCreation(BoundArrayCreation node) private ArrayTypeSymbol VisitArrayInitializer(BoundArrayCreation node) { + BoundArrayInitialization initialization = node.InitializerOpt; + var expressions = ArrayBuilder.GetInstance(initialization.Initializers.Length); + GetArrayElements(initialization, expressions); + int n = expressions.Count; + + // Consider recording in the BoundArrayCreation + // whether the array was implicitly typed, rather than relying on syntax. + bool isInferred = node.Syntax.Kind() == SyntaxKind.ImplicitArrayCreationExpression; var arrayType = (ArrayTypeSymbol)node.Type; var elementType = arrayType.ElementType; + if (!isInferred) + { + for (int i = 0; i < n; i++) + { + _ = VisitOptionalImplicitConversion(expressions[i], elementType, useLegacyWarnings: false, AssignmentKind.Assignment); + } - BoundArrayInitialization initialization = node.InitializerOpt; - var elementBuilder = ArrayBuilder.GetInstance(initialization.Initializers.Length); - GetArrayElements(initialization, elementBuilder); - - // https://github.com/dotnet/roslyn/issues/27961 Removing and recalculating conversions should not - // be necessary for explicitly typed arrays. In those cases, VisitConversion should warn - // on nullability mismatch (although we'll need to ensure we handle the case where - // initial binding calculated an Identity conversion, even though nullability was distinct). - int n = elementBuilder.Count; - var conversionBuilder = ArrayBuilder.GetInstance(n); - var resultBuilder = ArrayBuilder.GetInstance(n); + _resultType = _invalidType; + return arrayType; + } + + var conversions = ArrayBuilder.GetInstance(n); + var resultTypes = ArrayBuilder.GetInstance(n); for (int i = 0; i < n; i++) { - (BoundExpression element, Conversion conversion) = RemoveConversion(elementBuilder[i], includeExplicitConversions: false); - elementBuilder[i] = element; - conversionBuilder.Add(conversion); - resultBuilder.Add(VisitRvalueWithResult(element)); + // collect expressions, conversions and result types + (BoundExpression expression, Conversion conversion) = RemoveConversion(expressions[i], includeExplicitConversions: false); + expressions[i] = expression; + conversions.Add(conversion); + var resultType = VisitRvalueWithResult(expression); + resultTypes.Add(resultType); } - bool checkConversions = true; - // Consider recording in the BoundArrayCreation - // whether the array was implicitly typed, rather than relying on syntax. - if (node.Syntax.Kind() == SyntaxKind.ImplicitArrayCreationExpression) + var placeholderBuilder = ArrayBuilder.GetInstance(n); + for (int i = 0; i < n; i++) { - TypeSymbol bestType = null; - if (!node.HasErrors) + placeholderBuilder.Add(CreatePlaceholderIfNecessary(expressions[i], resultTypes[i])); + } + var placeholders = placeholderBuilder.ToImmutableAndFree(); + + TypeSymbol bestType = null; + bool hadNestedNullabilityMismatch = false; + if (!node.HasErrors) + { + HashSet useSiteDiagnostics = null; + bestType = BestTypeInferrer.InferBestType(placeholders, _conversions, out hadNestedNullabilityMismatch, ref useSiteDiagnostics); + if (hadNestedNullabilityMismatch) { - var placeholderBuilder = ArrayBuilder.GetInstance(n); - for (int i = 0; i < n; i++) - { - placeholderBuilder.Add(CreatePlaceholderIfNecessary(elementBuilder[i], resultBuilder[i])); - } - var placeholders = placeholderBuilder.ToImmutableAndFree(); - bool hadNullabilityMismatch; - HashSet useSiteDiagnostics = null; - bestType = BestTypeInferrer.InferBestType(placeholders, _conversions, out hadNullabilityMismatch, ref useSiteDiagnostics); - if (hadNullabilityMismatch) - { - ReportSafetyDiagnostic(ErrorCode.WRN_NoBestNullabilityArrayElements, node.Syntax); - checkConversions = false; - } + ReportSafetyDiagnostic(ErrorCode.WRN_NoBestNullabilityArrayElements, node.Syntax); } - if ((object)bestType == null) + } + + TypeSymbolWithAnnotations inferredType; + if (bestType is null) + { + inferredType = elementType.SetUnknownNullabilityForReferenceTypes(); + } + else + { + inferredType = TypeSymbolWithAnnotations.Create(bestType); + } + + if ((object)bestType != null && !inferredType.IsValueType) + { + // Note: so long as we have a best type, we can proceed. But we don't want to report warnings on nested nullability + + // Convert elements to best type to determine element top-level nullability and to report nested nullability warnings + for (int i = 0; i < n; i++) { - elementType = elementType.SetUnknownNullabilityForReferenceTypes(); - checkConversions = false; + var placeholder = placeholders[i]; + resultTypes[i] = ApplyConversion(placeholder, placeholder, conversions[i], inferredType, resultTypes[i], checkConversion: true, + fromExplicitCast: false, useLegacyWarnings: false, AssignmentKind.Assignment, reportNestedWarnings: !hadNestedNullabilityMismatch, reportTopLevelWarnings: false); } - else + + // Set top-level nullability on inferred element type + inferredType = TypeSymbolWithAnnotations.Create(inferredType.TypeSymbol, BestTypeInferrer.GetNullableAnnotation(resultTypes)); + + for (int i = 0; i < n; i++) { - elementType = TypeSymbolWithAnnotations.Create(bestType, BestTypeInferrer.GetNullableAnnotation(bestType, resultBuilder)); + var nodeForSyntax = expressions[i]; + // Report top-level warnings + _ = ApplyConversion(nodeForSyntax, operandOpt: null, Conversion.Identity, targetTypeWithNullability: inferredType, operandType: resultTypes[i], + checkConversion: true, fromExplicitCast: false, useLegacyWarnings: false, AssignmentKind.Assignment, reportNestedWarnings: false); } - arrayType = arrayType.WithElementType(elementType); } - if (checkConversions && !elementType.IsValueType) + resultTypes.Free(); + expressions.Free(); + + _resultType = _invalidType; + arrayType = arrayType.WithElementType(inferredType); + return arrayType; + } + + /// + /// Applies a method similar to + /// The expressions returned from a lambda are not converted though, so we'll have to classify fresh conversions. + /// Note: even if some conversions fail, we'll proceed to infer top-level nullability. That is reasonable in common cases. + /// + internal static TypeSymbolWithAnnotations BestTypeForLambdaReturns( + ArrayBuilder<(BoundExpression, TypeSymbolWithAnnotations)> returns, + CSharpCompilation compilation, + BoundNode node) + { + var walker = new NullableWalker(compilation, method: null, + useMethodSignatureReturnType: false, useMethodSignatureParameterTypes: false, methodSignatureOpt: null, + node, returnTypesOpt: null, initialState: null, callbackOpt: null); + + int n = returns.Count; + var expressions = ArrayBuilder.GetInstance(); + var resultTypes = ArrayBuilder.GetInstance(n); + var placeholdersBuilder = ArrayBuilder.GetInstance(n); + for (int i = 0; i < n; i++) { + var (returnExpr, resultType) = returns[i]; + expressions.Add(returnExpr); + resultTypes.Add(resultType); + placeholdersBuilder.Add(CreatePlaceholderIfNecessary(returnExpr, resultType)); + } + + HashSet useSiteDiagnostics = null; + var placeholders = placeholdersBuilder.ToImmutableAndFree(); + TypeSymbol bestType = BestTypeInferrer.InferBestType(placeholders, walker._conversions, hadNullabilityMismatch: out _, ref useSiteDiagnostics); + + TypeSymbolWithAnnotations inferredType; + if ((object)bestType != null) + { + // Note: so long as we have a best type, we can proceed. + var bestTypeWithObliviousAnnotation = TypeSymbolWithAnnotations.Create(bestType); + ConversionsBase conversionsWithoutNullability = walker._conversions.WithNullability(false); for (int i = 0; i < n; i++) { - var conversion = conversionBuilder[i]; - var element = elementBuilder[i]; - var resultType = resultBuilder[i]; - ApplyConversion(element, element, conversion, elementType, resultType, checkConversion: true, fromExplicitCast: false, useLegacyWarnings: false, AssignmentKind.Assignment); + BoundExpression placeholder = placeholders[i]; + Conversion conversion = conversionsWithoutNullability.ClassifyConversionFromExpression(placeholder, bestType, ref useSiteDiagnostics); + resultTypes[i] = walker.ApplyConversion(placeholder, placeholder, conversion, bestTypeWithObliviousAnnotation, resultTypes[i], + checkConversion: false, fromExplicitCast: false, useLegacyWarnings: false, AssignmentKind.Return, + reportNestedWarnings: false, reportTopLevelWarnings: false); } + + // Set top-level nullability on inferred type + inferredType = TypeSymbolWithAnnotations.Create(bestType, BestTypeInferrer.GetNullableAnnotation(resultTypes)); + } + else + { + inferredType = default; } - resultBuilder.Free(); - elementBuilder.Free(); - _resultType = _invalidType; - return arrayType; + resultTypes.Free(); + expressions.Free(); + walker.Free(); + + return inferredType; } private static void GetArrayElements(BoundArrayInitialization node, ArrayBuilder builder) @@ -1977,21 +2057,21 @@ public override BoundNode VisitConditionalAccess(BoundConditionalAccess node) // If T is not nullable - result is nullable if U is nullable // If U is nullable - result is nullable // If U is not nullable, T is also not nullable - result is not nullable - // So, nullability of the result can be inferred from nullability of U, and the type of the result is U. + // So, nullability of the result can be inferred from nullability of U, and the type of the result is U. // U ?. T // If U is nullable - result is nullable // If U is not nullable, T is also not nullable - result is not nullable // If T is nullable, U is also nullable - result is nullable // If T is not nullable - result is nullable if U is nullable - // So, nullability of the result can be inferred from nullability of U, but the type of the result is T. + // So, nullability of the result can be inferred from nullability of U, but the type of the result is T. // At the moment we don't have a way to represent this correlation, result type is one type parameter, but - // nullability comes from another. + // nullability comes from another. // Ideally, we would want to have the following behavior: // U x = U?.T - no warning // T y = U?.T - a warning // But we can track the state only in the way when either both produce a warning, or none. - // It feels like it is reasonable to prefer the latter approach, i.e. produce no warnings + // It feels like it is reasonable to prefer the latter approach, i.e. produce no warnings // for both scenarios - no false diagnostics. resultAnnotation = NullableAnnotation.NotAnnotated; // Inherit nullability of U } @@ -2201,6 +2281,11 @@ NullableAnnotation getNullableAnnotation(BoundExpression expr, TypeSymbolWithAnn } } + /// + /// Placeholders are bound expressions with type and annotation, when available. + /// But for typeless expressions (such as `null` or `(null, null)` we hold onto the original bound expression, + /// as it will be useful for conversions from expression. + /// private static BoundExpression CreatePlaceholderIfNecessary(BoundExpression expr, TypeSymbolWithAnnotations type) { return type.IsNull ? @@ -2861,6 +2946,7 @@ private static (ParameterSymbol Parameter, TypeSymbolWithAnnotations Type) GetCo private MethodSymbol InferMethodTypeArguments(BoundCall node, MethodSymbol method, ImmutableArray arguments) { Debug.Assert(method.IsGenericMethod); + // https://github.com/dotnet/roslyn/issues/27961 OverloadResolution.IsMemberApplicableInNormalForm and // IsMemberApplicableInExpandedForm use the least overridden method. We need to do the same here. var definition = method.ConstructedFrom; @@ -2869,6 +2955,7 @@ private MethodSymbol InferMethodTypeArguments(BoundCall node, MethodSymbol metho { refKinds.AddRange(node.ArgumentRefKindsOpt); } + // https://github.com/dotnet/roslyn/issues/27961 Do we really need OverloadResolution.GetEffectiveParameterTypes? // Aren't we doing roughly the same calculations in GetCorrespondingParameter? OverloadResolution.GetEffectiveParameterTypes( @@ -2885,6 +2972,7 @@ private MethodSymbol InferMethodTypeArguments(BoundCall node, MethodSymbol metho expanded: node.Expanded, parameterTypes: out ImmutableArray parameterTypes, parameterRefKinds: out ImmutableArray parameterRefKinds); + refKinds.Free(); bool hadNullabilityMismatch; HashSet useSiteDiagnostics = null; @@ -2898,7 +2986,8 @@ private MethodSymbol InferMethodTypeArguments(BoundCall node, MethodSymbol metho arguments, out hadNullabilityMismatch, ref useSiteDiagnostics, - getNullableAnnotationOpt: expr => GetNullableAnnotation(expr)); + getTypeWithAnnotationOpt: s_getTypeWithSpeakableAnnotations); + if (!result.Success) { return method; @@ -2910,6 +2999,10 @@ private MethodSymbol InferMethodTypeArguments(BoundCall node, MethodSymbol metho return definition.Construct(result.InferredTypeArguments); } + // Note: although only tuple types can have nested types that are unspeakable, we make all nested types speakable to be sure + private readonly static Func s_getTypeWithSpeakableAnnotations = + (expr) => TypeSymbolWithAnnotations.Create(expr.Type, GetNullableAnnotation(expr)).SetSpeakableNullabilityForReferenceTypes(); + private ImmutableArray GetArgumentsForMethodTypeInference(ImmutableArray arguments, ImmutableArray argumentResults) { // https://github.com/dotnet/roslyn/issues/27961 MethodTypeInferrer.Infer relies @@ -3529,7 +3622,7 @@ private TypeSymbolWithAnnotations ApplyConversion( resultAnnotation = (operandType.IsNullableTypeOrTypeParameter() && operandType.GetValueNullableAnnotation().IsAnyNullable()) ? NullableAnnotation.Nullable : NullableAnnotation.NotNullable; break; } - else if (IsUnconstrainedTypeParameter(operandType.TypeSymbol)) + else if (IsTypeParameterDisallowingAnnotation(operandType.TypeSymbol)) { if (operandType.IsPossiblyNullableReferenceTypeTypeParameter() && !targetTypeWithNullability.IsPossiblyNullableReferenceTypeTypeParameter()) { @@ -3714,7 +3807,8 @@ private TypeSymbolWithAnnotations ApplyConversion( return resultType; } - private TypeSymbolWithAnnotations ClassifyAndApplyConversion(BoundExpression node, TypeSymbolWithAnnotations targetType, TypeSymbolWithAnnotations operandType, bool useLegacyWarnings, AssignmentKind assignmentKind, ParameterSymbol target) + private TypeSymbolWithAnnotations ClassifyAndApplyConversion(BoundExpression node, TypeSymbolWithAnnotations targetType, TypeSymbolWithAnnotations operandType, + bool useLegacyWarnings, AssignmentKind assignmentKind, ParameterSymbol target) { Debug.Assert((object)target != null || assignmentKind != AssignmentKind.Argument); HashSet useSiteDiagnostics = null; @@ -4493,6 +4587,7 @@ private TypeSymbolWithAnnotations InferResultNullabilityOfBinaryLogicalOperator( public override BoundNode VisitAwaitExpression(BoundAwaitExpression node) { var result = base.VisitAwaitExpression(node); + CheckPossibleNullReceiver(node.Expression); if (node.Type.IsValueType || node.HasErrors || (object)node.AwaitableInfo.GetResult == null) { SetResult(node); @@ -4903,18 +4998,21 @@ private void CheckPossibleNullReceiver(BoundExpression receiverOpt, bool checkNu return; } ReportSafetyDiagnostic(isValueType ? ErrorCode.WRN_NullableValueTypeMayBeNull : ErrorCode.WRN_NullReferenceReceiver, syntaxOpt ?? receiverOpt.Syntax); - int slot = MakeSlot(receiverOpt); - if (slot > 0) + + var slotBuilder = ArrayBuilder.GetInstance(); + GetSlotsToMarkAsNotNullable(receiverOpt, slotBuilder); + if (slotBuilder.Count > 0) { - this.State[slot] = NullableAnnotation.NotNullable; + MarkSlotsAsNotNullable(slotBuilder, ref State); } + slotBuilder.Free(); } } } private static bool IsNullabilityMismatch(TypeSymbolWithAnnotations type1, TypeSymbolWithAnnotations type2) { - // Note, when we are paying attention to nullability, we ignore insignificant differences and oblivious mismatch. + // Note, when we are paying attention to nullability, we ignore insignificant differences and oblivious mismatch. // See TypeCompareKind.UnknownNullableModifierMatchesAny and TypeCompareKind.IgnoreInsignificantNullableModifiersDifference return type1.Equals(type2, TypeCompareKind.AllIgnoreOptions) && !type1.Equals(type2, TypeCompareKind.AllIgnoreOptions & ~TypeCompareKind.IgnoreNullableModifiersForReferenceTypes); @@ -4922,7 +5020,7 @@ private static bool IsNullabilityMismatch(TypeSymbolWithAnnotations type1, TypeS private static bool IsNullabilityMismatch(TypeSymbol type1, TypeSymbol type2) { - // Note, when we are paying attention to nullability, we ignore insignificant differences and oblivious mismatch. + // Note, when we are paying attention to nullability, we ignore insignificant differences and oblivious mismatch. // See TypeCompareKind.UnknownNullableModifierMatchesAny and TypeCompareKind.IgnoreInsignificantNullableModifiersDifference return type1.Equals(type2, TypeCompareKind.AllIgnoreOptions) && !type1.Equals(type2, TypeCompareKind.AllIgnoreOptions & ~TypeCompareKind.IgnoreNullableModifiersForReferenceTypes); diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/UnassignedFieldsWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/UnassignedFieldsWalker.cs index 4df5aaca97cbc..e717fe4815c8d 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/UnassignedFieldsWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/UnassignedFieldsWalker.cs @@ -97,7 +97,7 @@ private void ReportUninitializedNonNullableReferenceTypeFields() { continue; } - if (!fieldType.NullableAnnotation.IsAnyNotNullable() && !fieldType.TypeSymbol.IsUnconstrainedTypeParameter()) + if (!fieldType.NullableAnnotation.IsAnyNotNullable() && !fieldType.TypeSymbol.IsTypeParameterDisallowingAnnotation()) { continue; } diff --git a/src/Compilers/CSharp/Portable/NullableContextOptions.cs b/src/Compilers/CSharp/Portable/NullableContextOptions.cs index bc861cf51e2d8..f5c3de6d3cc5d 100644 --- a/src/Compilers/CSharp/Portable/NullableContextOptions.cs +++ b/src/Compilers/CSharp/Portable/NullableContextOptions.cs @@ -23,5 +23,15 @@ public enum NullableContextOptions : byte /// Nullable annotation context is enabled and the nullable warning context is safeonly. /// SafeOnly, + + /// + /// Nullable annotation context is disabled and the nullable warning context is enabled. + /// + Warnings, + + /// + /// Nullable annotation context is disabled and the nullable warning context is safeonly. + /// + SafeOnlyWarnings, } } diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt index 4eaafea5bdeaf..7cf33f8d886d0 100644 --- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt @@ -6,6 +6,8 @@ Microsoft.CodeAnalysis.CSharp.NullableContextOptions Microsoft.CodeAnalysis.CSharp.NullableContextOptions.Disable = 0 -> Microsoft.CodeAnalysis.CSharp.NullableContextOptions Microsoft.CodeAnalysis.CSharp.NullableContextOptions.Enable = 1 -> Microsoft.CodeAnalysis.CSharp.NullableContextOptions Microsoft.CodeAnalysis.CSharp.NullableContextOptions.SafeOnly = 2 -> Microsoft.CodeAnalysis.CSharp.NullableContextOptions +Microsoft.CodeAnalysis.CSharp.NullableContextOptions.SafeOnlyWarnings = 4 -> Microsoft.CodeAnalysis.CSharp.NullableContextOptions +Microsoft.CodeAnalysis.CSharp.NullableContextOptions.Warnings = 3 -> Microsoft.CodeAnalysis.CSharp.NullableContextOptions Microsoft.CodeAnalysis.CSharp.Syntax.ForEachStatementSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken awaitKeyword, Microsoft.CodeAnalysis.SyntaxToken forEachKeyword, Microsoft.CodeAnalysis.SyntaxToken openParenToken, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.SyntaxToken inKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression, Microsoft.CodeAnalysis.SyntaxToken closeParenToken, Microsoft.CodeAnalysis.CSharp.Syntax.StatementSyntax statement) -> Microsoft.CodeAnalysis.CSharp.Syntax.ForEachStatementSyntax Microsoft.CodeAnalysis.CSharp.Syntax.ForEachVariableStatementSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken awaitKeyword, Microsoft.CodeAnalysis.SyntaxToken forEachKeyword, Microsoft.CodeAnalysis.SyntaxToken openParenToken, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax variable, Microsoft.CodeAnalysis.SyntaxToken inKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression, Microsoft.CodeAnalysis.SyntaxToken closeParenToken, Microsoft.CodeAnalysis.CSharp.Syntax.StatementSyntax statement) -> Microsoft.CodeAnalysis.CSharp.Syntax.ForEachVariableStatementSyntax Microsoft.CodeAnalysis.CSharp.Syntax.NullableDirectiveTriviaSyntax.NullableKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs index 18d60f7733b7a..0cc18d2da250d 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs @@ -100,13 +100,13 @@ private void AddNullableAnnotations(TypeSymbolWithAnnotations typeOpt) if (format.MiscellaneousOptions.IncludesOption(SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier) && !typeOpt.IsNullableType() && !typeOpt.IsValueType && (typeOpt.NullableAnnotation == NullableAnnotation.Annotated || - (typeOpt.NullableAnnotation == NullableAnnotation.Nullable && !typeOpt.TypeSymbol.IsUnconstrainedTypeParameter()))) + (typeOpt.NullableAnnotation == NullableAnnotation.Nullable && !typeOpt.TypeSymbol.IsTypeParameterDisallowingAnnotation()))) { AddPunctuation(SyntaxKind.QuestionToken); } else if (format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.IncludeNonNullableTypeModifier) && !typeOpt.IsValueType && - typeOpt.NullableAnnotation.IsAnyNotNullable() && !typeOpt.TypeSymbol.IsUnconstrainedTypeParameter()) + typeOpt.NullableAnnotation.IsAnyNotNullable() && !typeOpt.TypeSymbol.IsTypeParameterDisallowingAnnotation()) { AddPunctuation(SyntaxKind.ExclamationToken); } diff --git a/src/Compilers/CSharp/Portable/Symbols/ArrayTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ArrayTypeSymbol.cs index b358fab822cb0..850e076aecf04 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ArrayTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ArrayTypeSymbol.cs @@ -399,9 +399,9 @@ internal override bool ApplyNullableTransforms(byte defaultTransformFlag, Immuta return true; } - internal override TypeSymbol SetUnknownNullabilityForReferenceTypes() + internal override TypeSymbol SetNullabilityForReferenceTypes(Func transform) { - return WithElementType(ElementType.SetUnknownNullabilityForReferenceTypes()); + return WithElementType(transform(ElementType)); } internal override TypeSymbol MergeNullability(TypeSymbol other, VarianceKind variance, out bool hadNullabilityMismatch) diff --git a/src/Compilers/CSharp/Portable/Symbols/DynamicTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/DynamicTypeSymbol.cs index 5015cc502d070..8f00ff8aa5057 100644 --- a/src/Compilers/CSharp/Portable/Symbols/DynamicTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/DynamicTypeSymbol.cs @@ -227,7 +227,7 @@ internal override bool ApplyNullableTransforms(byte defaultTransformFlag, Immuta return true; } - internal override TypeSymbol SetUnknownNullabilityForReferenceTypes() + internal override TypeSymbol SetNullabilityForReferenceTypes(Func transform) { return this; } diff --git a/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs index 2527fe1202fcd..94fad4f28af1c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs @@ -813,7 +813,7 @@ internal override bool ApplyNullableTransforms(byte defaultTransformFlag, Immuta return true; } - internal override TypeSymbol SetUnknownNullabilityForReferenceTypes() + internal override TypeSymbol SetNullabilityForReferenceTypes(Func transform) { if (!IsGenericType) { @@ -827,7 +827,7 @@ internal override TypeSymbol SetUnknownNullabilityForReferenceTypes() for (int i = 0; i < allTypeArguments.Count; i++) { TypeSymbolWithAnnotations oldTypeArgument = allTypeArguments[i]; - TypeSymbolWithAnnotations newTypeArgument = oldTypeArgument.SetUnknownNullabilityForReferenceTypes(); + TypeSymbolWithAnnotations newTypeArgument = transform(oldTypeArgument); if (!oldTypeArgument.IsSameAs(newTypeArgument)) { allTypeArguments[i] = newTypeArgument; diff --git a/src/Compilers/CSharp/Portable/Symbols/PointerTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PointerTypeSymbol.cs index b9e8febdd0319..931cf2e487cbd 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PointerTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PointerTypeSymbol.cs @@ -269,9 +269,9 @@ internal override bool ApplyNullableTransforms(byte defaultTransformFlag, Immuta return true; } - internal override TypeSymbol SetUnknownNullabilityForReferenceTypes() + internal override TypeSymbol SetNullabilityForReferenceTypes(Func transform) { - return WithPointedAtType(PointedAtType.SetUnknownNullabilityForReferenceTypes()); + return WithPointedAtType(transform(PointedAtType)); } internal override TypeSymbol MergeNullability(TypeSymbol other, VarianceKind variance, out bool hadNullabilityMismatch) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs index f9ee44f3a0a04..bc6fb6f874982 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs @@ -623,7 +623,6 @@ private static void CheckOverrideMember(Symbol overridingMember, OverriddenOrHid break; } } - } if (!suppressError) diff --git a/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleTypeSymbol.cs index 32ac5b2a1c525..ece451f71663c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleTypeSymbol.cs @@ -1446,9 +1446,9 @@ internal override bool ApplyNullableTransforms(byte defaultTransformFlag, Immuta return false; } - internal override TypeSymbol SetUnknownNullabilityForReferenceTypes() + internal override TypeSymbol SetNullabilityForReferenceTypes(Func transform) { - var underlyingType = (NamedTypeSymbol)_underlyingType.SetUnknownNullabilityForReferenceTypes(); + var underlyingType = (NamedTypeSymbol)_underlyingType.SetNullabilityForReferenceTypes(transform); if ((object)underlyingType == _underlyingType) { return this; @@ -1497,7 +1497,7 @@ private static bool MergeUnderlyingTypeNullability( { TypeSymbolWithAnnotations typeArgumentA = typeArgumentsA[i]; TypeSymbolWithAnnotations typeArgumentB = typeArgumentsB[i]; - TypeSymbolWithAnnotations merged = typeArgumentA.MergeNullability(typeArgumentB, variance, out bool hadMismatch); + TypeSymbolWithAnnotations merged = mergeNullability(typeArgumentA, typeArgumentB, out bool hadMismatch); hadNullabilityMismatch |= hadMismatch; allTypeArguments.Add(merged); if (!typeArgumentA.IsSameAs(merged)) @@ -1514,8 +1514,37 @@ private static bool MergeUnderlyingTypeNullability( allTypeArguments.Free(); return haveChanges; + + // We use special rules for merging tuples, allowing nested types to remain unspeakable + TypeSymbolWithAnnotations mergeNullability(TypeSymbolWithAnnotations one, TypeSymbolWithAnnotations other, out bool nullabilityMismatch) + { + TypeSymbol typeSymbol = other.TypeSymbol; + NullableAnnotation nullableAnnotation = mergeNullableAnnotation(typeSymbol, one.NullableAnnotation, other.NullableAnnotation, out bool hadTopLevelMismatch); + TypeSymbol type = one.TypeSymbol.MergeNullability(typeSymbol, variance, out bool hadNestedMismatch); + Debug.Assert((object)type != null); + nullabilityMismatch = hadTopLevelMismatch | hadNestedMismatch; + return TypeSymbolWithAnnotations.Create(type, nullableAnnotation, one.CustomModifiers); + } + + NullableAnnotation mergeNullableAnnotation(TypeSymbol type, NullableAnnotation a, NullableAnnotation b, out bool nullabilityMismatch) + { + nullabilityMismatch = false; + switch (variance) + { + case VarianceKind.In: + return a.MeetForFlowAnalysisFinally(b); + case VarianceKind.Out: + return a.JoinForFlowAnalysisBranches(b, type, _IsPossiblyNullableReferenceTypeTypeParameterDelegate); + case VarianceKind.None: + return a.EnsureCompatibleForTuples(b, type, _IsPossiblyNullableReferenceTypeTypeParameterDelegate, out nullabilityMismatch); + default: + throw ExceptionUtilities.UnexpectedValue(variance); + } + } } + private readonly static Func _IsPossiblyNullableReferenceTypeTypeParameterDelegate = type => type.IsPossiblyNullableReferenceTypeTypeParameter(); + #region Use-Site Diagnostics internal override DiagnosticInfo GetUseSiteDiagnostic() diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs index 3f6dd2c34828c..6af80440700ec 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs @@ -677,7 +677,7 @@ internal override bool ApplyNullableTransforms(byte defaultTransformFlag, Immuta return true; } - internal override TypeSymbol SetUnknownNullabilityForReferenceTypes() + internal override TypeSymbol SetNullabilityForReferenceTypes(Func transform) { return this; } diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs index 2b0ef52fa4133..59e5ef54fb2f9 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs @@ -633,7 +633,23 @@ internal bool NeedsNullableAttribute() internal abstract bool ApplyNullableTransforms(byte defaultTransformFlag, ImmutableArray transforms, ref int position, out TypeSymbol result); - internal abstract TypeSymbol SetUnknownNullabilityForReferenceTypes(); + internal abstract TypeSymbol SetNullabilityForReferenceTypes(Func transform); + + internal TypeSymbol SetUnknownNullabilityForReferenceTypes() + { + return SetNullabilityForReferenceTypes(s_setUnknownNullability); + } + + private readonly static Func s_setUnknownNullability = + (type) => type.SetUnknownNullabilityForReferenceTypes(); + + internal TypeSymbol SetSpeakableNullabilityForReferenceTypes() + { + return SetNullabilityForReferenceTypes(s_setSpeakableNullability); + } + + private readonly static Func s_setSpeakableNullability = + (type) => type.SetSpeakableNullabilityForReferenceTypes(); /// /// Merges nested nullability from an otherwise identical type. diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index 87c37a54e4ea0..57d532ab344ff 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -41,16 +41,15 @@ public static bool CanBeConst(this TypeSymbol typeSymbol) return typeSymbol.IsReferenceType || typeSymbol.IsEnumType() || typeSymbol.SpecialType.CanBeConst(); } - // https://github.com/dotnet/roslyn/issues/30056: Should probably rename this method to have more specific name. - // At the moment it is used only for Nullable Reference Types feature and - // its implementation is specialized for this feature. - // T => true - // T where T : struct => false - // T where T : class => false - // T where T : class? => true - // T where T : IComparable => true - // T where T : IComparable? => true - public static bool IsUnconstrainedTypeParameter(this TypeSymbol type) + /// + /// T => true + /// T where T : struct => false + /// T where T : class => false + /// T where T : class? => true + /// T where T : IComparable => true + /// T where T : IComparable? => true + /// + public static bool IsTypeParameterDisallowingAnnotation(this TypeSymbol type) { if (type.TypeKind != TypeKind.TypeParameter) { @@ -62,12 +61,14 @@ public static bool IsUnconstrainedTypeParameter(this TypeSymbol type) return !typeParameter.IsValueType && !(typeParameter.IsReferenceType && typeParameter.IsNotNullableIfReferenceType == true); } - // T => true - // T where T : struct => false - // T where T : class => false - // T where T : class? => true - // T where T : IComparable => false - // T where T : IComparable? => true + /// + /// T => true + /// T where T : struct => false + /// T where T : class => false + /// T where T : class? => true + /// T where T : IComparable => false + /// T where T : IComparable? => true + /// public static bool IsPossiblyNullableReferenceTypeTypeParameter(this TypeSymbol type) { if (type.TypeKind != TypeKind.TypeParameter) diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolWithAnnotations.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolWithAnnotations.cs index ae50a9c839231..f889dad2e9310 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolWithAnnotations.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolWithAnnotations.cs @@ -32,34 +32,62 @@ public static bool IsAnyNotNullable(this NullableAnnotation annotation) return annotation == NullableAnnotation.NotAnnotated || annotation == NullableAnnotation.NotNullable; } + public static bool IsSpeakable(this NullableAnnotation annotation) + { + return annotation == NullableAnnotation.Unknown || + annotation == NullableAnnotation.NotAnnotated || + annotation == NullableAnnotation.Annotated; + } + /// - /// Join nullable annotations from the set of lower bounds for fixing a type parameter. + /// This method projects nullable annotations onto a smaller set that can be expressed in source. /// - public static NullableAnnotation JoinForFixingLowerBounds(this NullableAnnotation a, NullableAnnotation b) + public static NullableAnnotation AsSpeakable(this NullableAnnotation annotation, TypeSymbol type) { - if (a == b) + if (type is null && annotation == NullableAnnotation.Unknown) { - return a; + return default; } - if (a.IsAnyNullable() && b.IsAnyNullable()) + Debug.Assert((object)type != null); + switch (annotation) { - return NullableAnnotation.Annotated; - } + case NullableAnnotation.Unknown: + case NullableAnnotation.NotAnnotated: + case NullableAnnotation.Annotated: + return annotation; - // If nullability on both sides matches - result is that nullability (trivial cases like these are handled above) - // If either candidate is nullable - result is nullable - // Otherwise - result is "oblivious". + case NullableAnnotation.Nullable: + if (type.IsTypeParameterDisallowingAnnotation()) + { + return NullableAnnotation.NotAnnotated; + } + return NullableAnnotation.Annotated; - if (a.IsAnyNullable()) - { - Debug.Assert(!b.IsAnyNullable()); - return a; + case NullableAnnotation.NotNullable: + // Example of unspeakable types: + // - an unconstrained T which was null-tested already + // - a nullable value type which was null-tested already + // Note this projection is lossy for such types (we forget about the non-nullable state) + return NullableAnnotation.NotAnnotated; + + default: + throw ExceptionUtilities.UnexpectedValue(annotation); } + } - if (b.IsAnyNullable()) + /// + /// Join nullable annotations from the set of lower bounds for fixing a type parameter. + /// This uses the covariant merging rules. + /// + public static NullableAnnotation JoinForFixingLowerBounds(this NullableAnnotation a, NullableAnnotation b) + { + Debug.Assert(a.IsSpeakable()); + Debug.Assert(b.IsSpeakable()); + + if (a == NullableAnnotation.Annotated || b == NullableAnnotation.Annotated) { - return b; + return NullableAnnotation.Annotated; } if (a == NullableAnnotation.Unknown || b == NullableAnnotation.Unknown) @@ -67,11 +95,7 @@ public static NullableAnnotation JoinForFixingLowerBounds(this NullableAnnotatio return NullableAnnotation.Unknown; } - Debug.Assert((a == NullableAnnotation.NotAnnotated && b == NullableAnnotation.NotNullable) || - (b == NullableAnnotation.NotAnnotated && a == NullableAnnotation.NotNullable)); - return NullableAnnotation.NotAnnotated; // It is reasonable to settle on this value because the difference in annotations is either - // not significant for the type, or candidate corresponding to this value is possibly a - // nullable reference type type parameter and nullable should win. + return NullableAnnotation.NotAnnotated; } /// @@ -136,35 +160,24 @@ public static NullableAnnotation JoinForFlowAnalysisBranches(this NullableAnn /// /// Meet two nullable annotations for computing the nullable annotation of a type parameter from upper bounds. + /// This uses the contravariant merging rules. /// public static NullableAnnotation MeetForFixingUpperBounds(this NullableAnnotation a, NullableAnnotation b) { - if (a == b) - { - return a; - } + Debug.Assert(a.IsSpeakable()); + Debug.Assert(b.IsSpeakable()); - if (a.IsAnyNullable() && b.IsAnyNullable()) - { - return NullableAnnotation.Annotated; - } - - // If nullability on both sides matches - result is that nullability (trivial cases like these are handled above) - // If either candidate is not nullable - result is not nullable - // Otherwise - result is "oblivious". - - if (a == NullableAnnotation.NotNullable || b == NullableAnnotation.NotNullable) + if (a == NullableAnnotation.NotAnnotated || b == NullableAnnotation.NotAnnotated) { - return NullableAnnotation.NotNullable; + return NullableAnnotation.NotAnnotated; } - if (a == NullableAnnotation.NotAnnotated || b == NullableAnnotation.NotAnnotated) + if (a == NullableAnnotation.Unknown || b == NullableAnnotation.Unknown) { - return NullableAnnotation.NotAnnotated; + return NullableAnnotation.Unknown; } - Debug.Assert(a == NullableAnnotation.Unknown || b == NullableAnnotation.Unknown); - return NullableAnnotation.Unknown; + return NullableAnnotation.Annotated; } /// @@ -197,8 +210,36 @@ public static NullableAnnotation MeetForFlowAnalysisFinally(this NullableAnnotat /// Check that two nullable annotations are "compatible", which means they could be the same. Return the /// nullable annotation to be used as a result. Also returns through /// whether the caller should report a warning because there was an actual mismatch (e.g. nullable vs non-nullable). + /// This uses the invariant merging rules. + /// + public static NullableAnnotation EnsureCompatible(this NullableAnnotation a, NullableAnnotation b, out bool hadNullabilityMismatch) + { + Debug.Assert(a.IsSpeakable()); + Debug.Assert(b.IsSpeakable()); + + hadNullabilityMismatch = (a == NullableAnnotation.Annotated && b == NullableAnnotation.NotAnnotated) || + (a == NullableAnnotation.NotAnnotated && b == NullableAnnotation.Annotated); + + if (a == NullableAnnotation.NotAnnotated || b == NullableAnnotation.NotAnnotated) + { + return NullableAnnotation.NotAnnotated; + } + + if (a == NullableAnnotation.Annotated || b == NullableAnnotation.Annotated) + { + return NullableAnnotation.Annotated; + } + + return NullableAnnotation.Unknown; + } + + /// + /// Check that two nullable annotations are "compatible", which means they could be the same. Return the + /// nullable annotation to be used as a result. Also returns through + /// whether the caller should report a warning because there was an actual mismatch (e.g. nullable vs non-nullable). + /// This method can handle unspeakable types (for merging tuple types). /// - public static NullableAnnotation EnsureCompatible(this NullableAnnotation a, NullableAnnotation b, T type, Func isPossiblyNullableReferenceTypeTypeParameter, out bool hadNullabilityMismatch) + public static NullableAnnotation EnsureCompatibleForTuples(this NullableAnnotation a, NullableAnnotation b, T type, Func isPossiblyNullableReferenceTypeTypeParameter, out bool hadNullabilityMismatch) { hadNullabilityMismatch = false; if (a == b) @@ -213,7 +254,7 @@ public static NullableAnnotation EnsureCompatible(this NullableAnnotation a, // If nullability on both sides matches - result is that nullability (trivial cases like these are handled above) // If either candidate is "oblivious" - result is the nullability of the other candidate - // Otherwise - we declare a mismatch and result is not nullable. + // Otherwise - we declare a mismatch and result is not nullable. if (a == NullableAnnotation.Unknown) { @@ -334,6 +375,25 @@ private TypeSymbolWithAnnotations(TypeSymbol defaultType, NullableAnnotation nul _extensions = extensions; } + public TypeSymbolWithAnnotations AsSpeakable() + { + if (IsNull) + { + return default; + } + + TypeSymbol typeSymbol = this.TypeSymbol; + var annotation = this.NullableAnnotation; + var speakableAnnotation = annotation.AsSpeakable(typeSymbol); + + if (annotation == speakableAnnotation) + { + return this; + } + + return Create(typeSymbol, speakableAnnotation, this.CustomModifiers); + } + public override string ToString() => TypeSymbol.ToString(); public string Name => TypeSymbol.Name; public SymbolKind Kind => TypeSymbol.Kind; @@ -479,8 +539,11 @@ public TypeSymbolWithAnnotations SetIsAnnotated(CSharpCompilation compilation) /// internal TypeSymbolWithAnnotations MergeNullability(TypeSymbolWithAnnotations other, VarianceKind variance, out bool hadNullabilityMismatch) { + Debug.Assert(this.NullableAnnotation.IsSpeakable()); + Debug.Assert(other.NullableAnnotation.IsSpeakable()); + TypeSymbol typeSymbol = other.TypeSymbol; - NullableAnnotation nullableAnnotation = MergeNullableAnnotation(typeSymbol, NullableAnnotation, other.NullableAnnotation, variance, out bool hadTopLevelMismatch); + NullableAnnotation nullableAnnotation = MergeNullableAnnotation(this.NullableAnnotation, other.NullableAnnotation, variance, out bool hadTopLevelMismatch); TypeSymbol type = TypeSymbol.MergeNullability(typeSymbol, variance, out bool hadNestedMismatch); Debug.Assert((object)type != null); hadNullabilityMismatch = hadTopLevelMismatch | hadNestedMismatch; @@ -491,8 +554,11 @@ internal TypeSymbolWithAnnotations MergeNullability(TypeSymbolWithAnnotations ot /// Merges nullability. /// is true if there was conflict. /// - private static NullableAnnotation MergeNullableAnnotation(TypeSymbol type, NullableAnnotation a, NullableAnnotation b, VarianceKind variance, out bool hadNullabilityMismatch) + private static NullableAnnotation MergeNullableAnnotation(NullableAnnotation a, NullableAnnotation b, VarianceKind variance, out bool hadNullabilityMismatch) { + Debug.Assert(a.IsSpeakable()); + Debug.Assert(b.IsSpeakable()); + hadNullabilityMismatch = false; switch (variance) { @@ -501,14 +567,12 @@ private static NullableAnnotation MergeNullableAnnotation(TypeSymbol type, Nulla case VarianceKind.Out: return a.JoinForFixingLowerBounds(b); case VarianceKind.None: - return a.EnsureCompatible(b, type, _IsPossiblyNullableReferenceTypeTypeParameterDelegate, out hadNullabilityMismatch); + return a.EnsureCompatible(b, out hadNullabilityMismatch); default: throw ExceptionUtilities.UnexpectedValue(variance); } } - private readonly static Func _IsPossiblyNullableReferenceTypeTypeParameterDelegate = type => type.IsPossiblyNullableReferenceTypeTypeParameter(); - public TypeSymbolWithAnnotations WithModifiers(ImmutableArray customModifiers) => _extensions.WithModifiers(this, customModifiers); @@ -560,13 +624,13 @@ public string ToDisplayString(SymbolDisplayFormat format = null) if (format.MiscellaneousOptions.IncludesOption(SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier) && !IsNullableType() && !IsValueType && (NullableAnnotation == NullableAnnotation.Annotated || - (NullableAnnotation == NullableAnnotation.Nullable && !TypeSymbol.IsUnconstrainedTypeParameter()))) + (NullableAnnotation == NullableAnnotation.Nullable && !TypeSymbol.IsTypeParameterDisallowingAnnotation()))) { return str + "?"; } else if (format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.IncludeNonNullableTypeModifier) && !IsValueType && - NullableAnnotation.IsAnyNotNullable() && !TypeSymbol.IsUnconstrainedTypeParameter()) + NullableAnnotation.IsAnyNotNullable() && !TypeSymbol.IsTypeParameterDisallowingAnnotation()) { return str + "!"; } @@ -747,7 +811,7 @@ internal TypeSymbolWithAnnotations SubstituteTypeCore(AbstractTypeMap typeMap, b } else if (NullableAnnotation != NullableAnnotation.Unknown) { - if (!typeSymbol.IsUnconstrainedTypeParameter()) + if (!typeSymbol.IsTypeParameterDisallowingAnnotation()) { newAnnotation = NullableAnnotation; } @@ -936,6 +1000,33 @@ public TypeSymbolWithAnnotations SetUnknownNullabilityForReferenceTypes() return this; } + public TypeSymbolWithAnnotations SetSpeakableNullabilityForReferenceTypes() + { + if (IsNull) + { + return default; + } + + var newTypeSymbol = TypeSymbol.SetSpeakableNullabilityForReferenceTypes(); + + if (!NullableAnnotation.IsSpeakable()) + { + if (newTypeSymbol.IsValueType) + { + return Create(newTypeSymbol, customModifiers: CustomModifiers); + } + + return CreateNonLazyType(newTypeSymbol, NullableAnnotation.AsSpeakable(newTypeSymbol), CustomModifiers); + } + + if ((object)newTypeSymbol != TypeSymbol) + { + return WithTypeAndModifiers(newTypeSymbol, CustomModifiers); + } + + return this; + } + #pragma warning disable CS0809 [Obsolete("Unsupported", error: true)] public override bool Equals(object other) diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index fbc83a3b9b5ff..312f5eccf40ff 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -43,8 +43,8 @@ - Invalid option '{0}' for /nullable; must be 'disable', 'enable' or 'safeonly' - Invalid option '{0}' for /nullable; must be 'disable', 'enable' or 'safeonly' + Invalid option '{0}' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' + Invalid option '{0}' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' @@ -8104,8 +8104,8 @@ Pokud chcete odstranit toto varování, můžete místo toho použít /reference `latest` (latest version, including minor versions), or specific versions like `6` or `7.1` -nullable[+|-] Specify nullable context option enable|disable. --nullable:{enable|disable|safeonly} - Specify nullable context option enable|disable|safeonly. +-nullable:{enable|disable|safeonly|warnings|safeonlywarnings} + Specify nullable context option enable|disable|safeonly|warnings|safeonlywarnings. - SECURITY - -delaysign[+|-] Delay-sign the assembly using only the public diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index b963b542d1b27..2890b6220e2d7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -43,8 +43,8 @@ - Invalid option '{0}' for /nullable; must be 'disable', 'enable' or 'safeonly' - Invalid option '{0}' for /nullable; must be 'disable', 'enable' or 'safeonly' + Invalid option '{0}' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' + Invalid option '{0}' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' @@ -8104,8 +8104,8 @@ Um die Warnung zu beheben, können Sie stattdessen /reference verwenden (Einbett `latest` (latest version, including minor versions), or specific versions like `6` or `7.1` -nullable[+|-] Specify nullable context option enable|disable. --nullable:{enable|disable|safeonly} - Specify nullable context option enable|disable|safeonly. +-nullable:{enable|disable|safeonly|warnings|safeonlywarnings} + Specify nullable context option enable|disable|safeonly|warnings|safeonlywarnings. - SECURITY - -delaysign[+|-] Delay-sign the assembly using only the public diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index d1be9177a6fb7..2cc80a7403a08 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -43,8 +43,8 @@ - Invalid option '{0}' for /nullable; must be 'disable', 'enable' or 'safeonly' - Invalid option '{0}' for /nullable; must be 'disable', 'enable' or 'safeonly' + Invalid option '{0}' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' + Invalid option '{0}' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' @@ -8104,8 +8104,8 @@ Para eliminar la advertencia puede usar /reference (establezca la propiedad Embe `latest` (latest version, including minor versions), or specific versions like `6` or `7.1` -nullable[+|-] Specify nullable context option enable|disable. --nullable:{enable|disable|safeonly} - Specify nullable context option enable|disable|safeonly. +-nullable:{enable|disable|safeonly|warnings|safeonlywarnings} + Specify nullable context option enable|disable|safeonly|warnings|safeonlywarnings. - SECURITY - -delaysign[+|-] Delay-sign the assembly using only the public diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 8581bb270c8bb..b022450528b3a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -43,8 +43,8 @@ - Invalid option '{0}' for /nullable; must be 'disable', 'enable' or 'safeonly' - Invalid option '{0}' for /nullable; must be 'disable', 'enable' or 'safeonly' + Invalid option '{0}' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' + Invalid option '{0}' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' @@ -8104,8 +8104,8 @@ Pour supprimer l'avertissement, vous pouvez utiliser la commande /reference (dé `latest` (latest version, including minor versions), or specific versions like `6` or `7.1` -nullable[+|-] Specify nullable context option enable|disable. --nullable:{enable|disable|safeonly} - Specify nullable context option enable|disable|safeonly. +-nullable:{enable|disable|safeonly|warnings|safeonlywarnings} + Specify nullable context option enable|disable|safeonly|warnings|safeonlywarnings. - SECURITY - -delaysign[+|-] Delay-sign the assembly using only the public diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 55a4fc557e3e7..04642684b0193 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -43,8 +43,8 @@ - Invalid option '{0}' for /nullable; must be 'disable', 'enable' or 'safeonly' - Invalid option '{0}' for /nullable; must be 'disable', 'enable' or 'safeonly' + Invalid option '{0}' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' + Invalid option '{0}' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' @@ -8104,8 +8104,8 @@ Per rimuovere l'avviso, è invece possibile usare /reference (impostare la propr `latest` (latest version, including minor versions), or specific versions like `6` or `7.1` -nullable[+|-] Specify nullable context option enable|disable. --nullable:{enable|disable|safeonly} - Specify nullable context option enable|disable|safeonly. +-nullable:{enable|disable|safeonly|warnings|safeonlywarnings} + Specify nullable context option enable|disable|safeonly|warnings|safeonlywarnings. - SECURITY - -delaysign[+|-] Delay-sign the assembly using only the public diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index d706588ec2a79..932b81eab01af 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -43,8 +43,8 @@ - Invalid option '{0}' for /nullable; must be 'disable', 'enable' or 'safeonly' - Invalid option '{0}' for /nullable; must be 'disable', 'enable' or 'safeonly' + Invalid option '{0}' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' + Invalid option '{0}' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' @@ -8104,8 +8104,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ `latest` (latest version, including minor versions), or specific versions like `6` or `7.1` -nullable[+|-] Specify nullable context option enable|disable. --nullable:{enable|disable|safeonly} - Specify nullable context option enable|disable|safeonly. +-nullable:{enable|disable|safeonly|warnings|safeonlywarnings} + Specify nullable context option enable|disable|safeonly|warnings|safeonlywarnings. - SECURITY - -delaysign[+|-] Delay-sign the assembly using only the public diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 382d267a0da0f..bccf585c60b96 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -43,8 +43,8 @@ - Invalid option '{0}' for /nullable; must be 'disable', 'enable' or 'safeonly' - Invalid option '{0}' for /nullable; must be 'disable', 'enable' or 'safeonly' + Invalid option '{0}' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' + Invalid option '{0}' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' @@ -8104,8 +8104,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ `latest` (latest version, including minor versions), or specific versions like `6` or `7.1` -nullable[+|-] Specify nullable context option enable|disable. --nullable:{enable|disable|safeonly} - Specify nullable context option enable|disable|safeonly. +-nullable:{enable|disable|safeonly|warnings|safeonlywarnings} + Specify nullable context option enable|disable|safeonly|warnings|safeonlywarnings. - SECURITY - -delaysign[+|-] Delay-sign the assembly using only the public diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index f36c69b022c94..9a756e18a44d0 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -43,8 +43,8 @@ - Invalid option '{0}' for /nullable; must be 'disable', 'enable' or 'safeonly' - Invalid option '{0}' for /nullable; must be 'disable', 'enable' or 'safeonly' + Invalid option '{0}' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' + Invalid option '{0}' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' @@ -8104,8 +8104,8 @@ Aby usunąć ostrzeżenie, możesz zamiast tego użyć opcji /reference (ustaw w `latest` (latest version, including minor versions), or specific versions like `6` or `7.1` -nullable[+|-] Specify nullable context option enable|disable. --nullable:{enable|disable|safeonly} - Specify nullable context option enable|disable|safeonly. +-nullable:{enable|disable|safeonly|warnings|safeonlywarnings} + Specify nullable context option enable|disable|safeonly|warnings|safeonlywarnings. - SECURITY - -delaysign[+|-] Delay-sign the assembly using only the public diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 638ece6968fad..9819f3a041649 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -43,8 +43,8 @@ - Invalid option '{0}' for /nullable; must be 'disable', 'enable' or 'safeonly' - Invalid option '{0}' for /nullable; must be 'disable', 'enable' or 'safeonly' + Invalid option '{0}' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' + Invalid option '{0}' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' @@ -8104,8 +8104,8 @@ Para incorporar informações de tipo de interoperabilidade para os dois assembl `latest` (latest version, including minor versions), or specific versions like `6` or `7.1` -nullable[+|-] Specify nullable context option enable|disable. --nullable:{enable|disable|safeonly} - Specify nullable context option enable|disable|safeonly. +-nullable:{enable|disable|safeonly|warnings|safeonlywarnings} + Specify nullable context option enable|disable|safeonly|warnings|safeonlywarnings. - SECURITY - -delaysign[+|-] Delay-sign the assembly using only the public diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index a41c681a90841..83a3d8b160c0c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -43,8 +43,8 @@ - Invalid option '{0}' for /nullable; must be 'disable', 'enable' or 'safeonly' - Invalid option '{0}' for /nullable; must be 'disable', 'enable' or 'safeonly' + Invalid option '{0}' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' + Invalid option '{0}' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' @@ -8104,8 +8104,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ `latest` (latest version, including minor versions), or specific versions like `6` or `7.1` -nullable[+|-] Specify nullable context option enable|disable. --nullable:{enable|disable|safeonly} - Specify nullable context option enable|disable|safeonly. +-nullable:{enable|disable|safeonly|warnings|safeonlywarnings} + Specify nullable context option enable|disable|safeonly|warnings|safeonlywarnings. - SECURITY - -delaysign[+|-] Delay-sign the assembly using only the public diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 105e3f402ec31..1da32eb881999 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -43,8 +43,8 @@ - Invalid option '{0}' for /nullable; must be 'disable', 'enable' or 'safeonly' - Invalid option '{0}' for /nullable; must be 'disable', 'enable' or 'safeonly' + Invalid option '{0}' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' + Invalid option '{0}' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' @@ -8104,8 +8104,8 @@ Uyarıyı kaldırmak için, /reference kullanabilirsiniz (Birlikte Çalışma T `latest` (latest version, including minor versions), or specific versions like `6` or `7.1` -nullable[+|-] Specify nullable context option enable|disable. --nullable:{enable|disable|safeonly} - Specify nullable context option enable|disable|safeonly. +-nullable:{enable|disable|safeonly|warnings|safeonlywarnings} + Specify nullable context option enable|disable|safeonly|warnings|safeonlywarnings. - SECURITY - -delaysign[+|-] Delay-sign the assembly using only the public diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 11467d7641e60..98ce4869a3774 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -43,8 +43,8 @@ - Invalid option '{0}' for /nullable; must be 'disable', 'enable' or 'safeonly' - Invalid option '{0}' for /nullable; must be 'disable', 'enable' or 'safeonly' + Invalid option '{0}' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' + Invalid option '{0}' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' @@ -8104,8 +8104,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ `latest` (latest version, including minor versions), or specific versions like `6` or `7.1` -nullable[+|-] Specify nullable context option enable|disable. --nullable:{enable|disable|safeonly} - Specify nullable context option enable|disable|safeonly. +-nullable:{enable|disable|safeonly|warnings|safeonlywarnings} + Specify nullable context option enable|disable|safeonly|warnings|safeonlywarnings. - SECURITY - -delaysign[+|-] Delay-sign the assembly using only the public diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index c69eaf2954a88..653d24a9efd3b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -43,8 +43,8 @@ - Invalid option '{0}' for /nullable; must be 'disable', 'enable' or 'safeonly' - Invalid option '{0}' for /nullable; must be 'disable', 'enable' or 'safeonly' + Invalid option '{0}' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' + Invalid option '{0}' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' @@ -8104,8 +8104,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ `latest` (latest version, including minor versions), or specific versions like `6` or `7.1` -nullable[+|-] Specify nullable context option enable|disable. --nullable:{enable|disable|safeonly} - Specify nullable context option enable|disable|safeonly. +-nullable:{enable|disable|safeonly|warnings|safeonlywarnings} + Specify nullable context option enable|disable|safeonly|warnings|safeonlywarnings. - SECURITY - -delaysign[+|-] Delay-sign the assembly using only the public diff --git a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs index 33f5340d50c29..bf99c98668117 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs +++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs @@ -1967,6 +1967,18 @@ public void Embed() new[] { "a.cs", "b.cs" }.Select(f => Path.Combine(WorkingDirectory, f)), parsedArgs.EmbeddedFiles.Select(f => f.Path)); + parsedArgs = DefaultParse(new[] { "/embed:a.cs,b.cs", "/debug:portable", "a.cs", "b.cs", "c.cs" }, WorkingDirectory); + parsedArgs.Errors.Verify(); + AssertEx.Equal( + new[] { "a.cs", "b.cs" }.Select(f => Path.Combine(WorkingDirectory, f)), + parsedArgs.EmbeddedFiles.Select(f => f.Path)); + + parsedArgs = DefaultParse(new[] { @"/embed:""a,b.cs""", "/debug:portable", "a,b.cs", "c.cs" }, WorkingDirectory); + parsedArgs.Errors.Verify(); + AssertEx.Equal( + new[] { "a,b.cs" }.Select(f => Path.Combine(WorkingDirectory, f)), + parsedArgs.EmbeddedFiles.Select(f => f.Path)); + parsedArgs = DefaultParse(new[] { "/embed:a.txt", "/embed", "/debug:portable", "a.cs", "b.cs", "c.cs" }, WorkingDirectory); parsedArgs.Errors.Verify(); ; AssertEx.Equal( @@ -4156,7 +4168,7 @@ public void Nullable() parsedArgs = DefaultParse(new[] { @"/nullable:yes", "a.cs" }, WorkingDirectory); parsedArgs.Errors.Verify( - // error CS8636: Invalid option 'yes' for /nullable; must be 'disable', 'enable' or 'safeonly' + // error CS8636: Invalid option 'yes' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("yes").WithLocation(1, 1) ); Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions); @@ -4200,7 +4212,7 @@ public void Nullable() parsedArgs = DefaultParse(new[] { @"/nullable:yes", "/langversion:8", "a.cs" }, WorkingDirectory); parsedArgs.Errors.Verify( - // error CS8636: Invalid option 'yes' for /nullable; must be 'disable', 'enable' or 'safeonly' + // error CS8636: Invalid option 'yes' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("yes").WithLocation(1, 1) ); Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions); @@ -4238,7 +4250,7 @@ public void Nullable() parsedArgs = DefaultParse(new[] { @"/nullable-", @"/nullable:YES", "/langversion:8", "a.cs" }, WorkingDirectory); parsedArgs.Errors.Verify( - // error CS8636: Invalid option 'YES' for /nullable; must be 'disable', 'enable' or 'safeonly' + // error CS8636: Invalid option 'YES' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("YES").WithLocation(1, 1) ); Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions); @@ -4276,7 +4288,7 @@ public void Nullable() parsedArgs = DefaultParse(new[] { @"/nullable+", @"/nullable:YES", "/langversion:8", "a.cs" }, WorkingDirectory); parsedArgs.Errors.Verify( - // error CS8636: Invalid option 'YES' for /nullable; must be 'disable', 'enable' or 'safeonly' + // error CS8636: Invalid option 'YES' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("YES").WithLocation(1, 1) ); Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions); @@ -4314,7 +4326,7 @@ public void Nullable() parsedArgs = DefaultParse(new[] { @"/nullable:safeonly", @"/nullable:YES", "/langversion:8", "a.cs" }, WorkingDirectory); parsedArgs.Errors.Verify( - // error CS8636: Invalid option 'YES' for /nullable; must be 'disable', 'enable' or 'safeonly' + // error CS8636: Invalid option 'YES' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("YES").WithLocation(1, 1) ); Assert.Equal(NullableContextOptions.SafeOnly, parsedArgs.CompilationOptions.NullableContextOptions); @@ -4340,7 +4352,7 @@ public void Nullable() parsedArgs = DefaultParse(new[] { @"/nullable:yeS", "/langversion:7.3", "a.cs" }, WorkingDirectory); parsedArgs.Errors.Verify( - // error CS8636: Invalid option 'yeS' for /nullable; must be 'disable', 'enable' or 'safeonly' + // error CS8636: Invalid option 'yeS' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("yeS").WithLocation(1, 1) ); Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions); @@ -4395,24 +4407,152 @@ public void Nullable() parsedArgs = DefaultParse(new[] { @"/nullable:\""enable\""", "/langversion:8", "a.cs" }, WorkingDirectory); parsedArgs.Errors.Verify( - // error CS8636: Invalid option '"enable"' for /nullable; must be 'disable', 'enable' or 'safeonly' + // error CS8636: Invalid option '"enable"' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments(@"""enable""").WithLocation(1, 1) ); Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions); parsedArgs = DefaultParse(new[] { @"/nullable:\\disable\\", "/langversion:8", "a.cs" }, WorkingDirectory); parsedArgs.Errors.Verify( - // error CS8636: Invalid option '\\disable\\' for /nullable; must be 'disable', 'enable' or 'safeonly' + // error CS8636: Invalid option '\\disable\\' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments(@"\\disable\\").WithLocation(1, 1) ); Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions); parsedArgs = DefaultParse(new[] { @"/nullable:\\""enable\\""", "/langversion:8", "a.cs" }, WorkingDirectory); parsedArgs.Errors.Verify( - // error CS8636: Invalid option '\enable\' for /nullable; must be 'disable', 'enable' or 'safeonly' + // error CS8636: Invalid option '\enable\' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments(@"\enable\").WithLocation(1, 1) ); Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions); + + parsedArgs = DefaultParse(new[] { @"/nullable:safeonlywarnings", "a.cs" }, WorkingDirectory); + parsedArgs.Errors.Verify( + // error CS8630: Invalid 'nullable' value: 'SafeOnlyWarnings' for C# 7.0. Please use language version 8.0 or greater. + Diagnostic(ErrorCode.ERR_NullableOptionNotAvailable).WithArguments("nullable", "SafeOnlyWarnings", "7.0", "8.0").WithLocation(1, 1) + ); + Assert.Equal(NullableContextOptions.SafeOnlyWarnings, parsedArgs.CompilationOptions.NullableContextOptions); + + parsedArgs = DefaultParse(new[] { @"/nullable:SafeonlyWarnings", "/langversion:8", "a.cs" }, WorkingDirectory); + parsedArgs.Errors.Verify(); + Assert.Equal(NullableContextOptions.SafeOnlyWarnings, parsedArgs.CompilationOptions.NullableContextOptions); + + parsedArgs = DefaultParse(new[] { @"/nullable-", @"/nullable:safeonlyWarnings", "/langversion:8", "a.cs" }, WorkingDirectory); + parsedArgs.Errors.Verify(); + Assert.Equal(NullableContextOptions.SafeOnlyWarnings, parsedArgs.CompilationOptions.NullableContextOptions); + + parsedArgs = DefaultParse(new[] { @"/nullable+", @"/nullable:safeonlyWarnings", "/langversion:8", "a.cs" }, WorkingDirectory); + parsedArgs.Errors.Verify(); + Assert.Equal(NullableContextOptions.SafeOnlyWarnings, parsedArgs.CompilationOptions.NullableContextOptions); + + parsedArgs = DefaultParse(new[] { @"/nullable:safeonlyWarnings", @"/nullable-", "/langversion:8", "a.cs" }, WorkingDirectory); + parsedArgs.Errors.Verify(); + Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions); + + parsedArgs = DefaultParse(new[] { @"/nullable:safeonlyWarnings", @"/nullable", "/langversion:8", "a.cs" }, WorkingDirectory); + parsedArgs.Errors.Verify(); + Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions); + + parsedArgs = DefaultParse(new[] { @"/nullable:safeonlyWarnings", @"/nullable+", "/langversion:8", "a.cs" }, WorkingDirectory); + parsedArgs.Errors.Verify(); + Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions); + + parsedArgs = DefaultParse(new[] { @"/nullable:safeonlyWarnings", @"/nullable:", "/langversion:8", "a.cs" }, WorkingDirectory); + parsedArgs.Errors.Verify( + // error CS2006: Command-line syntax error: Missing '' for 'nullable' option + Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("", "nullable").WithLocation(1, 1) + ); + Assert.Equal(NullableContextOptions.SafeOnlyWarnings, parsedArgs.CompilationOptions.NullableContextOptions); + + parsedArgs = DefaultParse(new[] { @"/nullable:safeonlyWarnings", @"/nullable:YES", "/langversion:8", "a.cs" }, WorkingDirectory); + parsedArgs.Errors.Verify( + // error CS8636: Invalid option 'YES' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' + Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("YES").WithLocation(1, 1) + ); + Assert.Equal(NullableContextOptions.SafeOnlyWarnings, parsedArgs.CompilationOptions.NullableContextOptions); + + parsedArgs = DefaultParse(new[] { @"/nullable:safeonlyWarnings", @"/nullable:disable", "/langversion:8", "a.cs" }, WorkingDirectory); + parsedArgs.Errors.Verify(); + Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions); + + parsedArgs = DefaultParse(new[] { @"/nullable:safeonlyWarnings", @"/nullable:enable", "/langversion:8", "a.cs" }, WorkingDirectory); + parsedArgs.Errors.Verify(); + Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions); + + parsedArgs = DefaultParse(new[] { @"/nullable:safeonlyWarnings", @"/nullable:safeonlyWarnings", "/langversion:8", "a.cs" }, WorkingDirectory); + parsedArgs.Errors.Verify(); + Assert.Equal(NullableContextOptions.SafeOnlyWarnings, parsedArgs.CompilationOptions.NullableContextOptions); + + parsedArgs = DefaultParse(new[] { @"/nullable:safeonlyWarnings", "/langversion:7.3", "a.cs" }, WorkingDirectory); + parsedArgs.Errors.Verify( + // error CS8630: Invalid 'nullable' value: 'SafeonlyWarnings' for C# 7.3. Please use language version 8.0 or greater. + Diagnostic(ErrorCode.ERR_NullableOptionNotAvailable).WithArguments("nullable", "SafeOnlyWarnings", "7.3", "8.0").WithLocation(1, 1) + ); + Assert.Equal(NullableContextOptions.SafeOnlyWarnings, parsedArgs.CompilationOptions.NullableContextOptions); + + parsedArgs = DefaultParse(new[] { @"/nullable:warnings", "a.cs" }, WorkingDirectory); + parsedArgs.Errors.Verify( + // error CS8630: Invalid 'nullable' value: 'Warnings' for C# 7.0. Please use language version 8.0 or greater. + Diagnostic(ErrorCode.ERR_NullableOptionNotAvailable).WithArguments("nullable", "Warnings", "7.0", "8.0").WithLocation(1, 1) + ); + Assert.Equal(NullableContextOptions.Warnings, parsedArgs.CompilationOptions.NullableContextOptions); + + parsedArgs = DefaultParse(new[] { @"/nullable:Warnings", "/langversion:8", "a.cs" }, WorkingDirectory); + parsedArgs.Errors.Verify(); + Assert.Equal(NullableContextOptions.Warnings, parsedArgs.CompilationOptions.NullableContextOptions); + + parsedArgs = DefaultParse(new[] { @"/nullable-", @"/nullable:Warnings", "/langversion:8", "a.cs" }, WorkingDirectory); + parsedArgs.Errors.Verify(); + Assert.Equal(NullableContextOptions.Warnings, parsedArgs.CompilationOptions.NullableContextOptions); + + parsedArgs = DefaultParse(new[] { @"/nullable+", @"/nullable:Warnings", "/langversion:8", "a.cs" }, WorkingDirectory); + parsedArgs.Errors.Verify(); + Assert.Equal(NullableContextOptions.Warnings, parsedArgs.CompilationOptions.NullableContextOptions); + + parsedArgs = DefaultParse(new[] { @"/nullable:Warnings", @"/nullable-", "/langversion:8", "a.cs" }, WorkingDirectory); + parsedArgs.Errors.Verify(); + Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions); + + parsedArgs = DefaultParse(new[] { @"/nullable:Warnings", @"/nullable", "/langversion:8", "a.cs" }, WorkingDirectory); + parsedArgs.Errors.Verify(); + Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions); + + parsedArgs = DefaultParse(new[] { @"/nullable:Warnings", @"/nullable+", "/langversion:8", "a.cs" }, WorkingDirectory); + parsedArgs.Errors.Verify(); + Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions); + + parsedArgs = DefaultParse(new[] { @"/nullable:Warnings", @"/nullable:", "/langversion:8", "a.cs" }, WorkingDirectory); + parsedArgs.Errors.Verify( + // error CS2006: Command-line syntax error: Missing '' for 'nullable' option + Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("", "nullable").WithLocation(1, 1) + ); + Assert.Equal(NullableContextOptions.Warnings, parsedArgs.CompilationOptions.NullableContextOptions); + + parsedArgs = DefaultParse(new[] { @"/nullable:Warnings", @"/nullable:YES", "/langversion:8", "a.cs" }, WorkingDirectory); + parsedArgs.Errors.Verify( + // error CS8636: Invalid option 'YES' for /nullable; must be 'disable', 'enable', 'safeonly', 'warnings' or 'safeonlywarnings' + Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("YES").WithLocation(1, 1) + ); + Assert.Equal(NullableContextOptions.Warnings, parsedArgs.CompilationOptions.NullableContextOptions); + + parsedArgs = DefaultParse(new[] { @"/nullable:Warnings", @"/nullable:disable", "/langversion:8", "a.cs" }, WorkingDirectory); + parsedArgs.Errors.Verify(); + Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions); + + parsedArgs = DefaultParse(new[] { @"/nullable:Warnings", @"/nullable:enable", "/langversion:8", "a.cs" }, WorkingDirectory); + parsedArgs.Errors.Verify(); + Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions); + + parsedArgs = DefaultParse(new[] { @"/nullable:Warnings", @"/nullable:Warnings", "/langversion:8", "a.cs" }, WorkingDirectory); + parsedArgs.Errors.Verify(); + Assert.Equal(NullableContextOptions.Warnings, parsedArgs.CompilationOptions.NullableContextOptions); + + parsedArgs = DefaultParse(new[] { @"/nullable:Warnings", "/langversion:7.3", "a.cs" }, WorkingDirectory); + parsedArgs.Errors.Verify( + // error CS8630: Invalid 'nullable' value: 'Warnings' for C# 7.3. Please use language version 8.0 or greater. + Diagnostic(ErrorCode.ERR_NullableOptionNotAvailable).WithArguments("nullable", "Warnings", "7.3", "8.0").WithLocation(1, 1) + ); + Assert.Equal(NullableContextOptions.Warnings, parsedArgs.CompilationOptions.NullableContextOptions); } [Fact] @@ -8667,6 +8807,17 @@ public void ParseAdditionalFile() Assert.Equal(Path.Combine(WorkingDirectory, "web.config"), args.AdditionalFiles[0].Path); Assert.Equal(Path.Combine(WorkingDirectory, "app.manifest"), args.AdditionalFiles[1].Path); + args = DefaultParse(new[] { "/additionalfile:web.config,app.manifest", "a.cs" }, WorkingDirectory); + args.Errors.Verify(); + Assert.Equal(2, args.AdditionalFiles.Length); + Assert.Equal(Path.Combine(WorkingDirectory, "web.config"), args.AdditionalFiles[0].Path); + Assert.Equal(Path.Combine(WorkingDirectory, "app.manifest"), args.AdditionalFiles[1].Path); + + args = DefaultParse(new[] { @"/additionalfile:""web.config,app.manifest""", "a.cs" }, WorkingDirectory); + args.Errors.Verify(); + Assert.Equal(1, args.AdditionalFiles.Length); + Assert.Equal(Path.Combine(WorkingDirectory, "web.config,app.manifest"), args.AdditionalFiles[0].Path); + args = DefaultParse(new[] { "/additionalfile:web.config:app.manifest", "a.cs" }, WorkingDirectory); args.Errors.Verify(); Assert.Equal(1, args.AdditionalFiles.Length); diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDeconstructTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDeconstructTests.cs index 7cbde363b5f67..5b08829ef0ea8 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDeconstructTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDeconstructTests.cs @@ -8557,9 +8557,12 @@ static void M(int x) } [WorkItem(21028, "https://github.com/dotnet/roslyn/issues/21028")] - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/32006")] public void InferredName_Lambda() { + // See https://github.com/dotnet/roslyn/issues/32006 + // need to relax assertion in GetImplicitTupleLiteralConversion + var source = @"class C { diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs index eb36925741bbc..90ee6821fcd1c 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs @@ -18066,9 +18066,12 @@ public T M2(System.Func f) Assert.Equal("(System.Int32 a, System.Int32) x1", x1.ToTestDisplayString()); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/32006")] public void LambdaTypeInferenceWithDynamic() { + // See https://github.com/dotnet/roslyn/issues/32006 + // need to relax assertion in GetImplicitTupleLiteralConversion + var source = @" public class C { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/AwaitExpressionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/AwaitExpressionTests.cs index 51d65aad6c776..9bd1f60131de3 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/AwaitExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/AwaitExpressionTests.cs @@ -54,6 +54,18 @@ public C(Task t) { Assert.Equal("System.Boolean System.Runtime.CompilerServices.TaskAwaiter.IsCompleted { get; }", info.IsCompletedProperty.ToTestDisplayString()); } + [Fact] + [WorkItem(744146, "https://devdiv.visualstudio.com/DevDiv/_workitems/edit/744146")] + public void DefaultAwaitExpressionInfo() + { + AwaitExpressionInfo info = default; + Assert.Null(info.GetAwaiterMethod); + Assert.Null(info.GetResultMethod); + Assert.Null(info.IsCompletedProperty); + Assert.False(info.IsDynamic); + Assert.Equal(0, info.GetHashCode()); + } + private AwaitExpressionInfo GetAwaitExpressionInfo(string text, out CSharpCompilation compilation, params DiagnosticDescription[] diagnostics) { var tree = Parse(text, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp5)); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index 5fd4a6090f065..c4edbc2868411 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -50,6 +50,721 @@ static void Main() ); } + [Fact] + public void SpeakableInference_MethodTypeInference() + { + var source = +@"class Program +{ + void M(T t) + { + if (t == null) throw null; + t.ToString(); + var t2 = Copy(t); + t2.ToString(); // warn + } + static T Copy(T t) => throw null; +}"; + var comp = CreateCompilationWithIndexAndRange(source, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (8,9): warning CS8602: Possible dereference of a null reference. + // t2.ToString(); // warn + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "t2").WithLocation(8, 9) + ); + } + + [Fact] + public void SpeakableInference_MethodTypeInference_WithTuple() + { + var source = +@"class Program +{ + void M(T t) + { + if (t == null) throw null; + var tuple = (t, t); + tuple.Item1.ToString(); + tuple.Item2.ToString(); + + var tuple2 = Copy(tuple); + tuple2.Item1.ToString(); // warn + tuple2.Item2.ToString(); // warn + + var tuple3 = Copy(tuple); + tuple3.Item1.ToString(); // warn + tuple3.Item2.ToString(); // warn + } + static (T, U) Copy((T, U) t) => throw null; +}"; + var comp = CreateCompilationWithIndexAndRange(source, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (11,9): warning CS8602: Possible dereference of a null reference. + // tuple2.Item1.ToString(); // warn + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "tuple2.Item1").WithLocation(11, 9), + // (12,9): warning CS8602: Possible dereference of a null reference. + // tuple2.Item2.ToString(); // warn + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "tuple2.Item2").WithLocation(12, 9), + // (15,9): warning CS8602: Possible dereference of a null reference. + // tuple3.Item1.ToString(); // warn + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "tuple3.Item1").WithLocation(15, 9), + // (16,9): warning CS8602: Possible dereference of a null reference. + // tuple3.Item2.ToString(); // warn + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "tuple3.Item2").WithLocation(16, 9) + ); + } + + [Fact] + public void SpeakableInference_MethodTypeInference_WithNull() + { + var source = +@"class Program +{ + void M(T t) where T : class? + { + if (t == null) throw null; + t.ToString(); + var t2 = Copy(t, null); + t2.ToString(); // warn + } + static T Copy(T t, U t) => throw null; +}"; + var comp = CreateCompilationWithIndexAndRange(source, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (7,18): error CS0411: The type arguments for method 'Program.Copy(T, U)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // var t2 = Copy(t, null); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Copy").WithArguments("Program.Copy(T, U)").WithLocation(7, 18), + // (10,32): error CS0100: The parameter name 't' is a duplicate + // static T Copy(T t, U t) => throw null; + Diagnostic(ErrorCode.ERR_DuplicateParamName, "t").WithArguments("t").WithLocation(10, 32) + ); + } + + [Fact] + public void SpeakableInference_MethodTypeInference_NullAssigned() + { + var source = +@"class Program +{ + void M(T t) where T : class + { + t = null; + var t2 = Copy(t); + t2 /*T:T?*/ .ToString(); // warn + } + static T Copy(T t) => throw null; +}"; + var comp = CreateCompilationWithIndexAndRange(source, options: WithNonNullTypesTrue()); + comp.VerifyTypes(); + comp.VerifyDiagnostics( + // (5,13): warning CS8600: Converting null literal or possible null value to non-nullable type. + // t = null; + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(5, 13), + // (7,9): warning CS8602: Possible dereference of a null reference. + // t2 /*T:T?*/ .ToString(); // warn + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "t2").WithLocation(7, 9) + ); + } + + [Fact] + public void SpeakableInference_MethodTypeInference_NullableValueType() + { + var source = +@"class Program +{ + void M(int? t) + { + if (t == null) throw null; + t.Value.ToString(); + var t2 = Copy(t); + t2.Value.ToString(); // warn + } + void M2(T? t) where T : struct + { + if (t == null) throw null; + t.Value.ToString(); + var t2 = Copy(t); + t2.Value.ToString(); // warn + } + static T Copy(T t) => throw null; +}"; + var comp = CreateCompilationWithIndexAndRange(source, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (8,9): warning CS8629: Nullable value type may be null. + // t2.Value.ToString(); // warn + Diagnostic(ErrorCode.WRN_NullableValueTypeMayBeNull, "t2.Value").WithLocation(8, 9), + // (15,9): warning CS8629: Nullable value type may be null. + // t2.Value.ToString(); // warn + Diagnostic(ErrorCode.WRN_NullableValueTypeMayBeNull, "t2.Value").WithLocation(15, 9) + ); + } + + [Fact] + public void SpeakableInference_ArrayTypeInference() + { + var source = +@"class Program +{ + void M(T t) + { + if (t == null) throw null; + t.ToString(); + var t2 = new[] { t }; + t2[0].ToString(); // warn + } +}"; + var comp = CreateCompilationWithIndexAndRange(source, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (8,9): warning CS8602: Possible dereference of a null reference. + // t2[0].ToString(); // warn + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "t2[0]").WithLocation(8, 9) + ); + } + + [Fact] + public void SpeakableInference_ArrayTypeInference_WithTuple() + { + var source = +@"class Program +{ + void M(T t) + { + if (t == null) throw null; + var a = new[] { (t, t) }; + a[0].Item1.ToString(); + a[0].Item2.ToString(); + + var b = new (T, T)[] { (t, t) }; + b[0].Item1.ToString(); + b[0].Item2.ToString(); + } +}"; + var comp = CreateCompilationWithIndexAndRange(source, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (7,9): warning CS8602: Possible dereference of a null reference. + // a[0].Item1.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "a[0].Item1").WithLocation(7, 9), + // (8,9): warning CS8602: Possible dereference of a null reference. + // a[0].Item2.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "a[0].Item2").WithLocation(8, 9), + // (11,9): warning CS8602: Possible dereference of a null reference. + // b[0].Item1.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "b[0].Item1").WithLocation(11, 9), + // (12,9): warning CS8602: Possible dereference of a null reference. + // b[0].Item2.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "b[0].Item2").WithLocation(12, 9) + ); + } + + [Fact] + public void SpeakableInference_ArrayTypeInference_WithNull() + { + var source = +@"class Program +{ + void M(T t) where T : class? + { + if (t == null) throw null; + t.ToString(); + var t2 = new[] { t, null }; + t2[0].ToString(); // warn + } +}"; + var comp = CreateCompilationWithIndexAndRange(source, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (7,29): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // var t2 = new[] { t, null }; + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(7, 29), + // (8,9): warning CS8602: Possible dereference of a null reference. + // t2[0].ToString(); // warn + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "t2[0]").WithLocation(8, 9) + ); + } + + [Fact] + public void SpeakableInference_ArrayTypeInference_NullableValueType() + { + var source = +@"class Program +{ + void M(int? t) + { + if (t == null) throw null; + t.Value.ToString(); + var t2 = new[] { t }; + t2[0].Value.ToString(); // warn + } + void M2(T? t) where T : struct + { + if (t == null) throw null; + t.Value.ToString(); + var t2 = new[] { t }; + t2[0].Value.ToString(); // warn + } +}"; + var comp = CreateCompilationWithIndexAndRange(source, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (8,9): warning CS8629: Nullable value type may be null. + // t2[0].Value.ToString(); // warn + Diagnostic(ErrorCode.WRN_NullableValueTypeMayBeNull, "t2[0].Value").WithLocation(8, 9), + // (15,9): warning CS8629: Nullable value type may be null. + // t2[0].Value.ToString(); // warn + Diagnostic(ErrorCode.WRN_NullableValueTypeMayBeNull, "t2[0].Value").WithLocation(15, 9) + ); + } + + [Fact] + public void SpeakableInference_ArrayTypeInference_ConversionWithNullableOutput_WithNestedMismatch() + { + var source = +@"class A +{ + public static implicit operator C?(A a) => throw null; +} +class B : A +{ +} +class C +{ + void M(B x, C y) + { + var a = new[] { x, y }; + a[0].ToString(); + + var b = new[] { y, x }; + b[0].ToString(); + } +}"; + // https://github.com/dotnet/roslyn/issues/30480: Should report WRN_NoBestNullabilityArrayElements. Problem in BestTypeInferrer.Better + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (12,25): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. + // var a = new[] { x, y }; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x").WithArguments("C", "C").WithLocation(12, 25), + // (13,9): warning CS8602: Possible dereference of a null reference. + // a[0].ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "a[0]").WithLocation(13, 9), + // (15,28): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. + // var b = new[] { y, x }; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x").WithArguments("C", "C").WithLocation(15, 28), + // (16,9): warning CS8602: Possible dereference of a null reference. + // b[0].ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "b[0]").WithLocation(16, 9) + ); + } + + [Fact] + public void SpeakableInference_LambdaReturnTypeInference() + { + var source = +@"class Program +{ + void M(T t) + { + var x1 = F(() => + { + if (t == null) throw null; + bool b = true; + if (b) return t; + return t; + }); + x1.ToString(); + + var x2 = F(() => + { + if (t == null) throw null; + bool b = true; + if (b) return t; + return t; + }); + x2.ToString(); + } + T F(System.Func f) => throw null; +}"; + var comp = CreateCompilationWithIndexAndRange(source, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (12,9): warning CS8602: Possible dereference of a null reference. + // x1.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x1").WithLocation(12, 9), + // (21,9): warning CS8602: Possible dereference of a null reference. + // x2.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x2").WithLocation(21, 9) + ); + } + + [Fact] + public void SpeakableInference_LambdaReturnTypeInference2() + { + var source = +@"class Program +{ + void M(T t, T t2) + { + var x1 = F(() => + { + if (t == null) throw null; + bool b = true; + if (b) return t; + return t2; + }); + x1.ToString(); + + var x2 = F(() => + { + if (t == null) throw null; + bool b = true; + if (b) return t; + return t2; + }); + x2.ToString(); + } + T F(System.Func f) => throw null; +}"; + var comp = CreateCompilationWithIndexAndRange(source, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (12,9): warning CS8602: Possible dereference of a null reference. + // x1.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x1").WithLocation(12, 9), + // (21,9): warning CS8602: Possible dereference of a null reference. + // x2.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x2").WithLocation(21, 9) + ); + } + + [Fact] + public void SpeakableInference_LambdaReturnTypeInference_WithSingleReturn() + { + var source = +@"class Program +{ + void M() + { + var x1 = Copy(() => """"); + x1.ToString(); + } + void M2(T t) + { + if (t == null) throw null; + var x1 = Copy(() => t); + x1.ToString(); // 1 + } + T Copy(System.Func f) => throw null; +}"; + var comp = CreateCompilationWithIndexAndRange(source, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (12,9): warning CS8602: Possible dereference of a null reference. + // x1.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x1").WithLocation(12, 9) + ); + } + + [Fact] + public void SpeakableInference_LambdaReturnTypeInference_WithTuple() + { + var source = +@"class Program +{ + void M(T t) + { + var x1 = Copy(() => + { + if (t == null) throw null; + bool b = true; + if (b) return (t, t); + return (t, t); + }); + x1.Item1.ToString(); + x1.Item2.ToString(); + } + T Copy(System.Func f) => throw null; +}"; + var comp = CreateCompilationWithIndexAndRange(source, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (12,9): warning CS8602: Possible dereference of a null reference. + // x1.Item1.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x1.Item1").WithLocation(12, 9), + // (13,9): warning CS8602: Possible dereference of a null reference. + // x1.Item2.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x1.Item2").WithLocation(13, 9) + ); + } + + [Fact] + public void SpeakableInference_LambdaReturnTypeInference_ConversionWithNullableOutput() + { + var source = +@"class A +{ + public static implicit operator C?(A a) => new C(); +} +class B : A +{ +} +class C +{ + void M(B x, C y) + { + var x1 = F(() => + { + bool b = true; + if (b) return x; + return y; + }); + x1.ToString(); + + var x2 = F(() => + { + bool b = true; + if (b) return y; + return x; + }); + x2.ToString(); + } + T F(System.Func f) => throw null; +}"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (18,9): warning CS8602: Possible dereference of a null reference. + // x1.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x1").WithLocation(18, 9), + // (26,9): warning CS8602: Possible dereference of a null reference. + // x2.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x2").WithLocation(26, 9) + ); + } + + [Fact] + public void SpeakableInference_LambdaReturnTypeInference_ConversionWithNullableOutput2() + { + var source = +@"class A +{ + public static implicit operator C?(A a) => throw null; +} +class B : A +{ +} +class C +{ + void M(B x, C y) + { + var x1 = F(() => + { + bool b = true; + if (b) return x; + return y; + }); + x1.ToString(); + + var x2 = F(() => + { + bool b = true; + if (b) return y; + return x; + }); + x2.ToString(); + } + U F(System.Func f) => throw null; +}"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (18,9): warning CS8602: Possible dereference of a null reference. + // x1.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x1").WithLocation(18, 9), + // (26,9): warning CS8602: Possible dereference of a null reference. + // x2.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x2").WithLocation(26, 9) + ); + } + + [Fact] + public void SpeakableInference_LambdaReturnTypeInference_ConversionWithNullableOutput_WithNestedMismatch() + { + var source = +@"class A +{ + public static implicit operator C?(A a) => throw null; +} +class B : A +{ +} +class C +{ + void M(B x, C y) + { + var x1 = F(() => + { + bool b = true; + if (b) return x; + return y; + }); + x1.ToString(); + + var x2 = F(() => + { + bool b = true; + if (b) return y; + return x; + }); + x2.ToString(); + } + U F(System.Func f) => throw null; +}"; + + // https://github.com/dotnet/roslyn/issues/30480: Should report WRN_CantInferNullabilityOfMethodTypeArgs + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (15,27): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. + // if (b) return x; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x").WithArguments("C", "C").WithLocation(15, 27), + // (18,9): warning CS8602: Possible dereference of a null reference. + // x1.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x1").WithLocation(18, 9), + // (24,20): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. + // return x; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x").WithArguments("C", "C").WithLocation(24, 20), + // (26,9): warning CS8602: Possible dereference of a null reference. + // x2.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x2").WithLocation(26, 9) + ); + } + + [Fact] + public void SpeakableInference_LambdaReturnTypeInference_ConversionWithNullableInput() + { + var source = +@"class A +{ + public static implicit operator C(A? a) => null; // warn +} +class B : A +{ +} +class C +{ + void M(B? x, C y) + { + var x1 = F(() => + { + bool b = true; + if (b) return x; + return y; + }); + x1.ToString(); + + var x2 = F(() => + { + bool b = true; + if (b) return y; + return x; + }); + x2.ToString(); + } + T F(System.Func f) => throw null; +}"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (3,48): warning CS8603: Possible null reference return. + // public static implicit operator C(A? a) => null; // warn + Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(3, 48) + ); + } + + [Fact] + public void SpeakableInference_LambdaReturnTypeInference_WithNullabilityMismatch() + { + var source = @" +class C +{ + void M(C? x, C y) + { + var x1 = F(() => + { + bool b = true; + if (b) return x; + return y; + }); + _ = x1 /*T:C?*/; + x1.ToString(); + + var x2 = F(() => + { + bool b = true; + if (b) return y; + return x; + }); + _ = x2 /*T:C?*/; + x2.ToString(); + } + U F(System.Func f) => throw null; +}"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyTypes(); + // https://github.com/dotnet/roslyn/issues/30480: Should report WRN_CantInferNullabilityOfMethodTypeArgs + comp.VerifyDiagnostics( + // (10,20): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. + // return y; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "y").WithArguments("C", "C").WithLocation(10, 20), + // (13,9): warning CS8602: Possible dereference of a null reference. + // x1.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x1").WithLocation(13, 9), + // (18,27): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. + // if (b) return y; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "y").WithArguments("C", "C").WithLocation(18, 27), + // (22,9): warning CS8602: Possible dereference of a null reference. + // x2.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x2").WithLocation(22, 9) + ); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/32006")] + [WorkItem(32006, "https://github.com/dotnet/roslyn/issues/32006")] + public void SpeakableInference_LambdaReturnTypeInference_NonNullableTypelessOuptut() + { + // See https://github.com/dotnet/roslyn/issues/32006 + // need to relax assertion in GetImplicitTupleLiteralConversion + var source = +@" +class C +{ + void M(C? c) + { + var x1 = F(() => + { + bool b = true; + if (b) return (c, c); + return (null, null); + }); + x1.ToString(); + x1.Item1.ToString(); + } + T F(System.Func f) => throw null; +}"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (13,9): warning CS8602: Possible dereference of a null reference. + // x1.Item1.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x1.Item1").WithLocation(13, 9) + ); + } + + [Fact] + public void SpeakableInference_VarInference() + { + var source = +@"class Program +{ + void M(T t) + { + if (t == null) throw null; + var t2 = t; + t2.ToString(); + } +}"; + var comp = CreateCompilationWithIndexAndRange(source, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics(); + // Once we have a public API, we should check that the declaration type of `t2` is `T` https://github.com/dotnet/roslyn/issues/26198 + } + [Fact] public void Directive_Qualifiers() { @@ -1388,6 +2103,18 @@ public void NullableOption() Diagnostic(ErrorCode.ERR_NullableOptionNotAvailable).WithArguments("NullableContextOptions", "SafeOnly", "7.3", "8.0").WithLocation(1, 1) ); + comp = CreateCompilation("", options: WithNonNullTypes(NullableContextOptions.Warnings), parseOptions: TestOptions.Regular7_3); + comp.VerifyDiagnostics( + // error CS8630: Invalid 'NullableContextOptions' value: 'Warnings' for C# 7.3. Please use language version 8.0 or greater. + Diagnostic(ErrorCode.ERR_NullableOptionNotAvailable).WithArguments("NullableContextOptions", "Warnings", "7.3", "8.0").WithLocation(1, 1) + ); + + comp = CreateCompilation("", options: WithNonNullTypes(NullableContextOptions.SafeOnlyWarnings), parseOptions: TestOptions.Regular7_3); + comp.VerifyDiagnostics( + // error CS8630: Invalid 'NullableContextOptions' value: 'SafeOnlyWarnings' for C# 7.3. Please use language version 8.0 or greater. + Diagnostic(ErrorCode.ERR_NullableOptionNotAvailable).WithArguments("NullableContextOptions", "SafeOnlyWarnings", "7.3", "8.0").WithLocation(1, 1) + ); + comp = CreateCompilation("", options: WithNonNullTypes(NullableContextOptions.Disable), parseOptions: TestOptions.Regular7_3); comp.VerifyDiagnostics(); @@ -1397,6 +2124,12 @@ public void NullableOption() comp = CreateCompilation("", options: WithNonNullTypes(NullableContextOptions.SafeOnly), parseOptions: TestOptions.Regular8); comp.VerifyDiagnostics(); + comp = CreateCompilation("", options: WithNonNullTypes(NullableContextOptions.Warnings), parseOptions: TestOptions.Regular8); + comp.VerifyDiagnostics(); + + comp = CreateCompilation("", options: WithNonNullTypes(NullableContextOptions.SafeOnlyWarnings), parseOptions: TestOptions.Regular8); + comp.VerifyDiagnostics(); + comp = CreateCompilation("", options: WithNonNullTypes(NullableContextOptions.Disable), parseOptions: TestOptions.Regular8); comp.VerifyDiagnostics(); @@ -1412,6 +2145,18 @@ public void NullableOption() comp = CreateCompilation(new string[] { }, options: WithNonNullTypes(NullableContextOptions.SafeOnly), parseOptions: TestOptions.Regular8); comp.VerifyDiagnostics(); + comp = CreateCompilation(new string[] { }, options: WithNonNullTypes(NullableContextOptions.Warnings), parseOptions: TestOptions.Regular7_3); + comp.VerifyDiagnostics(); + + comp = CreateCompilation(new string[] { }, options: WithNonNullTypes(NullableContextOptions.Warnings), parseOptions: TestOptions.Regular8); + comp.VerifyDiagnostics(); + + comp = CreateCompilation(new string[] { }, options: WithNonNullTypes(NullableContextOptions.SafeOnlyWarnings), parseOptions: TestOptions.Regular7_3); + comp.VerifyDiagnostics(); + + comp = CreateCompilation(new string[] { }, options: WithNonNullTypes(NullableContextOptions.SafeOnlyWarnings), parseOptions: TestOptions.Regular8); + comp.VerifyDiagnostics(); + comp = CreateCompilation(new string[] { }, options: WithNonNullTypes(NullableContextOptions.Disable), parseOptions: TestOptions.Regular7_3); comp.VerifyDiagnostics(); @@ -18807,9 +19552,9 @@ static void F(I x, I y) var z = CreateI(A.F)/*T:I!*/; object o; o = (new[] { x, x })[0]/*T:I!*/; - o = (new[] { x, y })[0]/*T:I!*/; + o = (new[] { x, y })[0]/*T:I!*/; // 1 o = (new[] { x, z })[0]/*T:I!*/; - o = (new[] { y, x })[0]/*T:I!*/; + o = (new[] { y, x })[0]/*T:I!*/; // 2 o = (new[] { y, y })[0]/*T:I!*/; o = (new[] { y, z })[0]/*T:I!*/; o = (new[] { z, x })[0]/*T:I!*/; @@ -18850,10 +19595,10 @@ static void F(IOut x, IOut y) var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue(), references: new[] { ref0 }); comp.VerifyDiagnostics( // (12,14): warning CS8639: No best nullability found for implicitly-typed array. - // o = (new[] { x, y })[0]/*T:I!*/; + // o = (new[] { x, y })[0]/*T:I*/; // 1 Diagnostic(ErrorCode.WRN_NoBestNullabilityArrayElements, "new[] { x, y }").WithLocation(12, 14), // (14,14): warning CS8639: No best nullability found for implicitly-typed array. - // o = (new[] { y, x })[0]/*T:I!*/; + // o = (new[] { y, x })[0]/*T:I*/; // 2 Diagnostic(ErrorCode.WRN_NoBestNullabilityArrayElements, "new[] { y, x }").WithLocation(14, 14)); comp.VerifyTypes(); } @@ -18966,9 +19711,9 @@ static void G0(B.INone x0, B.INone y0) var z0 = CreateINone(A.F1)/*T:B.INone!*/; object o; o = (new[] { x0, x0 })[0]/*T:B.INone!*/; - o = (new[] { x0, y0 })[0]/*T:B.INone!*/; + o = (new[] { x0, y0 })[0]/*T:B.INone!*/; // 1 o = (new[] { x0, z0 })[0]/*T:B.INone!*/; - o = (new[] { y0, x0 })[0]/*T:B.INone!*/; + o = (new[] { y0, x0 })[0]/*T:B.INone!*/; // 2 o = (new[] { y0, y0 })[0]/*T:B.INone!*/; o = (new[] { y0, z0 })[0]/*T:B.INone!*/; o = (new[] { z0, x0 })[0]/*T:B.INone!*/; @@ -18981,9 +19726,9 @@ static void G1(B.I x1, B.I y1) var z1 = CreateI(A.F1, A.F2)/*T:B.I!*/; object o; o = (new[] { x1, x1 })[0]/*T:B.I!*/; - o = (new[] { x1, y1 })[0]/*T:B.I!*/; + o = (new[] { x1, y1 })[0]/*T:B.I!*/; // 3 o = (new[] { x1, z1 })[0]/*T:B.I!*/; - o = (new[] { y1, x1 })[0]/*T:B.I!*/; + o = (new[] { y1, x1 })[0]/*T:B.I!*/; // 4 o = (new[] { y1, y1 })[0]/*T:B.I!*/; o = (new[] { y1, z1 })[0]/*T:B.I!*/; o = (new[] { z1, x1 })[0]/*T:B.I!*/; @@ -18996,9 +19741,9 @@ static void G2(B.IIn x2, B.IIn y2) var z2 = CreateIIn(A.F1, A.F2)/*T:B.IIn!*/; object o; o = (new[] { x2, x2 })[0]/*T:B.IIn!*/; - o = (new[] { x2, y2 })[0]/*T:B.IIn!*/; + o = (new[] { x2, y2 })[0]/*T:B.IIn!*/; // 5 o = (new[] { x2, z2 })[0]/*T:B.IIn!*/; - o = (new[] { y2, x2 })[0]/*T:B.IIn!*/; + o = (new[] { y2, x2 })[0]/*T:B.IIn!*/; // 6 o = (new[] { y2, y2 })[0]/*T:B.IIn!*/; o = (new[] { y2, z2 })[0]/*T:B.IIn!*/; o = (new[] { z2, x2 })[0]/*T:B.IIn!*/; @@ -19011,9 +19756,9 @@ static void G3(B.IOut x3, B.IOut y3) var z3 = CreateIOut(A.F1, A.F2)/*T:B.IOut!*/; object o; o = (new[] { x3, x3 })[0]/*T:B.IOut!*/; - o = (new[] { x3, y3 })[0]/*T:B.IOut!*/; + o = (new[] { x3, y3 })[0]/*T:B.IOut!*/; // 7 o = (new[] { x3, z3 })[0]/*T:B.IOut!*/; - o = (new[] { y3, x3 })[0]/*T:B.IOut!*/; + o = (new[] { y3, x3 })[0]/*T:B.IOut!*/; // 8 o = (new[] { y3, y3 })[0]/*T:B.IOut!*/; o = (new[] { y3, z3 })[0]/*T:B.IOut!*/; o = (new[] { z3, x3 })[0]/*T:B.IOut!*/; @@ -19024,28 +19769,28 @@ static void G3(B.IOut x3, B.IOut y3) var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue(), references: new[] { ref0 }); comp.VerifyDiagnostics( // (17,14): warning CS8639: No best nullability found for implicitly-typed array. - // o = (new[] { x0, y0 })[0]/*T:B.INone!*/; + // o = (new[] { x0, y0 })[0]/*T:B.INone*/; // 1 Diagnostic(ErrorCode.WRN_NoBestNullabilityArrayElements, "new[] { x0, y0 }").WithLocation(17, 14), // (19,14): warning CS8639: No best nullability found for implicitly-typed array. - // o = (new[] { y0, x0 })[0]/*T:B.INone!*/; + // o = (new[] { y0, x0 })[0]/*T:B.INone*/; // 2 Diagnostic(ErrorCode.WRN_NoBestNullabilityArrayElements, "new[] { y0, x0 }").WithLocation(19, 14), // (32,14): warning CS8639: No best nullability found for implicitly-typed array. - // o = (new[] { x1, y1 })[0]/*T:B.I!*/; + // o = (new[] { x1, y1 })[0]/*T:B.I*/; // 3 Diagnostic(ErrorCode.WRN_NoBestNullabilityArrayElements, "new[] { x1, y1 }").WithLocation(32, 14), // (34,14): warning CS8639: No best nullability found for implicitly-typed array. - // o = (new[] { y1, x1 })[0]/*T:B.I!*/; + // o = (new[] { y1, x1 })[0]/*T:B.I*/; // 4 Diagnostic(ErrorCode.WRN_NoBestNullabilityArrayElements, "new[] { y1, x1 }").WithLocation(34, 14), // (47,14): warning CS8639: No best nullability found for implicitly-typed array. - // o = (new[] { x2, y2 })[0]/*T:B.IIn!*/; + // o = (new[] { x2, y2 })[0]/*T:B.IIn*/; // 5 Diagnostic(ErrorCode.WRN_NoBestNullabilityArrayElements, "new[] { x2, y2 }").WithLocation(47, 14), // (49,14): warning CS8639: No best nullability found for implicitly-typed array. - // o = (new[] { y2, x2 })[0]/*T:B.IIn!*/; + // o = (new[] { y2, x2 })[0]/*T:B.IIn*/; // 6 Diagnostic(ErrorCode.WRN_NoBestNullabilityArrayElements, "new[] { y2, x2 }").WithLocation(49, 14), // (62,14): warning CS8639: No best nullability found for implicitly-typed array. - // o = (new[] { x3, y3 })[0]/*T:B.IOut!*/; + // o = (new[] { x3, y3 })[0]/*T:B.IOut*/; // 7 Diagnostic(ErrorCode.WRN_NoBestNullabilityArrayElements, "new[] { x3, y3 }").WithLocation(62, 14), // (64,14): warning CS8639: No best nullability found for implicitly-typed array. - // o = (new[] { y3, x3 })[0]/*T:B.IOut!*/; + // o = (new[] { y3, x3 })[0]/*T:B.IOut*/; // 8 Diagnostic(ErrorCode.WRN_NoBestNullabilityArrayElements, "new[] { y3, x3 }").WithLocation(64, 14)); comp.VerifyTypes(); } @@ -19427,6 +20172,58 @@ static void F(B x, C? y) Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(new[] { y, x })[0]").WithLocation(13, 9)); } + [Fact] + public void MultipleConversions_ArrayInitializer_ConversionWithNullableOutput() + { + var source = +@"class A +{ + public static implicit operator C?(A a) => new C(); +} +class B : A +{ +} +class C +{ + static void F(B x, C y) + { + (new[] { x, y })[0].ToString(); + (new[] { y, x })[0].ToString(); + } +}"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (12,9): warning CS8602: Possible dereference of a null reference. + // (new[] { x, y })[0].ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(new[] { x, y })[0]").WithLocation(12, 9), + // (13,9): warning CS8602: Possible dereference of a null reference. + // (new[] { y, x })[0].ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(new[] { y, x })[0]").WithLocation(13, 9)); + } + + [Fact] + public void MultipleConversions_ArrayInitializer_ConversionWithNullableInput() + { + var source = +@"class A +{ + public static implicit operator C(A? a) => new C(); +} +class B : A +{ +} +class C +{ + static void F(B? x, C y) + { + (new[] { x, y })[0].ToString(); + (new[] { y, x })[0].ToString(); + } +}"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics(); + } + [Fact] public void ObjectInitializer_01() { @@ -21709,6 +22506,7 @@ static void M(object? x) [WorkItem(27961, "https://github.com/dotnet/roslyn/issues/27961")] [WorkItem(29837, "https://github.com/dotnet/roslyn/issues/29837")] + [WorkItem(30480, "https://github.com/dotnet/roslyn/issues/30480")] [Fact] public void LambdaReturnValue_NestedNullability_Invariant() { @@ -21732,21 +22530,33 @@ static void F(B x, B y) { var z = Create(A.F)/*T:B!*/; F(i => { switch (i) { case 0: return x; default: return x; }})/*T:B!*/; - F(i => { switch (i) { case 0: return x; default: return y; }})/*T:B*/; + F(i => { switch (i) { case 0: return x; default: return y; }})/*T:B!*/; // 1 F(i => { switch (i) { case 0: return x; default: return z; }})/*T:B*/; - F(i => { switch (i) { case 0: return y; default: return x; }})/*T:B*/; + F(i => { switch (i) { case 0: return y; default: return x; }})/*T:B!*/; // 2 F(i => { switch (i) { case 0: return y; default: return y; }})/*T:B!*/; F(i => { switch (i) { case 0: return y; default: return z; }})/*T:B*/; F(i => { switch (i) { case 0: return z; default: return x; }})/*T:B*/; F(i => { switch (i) { case 0: return z; default: return y; }})/*T:B*/; F(i => { switch (i) { case 0: return z; default: return z; }})/*T:B*/; - F(i => { switch (i) { case 0: return x; case 1: return y; default: return z; }})/*T:B*/; - F(i => { switch (i) { case 0: return z; case 1: return y; default: return x; }})/*T:B*/; + F(i => { switch (i) { case 0: return x; case 1: return y; default: return z; }})/*T:B*/; // 3 + F(i => { switch (i) { case 0: return z; case 1: return y; default: return x; }})/*T:B*/; // 4 } }"; var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue(), references: new[] { ref0 }); - // https://github.com/dotnet/roslyn/issues/30480: Should report WRN_CantInferNullabilityOfMethodTypeArgs for { B, B }. - comp.VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (11,46): warning CS8619: Nullability of reference types in value of type 'B' doesn't match target type 'B'. + // F(i => { switch (i) { case 0: return x; default: return y; }})/*T:B*/; // 1 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x").WithArguments("B", "B").WithLocation(11, 46), + // (13,65): warning CS8619: Nullability of reference types in value of type 'B' doesn't match target type 'B'. + // F(i => { switch (i) { case 0: return y; default: return x; }})/*T:B*/; // 2 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x").WithArguments("B", "B").WithLocation(13, 65), + // (19,46): warning CS8619: Nullability of reference types in value of type 'B' doesn't match target type 'B'. + // F(i => { switch (i) { case 0: return x; case 1: return y; default: return z; }})/*T:B*/; // 3 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x").WithArguments("B", "B").WithLocation(19, 46), + // (20,83): warning CS8619: Nullability of reference types in value of type 'B' doesn't match target type 'B'. + // F(i => { switch (i) { case 0: return z; case 1: return y; default: return x; }})/*T:B*/; // 4 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x").WithArguments("B", "B").WithLocation(20, 83) + ); comp.VerifyTypes(); } @@ -21776,9 +22586,9 @@ static void F1(I x, I y) { var z = CreateI(A.F)/*T:I!*/; F(b => { if (b) return x; else return x; })/*T:I!*/; - F(b => { if (b) return x; else return y; })/*T:I*/; + F(b => { if (b) return x; else return y; })/*T:I!*/; F(b => { if (b) return x; else return z; })/*T:I*/; - F(b => { if (b) return y; else return x; })/*T:I*/; + F(b => { if (b) return y; else return x; })/*T:I!*/; F(b => { if (b) return y; else return y; })/*T:I!*/; F(b => { if (b) return y; else return z; })/*T:I*/; F(b => { if (b) return z; else return x; })/*T:I*/; @@ -21817,7 +22627,14 @@ static void F3(IOut x, IOut y) var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue(), references: new[] { ref0 }); comp.VerifyTypes(); // https://github.com/dotnet/roslyn/issues/30480: Should report WRN_CantInferNullabilityOfMethodTypeArgs for { I, I }. - comp.VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (13,47): warning CS8619: Nullability of reference types in value of type 'I' doesn't match target type 'I'. + // F(b => { if (b) return x; else return y; })/*T:I!*/; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "y").WithArguments("I", "I").WithLocation(13, 47), + // (15,32): warning CS8619: Nullability of reference types in value of type 'I' doesn't match target type 'I'. + // F(b => { if (b) return y; else return x; })/*T:I!*/; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "y").WithArguments("I", "I").WithLocation(15, 32) + ); } [WorkItem(27961, "https://github.com/dotnet/roslyn/issues/27961")] @@ -21939,8 +22756,8 @@ static void G(bool b) { // I F((ref I a1, ref I b1) => { if (b) return ref a1; return ref b1; }); - F((ref I a2, ref I b2) => { if (b) return ref a2; return ref b2; }); - F((ref I a3, ref I b3) => { if (b) return ref a3; return ref b3; }); + F((ref I a2, ref I b2) => { if (b) return ref a2; return ref b2; }); // 1 + F((ref I a3, ref I b3) => { if (b) return ref a3; return ref b3; }); // 2 F((ref I a4, ref I b4) => { if (b) return ref a4; return ref b4; }); // IIn F((ref IIn c1, ref IIn d1) => { if (b) return ref c1; return ref d1; }); @@ -21960,7 +22777,14 @@ static void G(bool b) // https://github.com/dotnet/roslyn/issues/30964 - Report warnings about nullability mismatch in conditional operators for: // F((ref I a2, ref I b2) => { if (b) return ref a2; return ref b2; }); // F((ref I a3, ref I b3) => { if (b) return ref a3; return ref b3; }); - comp.VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (12,72): warning CS8619: Nullability of reference types in value of type 'I' doesn't match target type 'I'. + // F((ref I a2, ref I b2) => { if (b) return ref a2; return ref b2; }); // 1 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "a2").WithArguments("I", "I").WithLocation(12, 72), + // (13,87): warning CS8619: Nullability of reference types in value of type 'I' doesn't match target type 'I'. + // F((ref I a3, ref I b3) => { if (b) return ref a3; return ref b3; }); // 2 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "b3").WithArguments("I", "I").WithLocation(13, 87) + ); comp.VerifyTypes(); } @@ -28281,6 +29105,200 @@ public void OnCompleted(Action x) { } ); } + [Fact] + public void Await_ProduceResultTypeFromTask() + { + var source = @" +class C +{ + async void M() + { + var x = await Async(); + x.ToString(); + } + System.Threading.Tasks.Task Async() => throw null; +}"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (7,9): warning CS8602: Possible dereference of a null reference. + // x.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(7, 9) + ); + } + + [Fact] + public void Await_CheckNullReceiver() + { + var source = @" +class C +{ + async void M() + { + await Async(); + } + System.Threading.Tasks.Task? Async() => throw null; +}"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (6,15): warning CS8602: Possible dereference of a null reference. + // await Async(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "Async()").WithLocation(6, 15) + ); + } + + [Fact] + public void Await_ExtensionGetAwaiter() + { + var source = @" +public class Awaitable +{ + async void M() + { + await Async(); + } + Awaitable? Async() => throw null; +} +public static class Extensions +{ + public static System.Runtime.CompilerServices.TaskAwaiter GetAwaiter(this Awaitable? x) => throw null; +} +"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (6,15): warning CS8602: Possible dereference of a null reference. + // await Async(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "Async()").WithLocation(6, 15) + ); + } + + [Fact] + public void Await_UpdateExpression() + { + var source = @" +class C +{ + async void M(System.Threading.Tasks.Task? task) + { + await task; // warn + await task; + } +}"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (6,15): warning CS8602: Possible dereference of a null reference. + // await task; // warn + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "task").WithLocation(6, 15) + ); + } + + [Fact] + public void Await_LearnFromNullTest() + { + var source = @" +class C +{ + System.Threading.Tasks.Task? M() => throw null; + async System.Threading.Tasks.Task M2(C? c) + { + await c?.M(); // warn + c.ToString(); // no cascade + } +}"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (7,15): warning CS8602: Possible dereference of a null reference. + // await c?.M(); // warn + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c?.M()").WithLocation(7, 15) + ); + } + + [Fact] + public void ArrayAccess_LearnFromNullTest() + { + var source = @" +class C +{ + string[] field = null!; + void M2(C? c) + { + _ = (c?.field)[0]; // warn + c.ToString(); // no cascade + } +}"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (7,14): warning CS8602: Possible dereference of a null reference. + // _ = (c?.field)[0]; // warn + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c?.field").WithLocation(7, 14) + ); + } + + [Fact] + public void Call_LearnFromNullTest() + { + var source = @" +class C +{ + string M() => throw null; + C field = null!; + void M2(C? c) + { + _ = (c?.field).M(); // warn + c.ToString(); // no cascade + } +}"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (8,14): warning CS8602: Possible dereference of a null reference. + // _ = (c?.field).M(); // warn + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c?.field").WithLocation(8, 14) + ); + } + + [Fact] + public void Indexer_LearnFromNullTest() + { + var source = @" +class C +{ + string this[int i] => throw null; + C field = null!; + void M(C? c) + { + _ = (c?.field)[0]; // warn + c.ToString(); // no cascade + } +}"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (8,14): warning CS8602: Possible dereference of a null reference. + // _ = (c?.field)[0]; // warn + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c?.field").WithLocation(8, 14) + ); + } + + [Fact] + public void MemberAccess_LearnFromNullTest() + { + var source = @" +class C +{ + string this[int i] => throw null; + C field = null!; + void M(C? c) + { + _ = (c?.field).field; // warn + c.ToString(); // no cascade + } +}"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (8,14): warning CS8602: Possible dereference of a null reference. + // _ = (c?.field).field; // warn + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c?.field").WithLocation(8, 14) + ); + } + [Fact] public void NoPiaObjectCreation_01() { @@ -38927,19 +39945,31 @@ static void F1(object[]? c1) foreach (var x in c1) // 1 { } + foreach (var z in c1) // no cascade + { + } + } + static void F2(object[]? c1) + { foreach (var y in (IEnumerable)c1) // 2 { } + } + static void F3(object[]? c1) + { if (c1 == null) return; foreach (var z in c1) { } } - static void F2(IList? c2) + static void F4(IList? c2) { foreach (var x in c2) // 3 { } + } + static void F5(IList? c2) + { foreach (var y in (IEnumerable?)c2) // 4 { } @@ -38950,18 +39980,18 @@ static void F2(IList? c2) // (7,27): warning CS8602: Possible dereference of a null reference. // foreach (var x in c1) // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c1").WithLocation(7, 27), - // (10,27): warning CS8600: Converting null literal or possible null value to non-nullable type. + // (16,27): warning CS8600: Converting null literal or possible null value to non-nullable type. // foreach (var y in (IEnumerable)c1) // 2 - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(IEnumerable)c1").WithLocation(10, 27), - // (10,27): warning CS8602: Possible dereference of a null reference. + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(IEnumerable)c1").WithLocation(16, 27), + // (16,27): warning CS8602: Possible dereference of a null reference. // foreach (var y in (IEnumerable)c1) // 2 - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(IEnumerable)c1").WithLocation(10, 27), - // (20,27): warning CS8602: Possible dereference of a null reference. + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(IEnumerable)c1").WithLocation(16, 27), + // (29,27): warning CS8602: Possible dereference of a null reference. // foreach (var x in c2) // 3 - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c2").WithLocation(20, 27), - // (23,27): warning CS8602: Possible dereference of a null reference. + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c2").WithLocation(29, 27), + // (35,27): warning CS8602: Possible dereference of a null reference. // foreach (var y in (IEnumerable?)c2) // 4 - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(IEnumerable?)c2").WithLocation(23, 27)); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(IEnumerable?)c2").WithLocation(35, 27)); } [WorkItem(23493, "https://github.com/dotnet/roslyn/issues/23493")] @@ -38978,18 +40008,27 @@ static void F1(T t1) where T : class?, IEnumerable? foreach (var x in t1) // 1 { } + } + static void F2(T t1) where T : class?, IEnumerable? + { foreach (var y in (IEnumerable?)t1) // 2 { } + } + static void F3(T t1) where T : class?, IEnumerable? + { foreach (var z in (IEnumerable)t1) // 3 { } } - static void F2(T t2) where T : class? + static void F4(T t2) where T : class? { foreach (var w in (IEnumerable?)t2) // 4 { } + } + static void F5(T t2) where T : class? + { foreach (var v in (IEnumerable)t2) // 5 { } @@ -39000,24 +40039,24 @@ static void F2(T t2) where T : class? // (7,27): warning CS8602: Possible dereference of a null reference. // foreach (var x in t1) // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "t1").WithLocation(7, 27), - // (10,27): warning CS8602: Possible dereference of a null reference. - // foreach (var y in (IEnumerable?)t1) // 2 - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(IEnumerable?)t1").WithLocation(10, 27), - // (13,27): warning CS8600: Converting null literal or possible null value to non-nullable type. - // foreach (var z in (IEnumerable)t1) // 3 - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(IEnumerable)t1").WithLocation(13, 27), // (13,27): warning CS8602: Possible dereference of a null reference. + // foreach (var y in (IEnumerable?)t1) // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(IEnumerable?)t1").WithLocation(13, 27), + // (19,27): warning CS8600: Converting null literal or possible null value to non-nullable type. // foreach (var z in (IEnumerable)t1) // 3 - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(IEnumerable)t1").WithLocation(13, 27), + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(IEnumerable)t1").WithLocation(19, 27), // (19,27): warning CS8602: Possible dereference of a null reference. + // foreach (var z in (IEnumerable)t1) // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(IEnumerable)t1").WithLocation(19, 27), + // (25,27): warning CS8602: Possible dereference of a null reference. // foreach (var w in (IEnumerable?)t2) // 4 - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(IEnumerable?)t2").WithLocation(19, 27), - // (22,27): warning CS8600: Converting null literal or possible null value to non-nullable type. + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(IEnumerable?)t2").WithLocation(25, 27), + // (31,27): warning CS8600: Converting null literal or possible null value to non-nullable type. // foreach (var v in (IEnumerable)t2) // 5 - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(IEnumerable)t2").WithLocation(22, 27), - // (22,27): warning CS8602: Possible dereference of a null reference. + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(IEnumerable)t2").WithLocation(31, 27), + // (31,27): warning CS8602: Possible dereference of a null reference. // foreach (var v in (IEnumerable)t2) // 5 - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(IEnumerable)t2").WithLocation(22, 27)); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(IEnumerable)t2").WithLocation(31, 27)); } [WorkItem(23493, "https://github.com/dotnet/roslyn/issues/23493")] @@ -39034,18 +40073,30 @@ static void F1(T t1) where T : IEnumerable? foreach (var x in t1) // 1 { } + foreach (var x in t1) // no cascade + { + } + } + static void F2(T t1) where T : IEnumerable? + { foreach (var w in (IEnumerable?)t1) // 2 { } + } + static void F3(T t1) where T : IEnumerable? + { foreach (var v in (IEnumerable)t1) // 3 { } } - static void F2(T t2) + static void F4(T t2) { foreach (var y in (IEnumerable?)t2) // 4 { } + } + static void F5(T t2) + { foreach (var z in (IEnumerable)t2) // 5 { } @@ -39056,15 +40107,15 @@ static void F2(T t2) // (7,27): warning CS8602: Possible dereference of a null reference. // foreach (var x in t1) // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "t1").WithLocation(7, 27), - // (10,27): warning CS8602: Possible dereference of a null reference. + // (16,27): warning CS8602: Possible dereference of a null reference. // foreach (var w in (IEnumerable?)t1) // 2 - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(IEnumerable?)t1").WithLocation(10, 27), - // (13,27): warning CS8600: Converting null literal or possible null value to non-nullable type. + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(IEnumerable?)t1").WithLocation(16, 27), + // (22,27): warning CS8600: Converting null literal or possible null value to non-nullable type. // foreach (var v in (IEnumerable)t1) // 3 - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(IEnumerable)t1").WithLocation(13, 27), - // (13,27): warning CS8602: Possible dereference of a null reference. + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(IEnumerable)t1").WithLocation(22, 27), + // (22,27): warning CS8602: Possible dereference of a null reference. // foreach (var v in (IEnumerable)t1) // 3 - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(IEnumerable)t1").WithLocation(13, 27)); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(IEnumerable)t1").WithLocation(22, 27)); } [Fact] @@ -48271,6 +49322,12 @@ static void M(U u) // (16,9): warning CS8602: Possible dereference of a null reference. // x3.ToString(); // 3 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x3").WithLocation(16, 9), + // (21,9): warning CS8602: Possible dereference of a null reference. + // x4.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x4").WithLocation(21, 9), + // (24,9): warning CS8602: Possible dereference of a null reference. + // x5.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x5").WithLocation(24, 9), // (27,9): warning CS8602: Possible dereference of a null reference. // x6.ToString(); Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x6").WithLocation(27, 9)); @@ -48353,7 +49410,7 @@ static void F2(T x2, T[] a2) x3.ToString(); x3!.ToString(); var a3 = new[] { new T() }; - a3[0].ToString(); + a3[0].ToString(); // warn 6 } static T F4(T x4) { @@ -48389,7 +49446,10 @@ static T F4(T x4) Diagnostic(ErrorCode.HDN_NullCheckIsProbablyAlwaysTrue, "x2 != null").WithLocation(21, 13), // (24,9): warning CS8602: Possible dereference of a null reference. // a2[0].ToString(); // warn 5 - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "a2[0]").WithLocation(24, 9)); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "a2[0]").WithLocation(24, 9), + // (32,9): warning CS8602: Possible dereference of a null reference. + // a3[0].ToString(); // warn 6 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "a3[0]").WithLocation(32, 9)); } [Fact] @@ -51363,10 +52423,18 @@ class A class B {} "; - var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesFalse()); + assertNonNullTypesContext(NullableContextOptions.Disable); + assertNonNullTypesContext(NullableContextOptions.Warnings); + assertNonNullTypesContext(NullableContextOptions.SafeOnlyWarnings); - var f1 = comp.GetMember("A.F1"); - Assert.Equal("B!", f1.Type.ToTestDisplayString(includeNonNullable: true)); + void assertNonNullTypesContext(NullableContextOptions nullableContextOptions) + { + CSharpCompilationOptions compilationOptions = WithNonNullTypes(nullableContextOptions); + var comp = CreateCompilation(new[] { source }, options: compilationOptions); + + var f1 = comp.GetMember("A.F1"); + Assert.Equal("B!", f1.Type.ToTestDisplayString(includeNonNullable: true)); + } } [Fact] @@ -51409,12 +52477,20 @@ class A class B {} "; - var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesFalse()); + assertNonNullTypesContext(NullableContextOptions.Disable); + assertNonNullTypesContext(NullableContextOptions.Warnings); + assertNonNullTypesContext(NullableContextOptions.SafeOnlyWarnings); - var f1 = comp.GetMember("A.F1"); - Assert.Equal("B?", f1.Type.ToTestDisplayString(includeNonNullable: true)); + void assertNonNullTypesContext(NullableContextOptions nullableContextOptions) + { + CSharpCompilationOptions compilationOptions = WithNonNullTypes(nullableContextOptions); + var comp = CreateCompilation(new[] { source }, options: compilationOptions); - comp.VerifyDiagnostics(); + var f1 = comp.GetMember("A.F1"); + Assert.Equal("B?", f1.Type.ToTestDisplayString(includeNonNullable: true)); + + comp.VerifyDiagnostics(); + } } [Fact] @@ -51461,10 +52537,18 @@ class A F1; } "; - var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesFalse()); + assertNonNullTypesContext(NullableContextOptions.Disable); + assertNonNullTypesContext(NullableContextOptions.Warnings); + assertNonNullTypesContext(NullableContextOptions.SafeOnlyWarnings); - var f1 = comp.GetMember("A.F1"); - Assert.Equal("System.String!", f1.Type.ToTestDisplayString(includeNonNullable: true)); + void assertNonNullTypesContext(NullableContextOptions nullableContextOptions) + { + CSharpCompilationOptions compilationOptions = WithNonNullTypes(nullableContextOptions); + var comp = CreateCompilation(new[] { source }, options: compilationOptions); + + var f1 = comp.GetMember("A.F1"); + Assert.Equal("System.String!", f1.Type.ToTestDisplayString(includeNonNullable: true)); + } } [Fact] @@ -51506,10 +52590,18 @@ class A class B {} "; - var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesFalse()); + assertNonNullTypesContext(NullableContextOptions.Disable); + assertNonNullTypesContext(NullableContextOptions.Warnings); + assertNonNullTypesContext(NullableContextOptions.SafeOnlyWarnings); - var f1 = comp.GetMember("A.F1"); - Assert.Equal("B[]!", f1.Type.ToTestDisplayString(includeNonNullable: true)); + void assertNonNullTypesContext(NullableContextOptions nullableContextOptions) + { + CSharpCompilationOptions compilationOptions = WithNonNullTypes(nullableContextOptions); + var comp = CreateCompilation(new[] { source }, options: compilationOptions); + + var f1 = comp.GetMember("A.F1"); + Assert.Equal("B[]!", f1.Type.ToTestDisplayString(includeNonNullable: true)); + } } [Fact] @@ -51554,10 +52646,18 @@ class A class B {} "; - var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesFalse()); + assertNonNullTypesContext(NullableContextOptions.Disable); + assertNonNullTypesContext(NullableContextOptions.Warnings); + assertNonNullTypesContext(NullableContextOptions.SafeOnlyWarnings); - var f1 = comp.GetMember("A.F1"); - Assert.Equal(NullableAnnotation.NotAnnotated, f1.Type.NullableAnnotation); + void assertNonNullTypesContext(NullableContextOptions nullableContextOptions) + { + CSharpCompilationOptions compilationOptions = WithNonNullTypes(nullableContextOptions); + var comp = CreateCompilation(new[] { source }, options: compilationOptions); + + var f1 = comp.GetMember("A.F1"); + Assert.Equal(NullableAnnotation.NotAnnotated, f1.Type.NullableAnnotation); + } } [Fact] @@ -51602,10 +52702,18 @@ class A class B {} "; - var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesFalse()); + assertNonNullTypesContext(NullableContextOptions.Disable); + assertNonNullTypesContext(NullableContextOptions.Warnings); + assertNonNullTypesContext(NullableContextOptions.SafeOnlyWarnings); - var f1 = comp.GetMember("A.F1"); - Assert.Equal("B!", f1.Type.ToTestDisplayString(includeNonNullable: true)); + void assertNonNullTypesContext(NullableContextOptions nullableContextOptions) + { + CSharpCompilationOptions compilationOptions = WithNonNullTypes(nullableContextOptions); + var comp = CreateCompilation(new[] { source }, options: compilationOptions); + + var f1 = comp.GetMember("A.F1"); + Assert.Equal("B!", f1.Type.ToTestDisplayString(includeNonNullable: true)); + } } [Fact] @@ -51653,13 +52761,21 @@ void Test() class var {} "; - var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesFalse()); + assertNonNullTypesContext(NullableContextOptions.Disable); + assertNonNullTypesContext(NullableContextOptions.Warnings); + assertNonNullTypesContext(NullableContextOptions.SafeOnlyWarnings); - var tree = comp.SyntaxTrees.Single(); - var model = comp.GetSemanticModel(tree); + void assertNonNullTypesContext(NullableContextOptions nullableContextOptions) + { + CSharpCompilationOptions compilationOptions = WithNonNullTypes(nullableContextOptions); + var comp = CreateCompilation(new[] { source }, options: compilationOptions); - var decl = tree.GetRoot().DescendantNodes().OfType().Single(); - Assert.Equal("var!", ((LocalSymbol)model.GetDeclaredSymbol(decl.Designation)).Type.ToTestDisplayString(includeNonNullable: true)); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var decl = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal("var!", ((LocalSymbol)model.GetDeclaredSymbol(decl.Designation)).Type.ToTestDisplayString(includeNonNullable: true)); + } } [Fact] @@ -51704,10 +52820,18 @@ class A where T : { } "; - var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesFalse()); + assertNonNullTypesContext(NullableContextOptions.Disable); + assertNonNullTypesContext(NullableContextOptions.Warnings); + assertNonNullTypesContext(NullableContextOptions.SafeOnlyWarnings); - var a = comp.GetTypeByMetadataName("A`1"); - Assert.Equal("A where T : class!", a.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints.WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeNonNullableTypeModifier))); + void assertNonNullTypesContext(NullableContextOptions nullableContextOptions) + { + CSharpCompilationOptions compilationOptions = WithNonNullTypes(nullableContextOptions); + var comp = CreateCompilation(new[] { source }, options: compilationOptions); + + var a = comp.GetTypeByMetadataName("A`1"); + Assert.Equal("A where T : class!", a.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints.WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeNonNullableTypeModifier))); + } } [Fact] @@ -51740,12 +52864,20 @@ class A where T : class { } "; - var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesFalse()); + assertNonNullTypesContext(NullableContextOptions.Disable); + assertNonNullTypesContext(NullableContextOptions.Warnings); + assertNonNullTypesContext(NullableContextOptions.SafeOnlyWarnings); - var a = comp.GetTypeByMetadataName("A`1"); - Assert.Equal("A where T : class?", a.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints.WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeNonNullableTypeModifier))); + void assertNonNullTypesContext(NullableContextOptions nullableContextOptions) + { + CSharpCompilationOptions compilationOptions = WithNonNullTypes(nullableContextOptions); + var comp = CreateCompilation(new[] { source }, options: compilationOptions); - comp.VerifyDiagnostics(); + var a = comp.GetTypeByMetadataName("A`1"); + Assert.Equal("A where T : class?", a.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints.WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeNonNullableTypeModifier))); + + comp.VerifyDiagnostics(); + } } [Fact] @@ -51786,10 +52918,18 @@ class A where T : class unmanaged {} "; - var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesFalse()); + assertNonNullTypesContext(NullableContextOptions.Disable); + assertNonNullTypesContext(NullableContextOptions.Warnings); + assertNonNullTypesContext(NullableContextOptions.SafeOnlyWarnings); - var a = comp.GetTypeByMetadataName("A`1"); - Assert.Equal("A where T : unmanaged!", a.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints.WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeNonNullableTypeModifier))); + void assertNonNullTypesContext(NullableContextOptions nullableContextOptions) + { + CSharpCompilationOptions compilationOptions = WithNonNullTypes(nullableContextOptions); + var comp = CreateCompilation(new[] { source }, options: compilationOptions); + + var a = comp.GetTypeByMetadataName("A`1"); + Assert.Equal("A where T : unmanaged!", a.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints.WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeNonNullableTypeModifier))); + } } [Fact] @@ -51833,12 +52973,13 @@ void Tests() " ); - AssertGetSpeculativeTypeInfo(source, WithNonNullTypesTrue(), type, "A"); + AssertGetSpeculativeTypeInfo(source, NullableContextOptions.Enable, type, "A"); + AssertGetSpeculativeTypeInfo(source, NullableContextOptions.SafeOnly, type, "A"); } - private static void AssertGetSpeculativeTypeInfo(string source, CSharpCompilationOptions options, TypeSyntax type, string expected) + private static void AssertGetSpeculativeTypeInfo(string source, NullableContextOptions nullableContextOptions, TypeSyntax type, string expected) { - var comp = CreateCompilation(new[] { source }, options: options); + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypes(nullableContextOptions)); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); @@ -51870,7 +53011,9 @@ void Tests() " ); - AssertGetSpeculativeTypeInfo(source, WithNonNullTypesFalse(), type, "A"); + AssertGetSpeculativeTypeInfo(source, NullableContextOptions.Disable, type, "A"); + AssertGetSpeculativeTypeInfo(source, NullableContextOptions.Warnings, type, "A"); + AssertGetSpeculativeTypeInfo(source, NullableContextOptions.SafeOnlyWarnings, type, "A"); } [Fact] @@ -51895,7 +53038,8 @@ void Tests() " ); - AssertGetSpeculativeTypeInfo(source, WithNonNullTypesTrue(), type, "A"); + AssertGetSpeculativeTypeInfo(source, NullableContextOptions.Enable, type, "A"); + AssertGetSpeculativeTypeInfo(source, NullableContextOptions.SafeOnly, type, "A"); } [Fact] @@ -51920,7 +53064,9 @@ void Tests() " ); - AssertGetSpeculativeTypeInfo(source, WithNonNullTypesFalse(), type, "A"); + AssertGetSpeculativeTypeInfo(source, NullableContextOptions.Disable, type, "A"); + AssertGetSpeculativeTypeInfo(source, NullableContextOptions.Warnings, type, "A"); + AssertGetSpeculativeTypeInfo(source, NullableContextOptions.SafeOnlyWarnings, type, "A"); } [Fact] @@ -51947,7 +53093,8 @@ void Tests() " ); - AssertGetSpeculativeTypeInfo(source, WithNonNullTypesTrue(), type, "A"); + AssertGetSpeculativeTypeInfo(source, NullableContextOptions.Enable, type, "A"); + AssertGetSpeculativeTypeInfo(source, NullableContextOptions.SafeOnly, type, "A"); } [Fact] @@ -51974,7 +53121,9 @@ void Tests() " ); - AssertGetSpeculativeTypeInfo(source, WithNonNullTypesFalse(), type, "A"); + AssertGetSpeculativeTypeInfo(source, NullableContextOptions.Disable, type, "A"); + AssertGetSpeculativeTypeInfo(source, NullableContextOptions.Warnings, type, "A"); + AssertGetSpeculativeTypeInfo(source, NullableContextOptions.SafeOnlyWarnings, type, "A"); } [Fact] @@ -52002,7 +53151,8 @@ void Tests() " ); - AssertGetSpeculativeTypeInfo(source, WithNonNullTypesTrue(), type, "A"); + AssertGetSpeculativeTypeInfo(source, NullableContextOptions.Enable, type, "A"); + AssertGetSpeculativeTypeInfo(source, NullableContextOptions.SafeOnly, type, "A"); } [Fact] @@ -52030,7 +53180,9 @@ void Tests() " ); - AssertGetSpeculativeTypeInfo(source, WithNonNullTypesFalse(), type, "A"); + AssertGetSpeculativeTypeInfo(source, NullableContextOptions.Disable, type, "A"); + AssertGetSpeculativeTypeInfo(source, NullableContextOptions.Warnings, type, "A"); + AssertGetSpeculativeTypeInfo(source, NullableContextOptions.SafeOnlyWarnings, type, "A"); } [Fact] @@ -52054,12 +53206,13 @@ void Tests() " ); - AssertTryGetSpeculativeSemanticModel(source, WithNonNullTypesTrue(), type, "A"); + AssertTryGetSpeculativeSemanticModel(source, NullableContextOptions.Enable, type, "A"); + AssertTryGetSpeculativeSemanticModel(source, NullableContextOptions.SafeOnly, type, "A"); } - private static void AssertTryGetSpeculativeSemanticModel(string source, CSharpCompilationOptions options, TypeSyntax type, string expected) + private static void AssertTryGetSpeculativeSemanticModel(string source, NullableContextOptions nullableContextOptions, TypeSyntax type, string expected) { - var comp = CreateCompilation(new[] { source }, options: options); + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypes(nullableContextOptions)); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); @@ -52092,7 +53245,9 @@ void Tests() " ); - AssertTryGetSpeculativeSemanticModel(source, WithNonNullTypesFalse(), type, "A"); + AssertTryGetSpeculativeSemanticModel(source, NullableContextOptions.Disable, type, "A"); + AssertTryGetSpeculativeSemanticModel(source, NullableContextOptions.Warnings, type, "A"); + AssertTryGetSpeculativeSemanticModel(source, NullableContextOptions.SafeOnlyWarnings, type, "A"); } [Fact] @@ -52117,7 +53272,8 @@ void Tests() " ); - AssertTryGetSpeculativeSemanticModel(source, WithNonNullTypesTrue(), type, "A"); + AssertTryGetSpeculativeSemanticModel(source, NullableContextOptions.Enable, type, "A"); + AssertTryGetSpeculativeSemanticModel(source, NullableContextOptions.SafeOnly, type, "A"); } [Fact] @@ -52142,7 +53298,9 @@ void Tests() " ); - AssertTryGetSpeculativeSemanticModel(source, WithNonNullTypesFalse(), type, "A"); + AssertTryGetSpeculativeSemanticModel(source, NullableContextOptions.Disable, type, "A"); + AssertTryGetSpeculativeSemanticModel(source, NullableContextOptions.Warnings, type, "A"); + AssertTryGetSpeculativeSemanticModel(source, NullableContextOptions.SafeOnlyWarnings, type, "A"); } [Fact] @@ -52169,7 +53327,8 @@ void Tests() " ); - AssertTryGetSpeculativeSemanticModel(source, WithNonNullTypesTrue(), type, "A"); + AssertTryGetSpeculativeSemanticModel(source, NullableContextOptions.Enable, type, "A"); + AssertTryGetSpeculativeSemanticModel(source, NullableContextOptions.SafeOnly, type, "A"); } [Fact] @@ -52196,7 +53355,9 @@ void Tests() " ); - AssertTryGetSpeculativeSemanticModel(source, WithNonNullTypesFalse(), type, "A"); + AssertTryGetSpeculativeSemanticModel(source, NullableContextOptions.Disable, type, "A"); + AssertTryGetSpeculativeSemanticModel(source, NullableContextOptions.Warnings, type, "A"); + AssertTryGetSpeculativeSemanticModel(source, NullableContextOptions.SafeOnlyWarnings, type, "A"); } [Fact] @@ -52224,7 +53385,8 @@ void Tests() " ); - AssertTryGetSpeculativeSemanticModel(source, WithNonNullTypesTrue(), type, "A"); + AssertTryGetSpeculativeSemanticModel(source, NullableContextOptions.Enable, type, "A"); + AssertTryGetSpeculativeSemanticModel(source, NullableContextOptions.SafeOnly, type, "A"); } [Fact] @@ -52252,7 +53414,9 @@ void Tests() " ); - AssertTryGetSpeculativeSemanticModel(source, WithNonNullTypesFalse(), type, "A"); + AssertTryGetSpeculativeSemanticModel(source, NullableContextOptions.Disable, type, "A"); + AssertTryGetSpeculativeSemanticModel(source, NullableContextOptions.Warnings, type, "A"); + AssertTryGetSpeculativeSemanticModel(source, NullableContextOptions.SafeOnlyWarnings, type, "A"); } [Fact] @@ -52273,10 +53437,18 @@ class A class C {} "; - var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesFalse()); + assertNonNullTypesContext(NullableContextOptions.Disable); + assertNonNullTypesContext(NullableContextOptions.Warnings); + assertNonNullTypesContext(NullableContextOptions.SafeOnlyWarnings); - var f1 = comp.GetMember("A.F1"); - Assert.Equal("C!", f1.Type.ToTestDisplayString(includeNonNullable: true)); + void assertNonNullTypesContext(NullableContextOptions nullableContextOptions) + { + CSharpCompilationOptions compilationOptions = WithNonNullTypes(nullableContextOptions); + var comp = CreateCompilation(new[] { source }, options: compilationOptions); + + var f1 = comp.GetMember("A.F1"); + Assert.Equal("C!", f1.Type.ToTestDisplayString(includeNonNullable: true)); + } } [Fact] @@ -52324,10 +53496,18 @@ namespace C class B {} } "; - var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesFalse()); + assertNonNullTypesContext(NullableContextOptions.Disable); + assertNonNullTypesContext(NullableContextOptions.Warnings); + assertNonNullTypesContext(NullableContextOptions.SafeOnlyWarnings); - var f1 = comp.GetMember("A.F1"); - Assert.Equal("C.B!", f1.Type.ToTestDisplayString(includeNonNullable: true)); + void assertNonNullTypesContext(NullableContextOptions nullableContextOptions) + { + CSharpCompilationOptions compilationOptions = WithNonNullTypes(nullableContextOptions); + var comp = CreateCompilation(new[] { source }, options: compilationOptions); + + var f1 = comp.GetMember("A.F1"); + Assert.Equal("C.B!", f1.Type.ToTestDisplayString(includeNonNullable: true)); + } } [Fact] @@ -52378,10 +53558,18 @@ namespace C class B {} } "; - var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesFalse()); + assertNonNullTypesContext(NullableContextOptions.Disable); + assertNonNullTypesContext(NullableContextOptions.Warnings); + assertNonNullTypesContext(NullableContextOptions.SafeOnlyWarnings); - var f1 = comp.GetMember("A.F1"); - Assert.Equal("C.B!", f1.Type.ToTestDisplayString(includeNonNullable: true)); + void assertNonNullTypesContext(NullableContextOptions nullableContextOptions) + { + CSharpCompilationOptions compilationOptions = WithNonNullTypes(nullableContextOptions); + var comp = CreateCompilation(new[] { source }, options: compilationOptions); + + var f1 = comp.GetMember("A.F1"); + Assert.Equal("C.B!", f1.Type.ToTestDisplayString(includeNonNullable: true)); + } } [Fact] @@ -52428,10 +53616,18 @@ class A class B {} "; - var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesFalse()); + assertNonNullTypesContext(NullableContextOptions.Disable); + assertNonNullTypesContext(NullableContextOptions.Warnings); + assertNonNullTypesContext(NullableContextOptions.SafeOnlyWarnings); - var f1 = comp.GetMember("A.F1"); - Assert.Equal("B!", f1.Type.ToTestDisplayString(includeNonNullable: true)); + void assertNonNullTypesContext(NullableContextOptions nullableContextOptions) + { + CSharpCompilationOptions compilationOptions = WithNonNullTypes(nullableContextOptions); + var comp = CreateCompilation(new[] { source }, options: compilationOptions); + + var f1 = comp.GetMember("A.F1"); + Assert.Equal("B!", f1.Type.ToTestDisplayString(includeNonNullable: true)); + } } [Fact] @@ -52474,12 +53670,20 @@ class A class B {} "; - var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesFalse()); + assertNonNullTypesContext(NullableContextOptions.Disable); + assertNonNullTypesContext(NullableContextOptions.Warnings); + assertNonNullTypesContext(NullableContextOptions.SafeOnlyWarnings); - var f1 = comp.GetMember("A.F1"); - Assert.Equal("B?", f1.Type.ToTestDisplayString(includeNonNullable: true)); + void assertNonNullTypesContext(NullableContextOptions nullableContextOptions) + { + CSharpCompilationOptions compilationOptions = WithNonNullTypes(nullableContextOptions); + var comp = CreateCompilation(new[] { source }, options: compilationOptions); - comp.VerifyDiagnostics(); + var f1 = comp.GetMember("A.F1"); + Assert.Equal("B?", f1.Type.ToTestDisplayString(includeNonNullable: true)); + + comp.VerifyDiagnostics(); + } } [Fact] @@ -52542,16 +53746,24 @@ class A class B {} "; - var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesFalse()); + assertNonNullTypesContext(NullableContextOptions.Disable); + assertNonNullTypesContext(NullableContextOptions.Warnings); + assertNonNullTypesContext(NullableContextOptions.SafeOnlyWarnings); - var f1 = comp.GetMember("A.F1"); - Assert.Equal("B?", f1.Type.ToTestDisplayString(includeNonNullable: true)); + void assertNonNullTypesContext(NullableContextOptions nullableContextOptions) + { + CSharpCompilationOptions compilationOptions = WithNonNullTypes(nullableContextOptions); + var comp = CreateCompilation(new[] { source }, options: compilationOptions); - comp.VerifyDiagnostics( - // (8,6): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' context. - // ? - Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(8, 6) - ); + var f1 = comp.GetMember("A.F1"); + Assert.Equal("B?", f1.Type.ToTestDisplayString(includeNonNullable: true)); + + comp.VerifyDiagnostics( + // (8,6): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' context. + // ? + Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(8, 6) + ); + } } [Fact] @@ -52572,16 +53784,24 @@ class A class B {} "; - var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesFalse()); + assertNonNullTypesContext(NullableContextOptions.Disable); + assertNonNullTypesContext(NullableContextOptions.Warnings); + assertNonNullTypesContext(NullableContextOptions.SafeOnlyWarnings); - var f1 = comp.GetMember("A.F1"); - Assert.Equal("B?", f1.Type.ToTestDisplayString(includeNonNullable: true)); + void assertNonNullTypesContext(NullableContextOptions nullableContextOptions) + { + CSharpCompilationOptions compilationOptions = WithNonNullTypes(nullableContextOptions); + var comp = CreateCompilation(new[] { source }, options: compilationOptions); - comp.VerifyDiagnostics( - // (8,6): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' context. - // ? - Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(8, 6) - ); + var f1 = comp.GetMember("A.F1"); + Assert.Equal("B?", f1.Type.ToTestDisplayString(includeNonNullable: true)); + + comp.VerifyDiagnostics( + // (8,6): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' context. + // ? + Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(8, 6) + ); + } } [Fact] @@ -52601,16 +53821,24 @@ class A class B {} "; - var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesFalse()); + assertNonNullTypesContext(NullableContextOptions.Disable); + assertNonNullTypesContext(NullableContextOptions.Warnings); + assertNonNullTypesContext(NullableContextOptions.SafeOnlyWarnings); - var f1 = comp.GetMember("A.F1"); - Assert.Equal("B?", f1.Type.ToTestDisplayString(includeNonNullable: true)); + void assertNonNullTypesContext(NullableContextOptions nullableContextOptions) + { + CSharpCompilationOptions compilationOptions = WithNonNullTypes(nullableContextOptions); + var comp = CreateCompilation(new[] { source }, options: compilationOptions); - comp.VerifyDiagnostics( - // (8,6): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' context. - // ? - Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(8, 6) - ); + var f1 = comp.GetMember("A.F1"); + Assert.Equal("B?", f1.Type.ToTestDisplayString(includeNonNullable: true)); + + comp.VerifyDiagnostics( + // (8,6): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' context. + // ? + Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(8, 6) + ); + } } [Fact] @@ -52631,16 +53859,24 @@ class A class B {} "; - var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesFalse()); + assertNonNullTypesContext(NullableContextOptions.Disable); + assertNonNullTypesContext(NullableContextOptions.Warnings); + assertNonNullTypesContext(NullableContextOptions.SafeOnlyWarnings); - var f1 = comp.GetMember("A.F1"); - Assert.Equal("B?", f1.Type.ToTestDisplayString(includeNonNullable: true)); + void assertNonNullTypesContext(NullableContextOptions nullableContextOptions) + { + CSharpCompilationOptions compilationOptions = WithNonNullTypes(nullableContextOptions); + var comp = CreateCompilation(new[] { source }, options: compilationOptions); - comp.VerifyDiagnostics( - // (8,6): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' context. - // ? - Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(8, 6) - ); + var f1 = comp.GetMember("A.F1"); + Assert.Equal("B?", f1.Type.ToTestDisplayString(includeNonNullable: true)); + + comp.VerifyDiagnostics( + // (8,6): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' context. + // ? + Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(8, 6) + ); + } } [Fact] @@ -52660,12 +53896,20 @@ class A class B {} "; - var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesFalse()); + assertNonNullTypesContext(NullableContextOptions.Disable); + assertNonNullTypesContext(NullableContextOptions.Warnings); + assertNonNullTypesContext(NullableContextOptions.SafeOnlyWarnings); - var f1 = comp.GetMember("A.F1"); - Assert.Equal("B", f1.Type.ToTestDisplayString(includeNonNullable: true)); + void assertNonNullTypesContext(NullableContextOptions nullableContextOptions) + { + CSharpCompilationOptions compilationOptions = WithNonNullTypes(nullableContextOptions); + var comp = CreateCompilation(new[] { source }, options: compilationOptions); - comp.VerifyDiagnostics(); + var f1 = comp.GetMember("A.F1"); + Assert.Equal("B", f1.Type.ToTestDisplayString(includeNonNullable: true)); + + comp.VerifyDiagnostics(); + } } [Fact] @@ -52685,12 +53929,20 @@ class A class B {} "; - var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesFalse()); + assertNonNullTypesContext(NullableContextOptions.Disable); + assertNonNullTypesContext(NullableContextOptions.Warnings); + assertNonNullTypesContext(NullableContextOptions.SafeOnlyWarnings); - var f1 = comp.GetMember("A.F1"); - Assert.Equal("B", f1.Type.ToTestDisplayString(includeNonNullable: true)); + void assertNonNullTypesContext(NullableContextOptions nullableContextOptions) + { + CSharpCompilationOptions compilationOptions = WithNonNullTypes(nullableContextOptions); + var comp = CreateCompilation(new[] { source }, options: compilationOptions); - comp.VerifyDiagnostics(); + var f1 = comp.GetMember("A.F1"); + Assert.Equal("B", f1.Type.ToTestDisplayString(includeNonNullable: true)); + + comp.VerifyDiagnostics(); + } } [Fact] @@ -52708,12 +53960,20 @@ class A class B {} "; - var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesFalse()); + assertNonNullTypesContext(NullableContextOptions.Disable); + assertNonNullTypesContext(NullableContextOptions.Warnings); + assertNonNullTypesContext(NullableContextOptions.SafeOnlyWarnings); - var f1 = comp.GetMember("A.F1"); - Assert.Equal("B", f1.Type.ToTestDisplayString(includeNonNullable: true)); + void assertNonNullTypesContext(NullableContextOptions nullableContextOptions) + { + CSharpCompilationOptions compilationOptions = WithNonNullTypes(nullableContextOptions); + var comp = CreateCompilation(new[] { source }, options: compilationOptions); - comp.VerifyDiagnostics(); + var f1 = comp.GetMember("A.F1"); + Assert.Equal("B", f1.Type.ToTestDisplayString(includeNonNullable: true)); + + comp.VerifyDiagnostics(); + } } [Fact] @@ -52733,12 +53993,20 @@ class A class B {} "; - var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesFalse()); + assertNonNullTypesContext(NullableContextOptions.Disable); + assertNonNullTypesContext(NullableContextOptions.Warnings); + assertNonNullTypesContext(NullableContextOptions.SafeOnlyWarnings); - var f1 = comp.GetMember("A.F1"); - Assert.Equal("B", f1.Type.ToTestDisplayString(includeNonNullable: true)); + void assertNonNullTypesContext(NullableContextOptions nullableContextOptions) + { + CSharpCompilationOptions compilationOptions = WithNonNullTypes(nullableContextOptions); + var comp = CreateCompilation(new[] { source }, options: compilationOptions); - comp.VerifyDiagnostics(); + var f1 = comp.GetMember("A.F1"); + Assert.Equal("B", f1.Type.ToTestDisplayString(includeNonNullable: true)); + + comp.VerifyDiagnostics(); + } } [Fact] @@ -53682,14 +54950,29 @@ class Program static void F1(object[]? x1, object[]? y1) { foreach (var x in x1) { } // 1 + } + static void F2(object[]? x1, object[]? y1) + { foreach (var x in x1) { } + } + static void F3(object[]? x1, object[]? y1) + { foreach (var y in y1) { } // 2 + } + static void F4(object[]? x1, object[]? y1) + { y1.GetEnumerator(); } - static void F2(Enumerable? x2, Enumerable? y2) + static void F5(Enumerable? x2, Enumerable? y2) { foreach (var x in x2) { } // 3 + } + static void F6(Enumerable? x2, Enumerable? y2) + { foreach (var x in x2) { } + } + static void F7(Enumerable? x2, Enumerable? y2) + { y2.GetEnumerator(); // 4 foreach (var y in y2) { } } @@ -53700,24 +54983,24 @@ static void F2(Enumerable? x2, Enumerable? y2) // (9,27): warning CS8602: Possible dereference of a null reference. // foreach (var x in x1) { } // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x1").WithLocation(9, 27), - // (10,27): warning CS8602: Possible dereference of a null reference. + // (13,27): warning CS8602: Possible dereference of a null reference. // foreach (var x in x1) { } - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x1").WithLocation(10, 27), - // (11,27): warning CS8602: Possible dereference of a null reference. + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x1").WithLocation(13, 27), + // (17,27): warning CS8602: Possible dereference of a null reference. // foreach (var y in y1) { } // 2 - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "y1").WithLocation(11, 27), - // (12,9): warning CS8602: Possible dereference of a null reference. + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "y1").WithLocation(17, 27), + // (21,9): warning CS8602: Possible dereference of a null reference. // y1.GetEnumerator(); - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "y1").WithLocation(12, 9), - // (16,27): warning CS8602: Possible dereference of a null reference. + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "y1").WithLocation(21, 9), + // (25,27): warning CS8602: Possible dereference of a null reference. // foreach (var x in x2) { } // 3 - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x2").WithLocation(16, 27), - // (17,27): warning CS8602: Possible dereference of a null reference. + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x2").WithLocation(25, 27), + // (29,27): warning CS8602: Possible dereference of a null reference. // foreach (var x in x2) { } - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x2").WithLocation(17, 27), - // (18,9): warning CS8602: Possible dereference of a null reference. + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x2").WithLocation(29, 27), + // (33,9): warning CS8602: Possible dereference of a null reference. // y2.GetEnumerator(); // 4 - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "y2").WithLocation(18, 9)); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "y2").WithLocation(33, 9)); } [Fact] @@ -54249,11 +55532,15 @@ public void NullabilityOfTypeParameters_007() var source = @" class Outer { - void M0(T x0, T y0) + T M0(T x0, T y0) { - if (x0 == null) return; + if (x0 == null) throw null; M2(x0) = x0; - M2(x0) = y0; + M2(x0) = y0; + + M2(x0).ToString(); + M2(x0).ToString(); + throw null; } void M1(object? x1, object? y1) @@ -54266,14 +55553,29 @@ void M1(object? x1, object? y1) ref U M2(U a) where U : object => throw null; } "; - + // Note: you cannot pass a `T` to a `U : object` even if the `T` was null-tested CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (8,18): warning CS8601: Possible null reference assignment. - // M2(x0) = y0; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "y0").WithLocation(8, 18), - // (15,18): warning CS8601: Possible null reference assignment. + // (7,9): warning CS8631: The type 'T' cannot be used as type parameter 'U' in the generic type or method 'Outer.M2(U)'. Nullability of type argument 'T' doesn't match constraint type 'object'. + // M2(x0) = x0; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "M2").WithArguments("Outer.M2(U)", "object", "U", "T").WithLocation(7, 9), + // (8,9): warning CS8631: The type 'T' cannot be used as type parameter 'U' in the generic type or method 'Outer.M2(U)'. Nullability of type argument 'T' doesn't match constraint type 'object'. + // M2(x0) = y0; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "M2").WithArguments("Outer.M2(U)", "object", "U", "T").WithLocation(8, 9), + // (10,9): warning CS8631: The type 'T' cannot be used as type parameter 'U' in the generic type or method 'Outer.M2(U)'. Nullability of type argument 'T' doesn't match constraint type 'object'. + // M2(x0).ToString(); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "M2").WithArguments("Outer.M2(U)", "object", "U", "T").WithLocation(10, 9), + // (10,9): warning CS8602: Possible dereference of a null reference. + // M2(x0).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M2(x0)").WithLocation(10, 9), + // (11,9): warning CS8631: The type 'T' cannot be used as type parameter 'U' in the generic type or method 'Outer.M2(U)'. Nullability of type argument 'T' doesn't match constraint type 'object'. + // M2(x0).ToString(); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "M2").WithArguments("Outer.M2(U)", "object", "U", "T").WithLocation(11, 9), + // (11,9): warning CS8602: Possible dereference of a null reference. + // M2(x0).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M2(x0)").WithLocation(11, 9), + // (19,18): warning CS8601: Possible null reference assignment. // M2(x1) = y1; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "y1").WithLocation(15, 18) + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "y1").WithLocation(19, 18) ); } @@ -54289,6 +55591,10 @@ void M0(T x0, U y0, U z0) where U : T if (y0 == null) return; M2(x0) = y0; M2(x0) = z0; + M2(x0) = y0; + M2(x0) = z0; + M2(x0).ToString(); + M2(x0).ToString(); } ref U M2(U a) => throw null; @@ -54296,9 +55602,12 @@ void M0(T x0, U y0, U z0) where U : T "; CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (9,18): warning CS8601: Possible null reference assignment. - // M2(x0) = z0; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "z0").WithLocation(9, 18) + // (12,9): warning CS8602: Possible dereference of a null reference. + // M2(x0).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M2(x0)").WithLocation(12, 9), + // (13,9): warning CS8602: Possible dereference of a null reference. + // M2(x0).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M2(x0)").WithLocation(13, 9) ); } @@ -55278,6 +56587,9 @@ void M1(T x) where T : object "; CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( + // (7,9): warning CS8631: The type 'T' cannot be used as type parameter 'T' in the generic type or method 'Outer.M1(T)'. Nullability of type argument 'T' doesn't match constraint type 'object'. + // M1(x); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "M1").WithArguments("Outer.M1(T)", "object", "T", "T").WithLocation(7, 9), // (8,9): warning CS8631: The type 'T' cannot be used as type parameter 'T' in the generic type or method 'Outer.M1(T)'. Nullability of type argument 'T' doesn't match constraint type 'object'. // M1(x); Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "M1").WithArguments("Outer.M1(T)", "object", "T", "T").WithLocation(8, 9) @@ -55330,6 +56642,9 @@ void M1(T x) where T : class "; CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( + // (7,9): warning CS8634: The type 'T' cannot be used as type parameter 'T' in the generic type or method 'Outer.M1(T)'. Nullability of type argument 'T' doesn't match 'class' constraint. + // M1(x); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterReferenceTypeConstraint, "M1").WithArguments("Outer.M1(T)", "T", "T").WithLocation(7, 9), // (8,9): warning CS8634: The type 'T' cannot be used as type parameter 'T' in the generic type or method 'Outer.M1(T)'. Nullability of type argument 'T' doesn't match 'class' constraint. // M1(x); Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterReferenceTypeConstraint, "M1").WithArguments("Outer.M1(T)", "T", "T").WithLocation(8, 9) @@ -55511,13 +56826,21 @@ void M0(T x, T y) if (x == null) return; if (y == null) return; M1(x, y).ToString(); + M1(x, y).ToString(); } T M1(T x, T y) => throw null; } "; - CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics(); + CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( + // (8,9): warning CS8602: Possible dereference of a null reference. + // M1(x, y).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M1(x, y)").WithLocation(8, 9), + // (9,9): warning CS8602: Possible dereference of a null reference. + // M1(x, y).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M1(x, y)").WithLocation(9, 9) + ); } [Fact] @@ -55570,13 +56893,18 @@ void M0(T x0, T y0, U z0) where U : T { if (x0 == null) return; M2(x0, y0) = z0; + M2(x0, y0).ToString(); } ref U M2(U a, U b) => throw null; } "; - CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics(); + CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( + // (8,9): warning CS8602: Possible dereference of a null reference. + // M2(x0, y0).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M2(x0, y0)").WithLocation(8, 9) + ); } [Fact] @@ -55609,6 +56937,9 @@ void M0(T x0, T y0, U z0) where U : T if (x0 == null) return; if (y0 == null) return; M2(x0, y0) = z0; + M2(x0, y0) = z0; + + M2(x0, y0).ToString(); } ref U M2(U a, U b) => throw null; @@ -55616,9 +56947,9 @@ void M0(T x0, T y0, U z0) where U : T "; CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (8,22): warning CS8601: Possible null reference assignment. - // M2(x0, y0) = z0; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "z0").WithLocation(8, 22) + // (11,9): warning CS8602: Possible dereference of a null reference. + // M2(x0, y0).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M2(x0, y0)").WithLocation(11, 9) ); } @@ -55713,7 +57044,12 @@ void M0(T x0, T y0, U z0) where U : T { if (x0 == null) return; M2(out M3(x0), out y0) = z0; + M2(out M3(x0), out y0); M2(out M3(x0), out y0); + M2(out M3(x0), out y0); + + M2(out M3(x0), out y0).ToString(); + M2(out M3(x0), out y0).ToString(); } ref U M2(out U a, out U b) => throw null; @@ -55722,15 +57058,12 @@ void M0(T x0, T y0, U z0) where U : T "; CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (7,9): warning CS8638: The nullability of type arguments for method 'Outer.M2(out U, out U)' cannot be inferred from the usage. Try specifying the type arguments explicitly. - // M2(out M3(x0), out y0) = z0; - Diagnostic(ErrorCode.WRN_CantInferNullabilityOfMethodTypeArgs, "M2(out M3(x0), out y0)").WithArguments("Outer.M2(out U, out U)").WithLocation(7, 9), - // (7,16): warning CS8601: Possible null reference assignment. - // M2(out M3(x0), out y0) = z0; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "M3(x0)").WithLocation(7, 16), - // (8,19): warning CS8601: Possible null reference assignment. - // M2(out M3(x0), out y0); - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "M3(x0)").WithLocation(8, 19) + // (12,9): warning CS8602: Possible dereference of a null reference. + // M2(out M3(x0), out y0).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M2(out M3(x0), out y0)").WithLocation(12, 9), + // (13,9): warning CS8602: Possible dereference of a null reference. + // M2(out M3(x0), out y0).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M2(out M3(x0), out y0)").WithLocation(13, 9) ); } @@ -55744,7 +57077,12 @@ void M0(T x0, T y0, U z0) where U : T { if (y0 == null) return; M2(out x0, out M3(y0)) = z0; + M2(out x0, out M3(y0)); M2(out x0, out M3(y0)); + M2(out x0, out M3(y0)); + + M2(out x0, out M3(y0)).ToString(); // warn + M2(out x0, out M3(y0)).ToString(); // warn } ref U M2(out U a, out U b) => throw null; @@ -55753,15 +57091,13 @@ void M0(T x0, T y0, U z0) where U : T "; CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (7,9): warning CS8638: The nullability of type arguments for method 'Outer.M2(out U, out U)' cannot be inferred from the usage. Try specifying the type arguments explicitly. - // M2(out x0, out M3(y0)) = z0; - Diagnostic(ErrorCode.WRN_CantInferNullabilityOfMethodTypeArgs, "M2(out x0, out M3(y0))").WithArguments("Outer.M2(out U, out U)").WithLocation(7, 9), - // (7,24): warning CS8601: Possible null reference assignment. - // M2(out x0, out M3(y0)) = z0; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "M3(y0)").WithLocation(7, 24), - // (8,27): warning CS8601: Possible null reference assignment. - // M2(out x0, out M3(y0)); - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "M3(y0)").WithLocation(8, 27)); + // (12,9): warning CS8602: Possible dereference of a null reference. + // M2(out x0, out M3(y0)).ToString(); // warn + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M2(out x0, out M3(y0))").WithLocation(12, 9), + // (13,9): warning CS8602: Possible dereference of a null reference. + // M2(out x0, out M3(y0)).ToString(); // warn + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M2(out x0, out M3(y0))").WithLocation(13, 9) + ); } [Fact] @@ -55775,6 +57111,10 @@ void M0(T x0, T y0, U z0) where U : T if (x0 == null) return; if (y0 == null) return; M2(out M3(x0), out M3(y0)) = z0; + M2(out M3(x0), out M3(y0)) = z0; + M2(out M3(x0), out M3(y0)) = z0; + M2(out M3(x0), out M3(y0)) = z0; + M2(out M3(x0), out M3(y0)).ToString(); } ref U M2(out U a, out U b) => throw null; @@ -55783,9 +57123,9 @@ void M0(T x0, T y0, U z0) where U : T "; CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (8,38): warning CS8601: Possible null reference assignment. - // M2(out M3(x0), out M3(y0)) = z0; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "z0").WithLocation(8, 38) + // (12,9): warning CS8602: Possible dereference of a null reference. + // M2(out M3(x0), out M3(y0)).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M2(out M3(x0), out M3(y0))").WithLocation(12, 9) ); } @@ -55869,6 +57209,9 @@ void M0(T x0, T y0, U z0) where U : T where T : class? { if (x0 == null) return; M2(M3(x0), M3(y0)) = z0; + M2(M3(x0), M3(y0)) = z0; + + M2(M3(x0), M3(y0)).ToString(); } ref U M2(I1 a, I1 b) => throw null; @@ -55879,9 +57222,9 @@ interface I1 {} "; CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (7,30): warning CS8601: Possible null reference assignment. - // M2(M3(x0), M3(y0)) = z0; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "z0").WithLocation(7, 30) + // (10,9): warning CS8602: Possible dereference of a null reference. + // M2(M3(x0), M3(y0)).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M2(M3(x0), M3(y0))").WithLocation(10, 9) ); } @@ -55895,6 +57238,9 @@ void M0(T x0, T y0, U z0) where U : T where T : class? { if (y0 == null) return; M2(M3(x0), M3(y0)) = z0; + M2(M3(x0), M3(y0)) = z0; + + M2(M3(x0), M3(y0)).ToString(); } ref U M2(I1 a, I1 b) => throw null; @@ -55905,9 +57251,9 @@ interface I1 {} "; CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (7,30): warning CS8601: Possible null reference assignment. - // M2(M3(x0), M3(y0)) = z0; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "z0").WithLocation(7, 30) + // (10,9): warning CS8602: Possible dereference of a null reference. + // M2(M3(x0), M3(y0)).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M2(M3(x0), M3(y0))").WithLocation(10, 9) ); } @@ -55922,6 +57268,10 @@ void M0(T x0, T y0, U z0) where U : T where T : class? if (x0 == null) return; if (y0 == null) return; M2(M3(x0), M3(y0)) = z0; + M2(M3(x0), M3(y0)) = z0; + M2(M3(x0), M3(y0)) = z0; + + M2(M3(x0), M3(y0)).ToString(); } ref U M2(I1 a, I1 b) => throw null; @@ -55932,9 +57282,9 @@ interface I1 {} "; CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (8,30): warning CS8601: Possible null reference assignment. - // M2(M3(x0), M3(y0)) = z0; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "z0").WithLocation(8, 30) + // (12,9): warning CS8602: Possible dereference of a null reference. + // M2(M3(x0), M3(y0)).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M2(M3(x0), M3(y0))").WithLocation(12, 9) ); } @@ -56056,6 +57406,8 @@ void M0(T x0, T y0, U z0) where U : T if (x0 == null) return; M2(ref M3(x0), ref y0) = z0; M2(ref M3(x0), ref y0); + + M2(ref M3(x0), ref y0).ToString(); } ref U M2(ref U a, ref U b) => throw null; @@ -56064,15 +57416,9 @@ void M0(T x0, T y0, U z0) where U : T "; CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (7,9): warning CS8638: The nullability of type arguments for method 'Outer.M2(ref U, ref U)' cannot be inferred from the usage. Try specifying the type arguments explicitly. - // M2(ref M3(x0), ref y0) = z0; - Diagnostic(ErrorCode.WRN_CantInferNullabilityOfMethodTypeArgs, "M2(ref M3(x0), ref y0)").WithArguments("Outer.M2(ref U, ref U)").WithLocation(7, 9), - // (7,16): warning CS8601: Possible null reference assignment. - // M2(ref M3(x0), ref y0) = z0; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "M3(x0)").WithLocation(7, 16), - // (8,19): warning CS8601: Possible null reference assignment. - // M2(ref M3(x0), ref y0); - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "M3(x0)").WithLocation(8, 19) + // (10,9): warning CS8602: Possible dereference of a null reference. + // M2(ref M3(x0), ref y0).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M2(ref M3(x0), ref y0)").WithLocation(10, 9) ); } @@ -56087,6 +57433,8 @@ void M0(T x0, T y0, U z0) where U : T if (y0 == null) return; M2(ref x0, ref M3(y0)) = z0; M2(ref x0, ref M3(y0)); + + M2(ref x0, ref M3(y0)).ToString(); } ref U M2(ref U a, ref U b) => throw null; @@ -56095,15 +57443,9 @@ void M0(T x0, T y0, U z0) where U : T "; CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (7,9): warning CS8638: The nullability of type arguments for method 'Outer.M2(ref U, ref U)' cannot be inferred from the usage. Try specifying the type arguments explicitly. - // M2(ref x0, ref M3(y0)) = z0; - Diagnostic(ErrorCode.WRN_CantInferNullabilityOfMethodTypeArgs, "M2(ref x0, ref M3(y0))").WithArguments("Outer.M2(ref U, ref U)").WithLocation(7, 9), - // (7,24): warning CS8601: Possible null reference assignment. - // M2(ref x0, ref M3(y0)) = z0; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "M3(y0)").WithLocation(7, 24), - // (8,27): warning CS8601: Possible null reference assignment. - // M2(ref x0, ref M3(y0)); - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "M3(y0)").WithLocation(8, 27) + // (10,9): warning CS8602: Possible dereference of a null reference. + // M2(ref x0, ref M3(y0)).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M2(ref x0, ref M3(y0))").WithLocation(10, 9) ); } @@ -56118,6 +57460,7 @@ void M0(T x0, T y0, U z0) where U : T if (x0 == null) return; if (y0 == null) return; M2(ref M3(x0), ref M3(y0)) = z0; + M2(ref M3(x0), ref M3(y0)) = z0; } ref U M2(ref U a, ref U b) where U : object => throw null; @@ -56126,9 +57469,12 @@ void M0(T x0, T y0, U z0) where U : T "; CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (8,38): warning CS8601: Possible null reference assignment. + // (8,9): warning CS8631: The type 'T' cannot be used as type parameter 'U' in the generic type or method 'Outer.M2(ref U, ref U)'. Nullability of type argument 'T' doesn't match constraint type 'object'. // M2(ref M3(x0), ref M3(y0)) = z0; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "z0").WithLocation(8, 38) + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "M2").WithArguments("Outer.M2(ref U, ref U)", "object", "U", "T").WithLocation(8, 9), + // (9,9): warning CS8631: The type 'T' cannot be used as type parameter 'U' in the generic type or method 'Outer.M2(ref U, ref U)'. Nullability of type argument 'T' doesn't match constraint type 'object'. + // M2(ref M3(x0), ref M3(y0)) = z0; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "M2").WithArguments("Outer.M2(ref U, ref U)", "object", "U", "T").WithLocation(9, 9) ); } @@ -56143,6 +57489,7 @@ void M0(T x0, T y0) { if (x0 == null) return; M2(x0).M3(ref y0); + M2(x0).M3(ref y0); } Other M2(U a) where U : object => throw null; @@ -56154,9 +57501,12 @@ class Other where U : object } "; CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (7,23): warning CS8604: Possible null reference argument for parameter 'a' in 'void Other.M3(ref T a)'. + // (7,9): warning CS8631: The type 'T' cannot be used as type parameter 'U' in the generic type or method 'Outer.M2(U)'. Nullability of type argument 'T' doesn't match constraint type 'object'. // M2(x0).M3(ref y0); - Diagnostic(ErrorCode.WRN_NullReferenceArgument, "y0").WithArguments("a", "void Other.M3(ref T a)").WithLocation(7, 23) + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "M2").WithArguments("Outer.M2(U)", "object", "U", "T").WithLocation(7, 9), + // (8,9): warning CS8631: The type 'T' cannot be used as type parameter 'U' in the generic type or method 'Outer.M2(U)'. Nullability of type argument 'T' doesn't match constraint type 'object'. + // M2(x0).M3(ref y0); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "M2").WithArguments("Outer.M2(U)", "object", "U", "T").WithLocation(8, 9) ); } @@ -56299,12 +57649,6 @@ interface I1 {} "; CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (7,9): warning CS8631: The type 'I1' cannot be used as type parameter 'U' in the generic type or method 'Outer.M3(U, W)'. Nullability of type argument 'I1' doesn't match constraint type 'I1'. - // M3(M2(x0), a0); - Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "M3").WithArguments("Outer.M3(U, W)", "I1", "U", "I1").WithLocation(7, 9), - // (8,9): warning CS8631: The type 'I1' cannot be used as type parameter 'U' in the generic type or method 'Outer.M3(U, W)'. Nullability of type argument 'I1' doesn't match constraint type 'I1'. - // M3(M2(a0), x0); - Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "M3").WithArguments("Outer.M3(U, W)", "I1", "U", "I1").WithLocation(8, 9), // (9,9): warning CS8631: The type 'I1' cannot be used as type parameter 'U' in the generic type or method 'Outer.M3(U, W)'. Nullability of type argument 'I1' doesn't match constraint type 'I1'. // M3, object?>(z0, null); Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "M3, object?>").WithArguments("Outer.M3(U, W)", "I1", "U", "I1").WithLocation(9, 9) @@ -56321,6 +57665,8 @@ void M0(T x0, I1 z0) where T : class? { if (x0 == null) return; M3(M2(x0)); + M3(M2(x0)); + M3>(M2(x0)); M3>(z0); } @@ -56331,7 +57677,17 @@ void M0(T x0, I1 z0) where T : class? interface I1 {} "; - CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics(); + CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( + // (7,9): warning CS8631: The type 'I1' cannot be used as type parameter 'U' in the generic type or method 'Outer.M3(U)'. Nullability of type argument 'I1' doesn't match constraint type 'I1'. + // M3(M2(x0)); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "M3").WithArguments("Outer.M3(U)", "I1", "U", "I1").WithLocation(7, 9), + // (8,9): warning CS8631: The type 'I1' cannot be used as type parameter 'U' in the generic type or method 'Outer.M3(U)'. Nullability of type argument 'I1' doesn't match constraint type 'I1'. + // M3(M2(x0)); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "M3").WithArguments("Outer.M3(U)", "I1", "U", "I1").WithLocation(8, 9), + // (9,9): warning CS8631: The type 'I1' cannot be used as type parameter 'U' in the generic type or method 'Outer.M3(U)'. Nullability of type argument 'I1' doesn't match constraint type 'I1'. + // M3>(M2(x0)); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "M3>").WithArguments("Outer.M3(U)", "I1", "U", "I1").WithLocation(9, 9) + ); } [Fact] @@ -56386,9 +57742,6 @@ interface I1 {} "; CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (9,9): warning CS8631: The type 'I1' cannot be used as type parameter 'U' in the generic type or method 'Outer.M3(U, W)'. Nullability of type argument 'I1' doesn't match constraint type 'I1'. - // M3(M2(x0), a0); // 1 - Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "M3").WithArguments("Outer.M3(U, W)", "I1", "U", "I1").WithLocation(9, 9), // (12,9): warning CS8631: The type 'I1' cannot be used as type parameter 'U' in the generic type or method 'Outer.M3(U, W)'. Nullability of type argument 'I1' doesn't match constraint type 'I1'. // M3, string?>(z0, null); Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "M3, string?>").WithArguments("Outer.M3(U, W)", "I1", "U", "I1").WithLocation(12, 9) @@ -56498,6 +57851,8 @@ void M0(T x0, T y0) if (y0 == null) return; T z0 = x0 ?? y0; M1(z0) = x0; + + M1(z0).ToString(); z0.ToString(); } @@ -56506,9 +57861,9 @@ void M0(T x0, T y0) "; CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (8,18): warning CS8601: Possible null reference assignment. - // M1(z0) = x0; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "x0").WithLocation(8, 18) + // (10,9): warning CS8602: Possible dereference of a null reference. + // M1(z0).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M1(z0)").WithLocation(10, 9) ); } @@ -56672,6 +58027,8 @@ void M0(T x0, T y0) where T : class? { if (x0 is null) return; M2(x0) = y0; + + M2(x0).ToString(); x0.ToString(); } @@ -56680,9 +58037,9 @@ void M0(T x0, T y0) where T : class? "; CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (7,18): warning CS8601: Possible null reference assignment. - // M2(x0) = y0; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "y0").WithLocation(7, 18) + // (9,9): warning CS8602: Possible dereference of a null reference. + // M2(x0).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M2(x0)").WithLocation(9, 9) ); } @@ -56717,6 +58074,8 @@ void M0(T x0, T y0) where T : class? { if (x0 == null) return; M2(x0) = y0; + + M2(x0).ToString(); x0.ToString(); } @@ -56725,9 +58084,9 @@ void M0(T x0, T y0) where T : class? "; CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (7,18): warning CS8601: Possible null reference assignment. - // M2(x0) = y0; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "y0").WithLocation(7, 18) + // (9,9): warning CS8602: Possible dereference of a null reference. + // M2(x0).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M2(x0)").WithLocation(9, 9) ); } @@ -56744,6 +58103,8 @@ void M0(T x0, T z0) { M2(x0) = z0; M2(y0) = z0; + M2(x0).ToString(); + M2(y0).ToString(); x0.ToString(); y0.ToString(); } @@ -56752,11 +58113,10 @@ void M0(T x0, T z0) ref T M2(T x) => throw null; } "; - // https://github.com/dotnet/roslyn/issues/30952 - Expect WRN_NullReferenceAssignment for [M2(y0) = z0;] CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (8,22): warning CS8601: Possible null reference assignment. - // M2(x0) = z0; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "z0").WithLocation(8, 22) + // (10,13): warning CS8602: Possible dereference of a null reference. + // M2(x0).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M2(x0)").WithLocation(10, 13) ); } @@ -56953,6 +58313,7 @@ void M0(T x0, T y0) { if (x0 == null) return; T z0 = x0 ?? y0; + M1(z0).ToString(); M1(z0) = y0; z0.ToString(); z0?.ToString(); @@ -56966,12 +58327,12 @@ void M0(T x0, T y0) // (7,16): hidden CS8607: Expression is probably never null. // T z0 = x0 ?? y0; Diagnostic(ErrorCode.HDN_ExpressionIsProbablyNeverNull, "x0").WithLocation(7, 16), - // (8,18): warning CS8601: Possible null reference assignment. - // M1(z0) = y0; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "y0").WithLocation(8, 18), - // (10,9): hidden CS8607: Expression is probably never null. + // (8,9): warning CS8602: Possible dereference of a null reference. + // M1(z0).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M1(z0)").WithLocation(8, 9), + // (11,9): hidden CS8607: Expression is probably never null. // z0?.ToString(); - Diagnostic(ErrorCode.HDN_ExpressionIsProbablyNeverNull, "z0").WithLocation(10, 9) + Diagnostic(ErrorCode.HDN_ExpressionIsProbablyNeverNull, "z0").WithLocation(11, 9) ); } @@ -56988,6 +58349,7 @@ void M0(T x0, T y0, T a0) T z0 = x0 ?? y0; M1(z0) = a0; z0?.ToString(); + M1(z0).ToString(); } ref S M1(S x) => throw null; @@ -56998,12 +58360,12 @@ void M0(T x0, T y0, T a0) // (8,16): hidden CS8607: Expression is probably never null. // T z0 = x0 ?? y0; Diagnostic(ErrorCode.HDN_ExpressionIsProbablyNeverNull, "x0").WithLocation(8, 16), - // (9,18): warning CS8601: Possible null reference assignment. - // M1(z0) = a0; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "a0").WithLocation(9, 18), // (10,9): hidden CS8607: Expression is probably never null. // z0?.ToString(); - Diagnostic(ErrorCode.HDN_ExpressionIsProbablyNeverNull, "z0").WithLocation(10, 9) + Diagnostic(ErrorCode.HDN_ExpressionIsProbablyNeverNull, "z0").WithLocation(10, 9), + // (11,9): warning CS8602: Possible dereference of a null reference. + // M1(z0).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M1(z0)").WithLocation(11, 9) ); } @@ -57042,6 +58404,7 @@ void M0(T x0, T y0, T u0, T v0) if (v0 == null) return; a0[0] = v0; M2(v0) = a0[1]; + M2(v0).ToString(); } ref T M2(T x) => throw null; @@ -57049,9 +58412,9 @@ void M0(T x0, T y0, T u0, T v0) "; CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (10,18): warning CS8601: Possible null reference assignment. - // M2(v0) = a0[1]; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "a0[1]").WithLocation(10, 18) + // (11,9): warning CS8602: Possible dereference of a null reference. + // M2(v0).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M2(v0)").WithLocation(11, 9) ); } @@ -57069,6 +58432,8 @@ void M0(T x0, T y0, T u0, T v0) if (v0 == null) return; a0[0] = v0; M2(v0) = a0[1]; + M2(v0) = a0[1]; + M2(v0).ToString(); } ref T M2(T x) => throw null; @@ -57076,9 +58441,9 @@ void M0(T x0, T y0, T u0, T v0) "; CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (11,18): warning CS8601: Possible null reference assignment. - // M2(v0) = a0[1]; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "a0[1]").WithLocation(11, 18) + // (13,9): warning CS8602: Possible dereference of a null reference. + // M2(v0).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M2(v0)").WithLocation(13, 9) ); } @@ -57096,6 +58461,8 @@ void M0(T x0, T y0, T u0, T v0) if (v0 == null) return; a0[0] = v0; M2(v0) = a0[1]; + M2(v0) = a0[1]; + M2(v0).ToString(); } ref T M2(T x) => throw null; @@ -57103,9 +58470,9 @@ void M0(T x0, T y0, T u0, T v0) "; CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (11,18): warning CS8601: Possible null reference assignment. - // M2(v0) = a0[1]; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "a0[1]").WithLocation(11, 18) + // (13,9): warning CS8602: Possible dereference of a null reference. + // M2(v0).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M2(v0)").WithLocation(13, 9) ); } @@ -57121,6 +58488,7 @@ void M0(T x0, T y0, T u0, T v0) if (y0 == null) return; var a0 = new[] {x0, y0}; a0[0] = u0; + a0[0].ToString(); if (v0 == null) return; a0[0] = v0; M2(v0) = a0[1]; @@ -57131,9 +58499,9 @@ void M0(T x0, T y0, T u0, T v0) "; CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (9,17): warning CS8601: Possible null reference assignment. - // a0[0] = u0; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "u0").WithLocation(9, 17) + // (10,9): warning CS8602: Possible dereference of a null reference. + // a0[0].ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "a0[0]").WithLocation(10, 9) ); } @@ -57552,8 +58920,8 @@ void F(T y0) if (y0 == null) return; T x0; x0 = null; - - M2(M1(x0), M1(y0)) = + + M2(M1(x0), M1(y0)) = (C a, C b) => throw null; } @@ -57565,12 +58933,9 @@ void F(T y0) // (8,14): warning CS8600: Converting null literal or possible null value to non-nullable type. // x0 = null; Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(8, 14), - // (11,13): warning CS8622: Nullability of reference types in type of parameter 'a' of 'lambda expression' doesn't match the target delegate 'Action, C>'. - // (C a, C b) => throw null; - Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOfTargetDelegate, "(C a, C b) => throw null").WithArguments("a", "lambda expression", "System.Action, C>").WithLocation(11, 13), - // (11,13): warning CS8622: Nullability of reference types in type of parameter 'b' of 'lambda expression' doesn't match the target delegate 'Action, C>'. - // (C a, C b) => throw null; - Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOfTargetDelegate, "(C a, C b) => throw null").WithArguments("b", "lambda expression", "System.Action, C>").WithLocation(11, 13) + // (10,15): warning CS8604: Possible null reference argument for parameter 'x' in 'C C.M1(T x)'. + // M2(M1(x0), M1(y0)) = + Diagnostic(ErrorCode.WRN_NullReferenceArgument, "x0").WithArguments("x", "C C.M1(T x)").WithLocation(10, 15) ); } @@ -57605,6 +58970,9 @@ void M0(T x0, T y0) if (y0 == null) return; T z0 = M2() ?? y0; M1(z0) = x0; + M1(z0) = x0; + + M1(z0).ToString(); z0.ToString(); } @@ -57616,9 +58984,9 @@ void M0(T x0, T y0) "; CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (8,18): warning CS8601: Possible null reference assignment. - // M1(z0) = x0; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "x0").WithLocation(8, 18) + // (11,9): warning CS8602: Possible dereference of a null reference. + // M1(z0).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M1(z0)").WithLocation(11, 9) ); } @@ -57633,6 +59001,9 @@ void M0(T x0, T y0) if (y0 == null) return; T z0 = y0 ?? M2(); M1(z0) = x0; + M1(z0) = x0; + + M1(z0).ToString(); z0.ToString(); } @@ -57647,9 +59018,9 @@ void M0(T x0, T y0) // (7,16): hidden CS8607: Expression is probably never null. // T z0 = y0 ?? M2(); Diagnostic(ErrorCode.HDN_ExpressionIsProbablyNeverNull, "y0").WithLocation(7, 16), - // (8,18): warning CS8601: Possible null reference assignment. - // M1(z0) = x0; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "x0").WithLocation(8, 18) + // (11,9): warning CS8602: Possible dereference of a null reference. + // M1(z0).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M1(z0)").WithLocation(11, 9) ); } @@ -57878,6 +59249,7 @@ void M0(bool b, T x0, T y0, T a0) T z0 = b ? x0 : y0; M1(z0) = a0; z0?.ToString(); + M1(z0).ToString(); } ref S M1(S x) => throw null; @@ -57885,12 +59257,12 @@ void M0(bool b, T x0, T y0, T a0) "; CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (9,18): warning CS8601: Possible null reference assignment. - // M1(z0) = a0; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "a0").WithLocation(9, 18), // (10,9): hidden CS8607: Expression is probably never null. // z0?.ToString(); - Diagnostic(ErrorCode.HDN_ExpressionIsProbablyNeverNull, "z0").WithLocation(10, 9) + Diagnostic(ErrorCode.HDN_ExpressionIsProbablyNeverNull, "z0").WithLocation(10, 9), + // (11,9): warning CS8602: Possible dereference of a null reference. + // M1(z0).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M1(z0)").WithLocation(11, 9) ); } @@ -58110,6 +59482,7 @@ void M0(T x0, T y0, T a0) if (y0 == null) return; T z0 = true ? x0 : y0; M1(z0) = a0; + M1(z0).ToString(); z0?.ToString(); z0.ToString(); } @@ -58119,12 +59492,12 @@ void M0(T x0, T y0, T a0) "; CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (9,18): warning CS8601: Possible null reference assignment. - // M1(z0) = a0; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "a0").WithLocation(9, 18), - // (10,9): hidden CS8607: Expression is probably never null. + // (10,9): warning CS8602: Possible dereference of a null reference. + // M1(z0).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M1(z0)").WithLocation(10, 9), + // (11,9): hidden CS8607: Expression is probably never null. // z0?.ToString(); - Diagnostic(ErrorCode.HDN_ExpressionIsProbablyNeverNull, "z0").WithLocation(10, 9) + Diagnostic(ErrorCode.HDN_ExpressionIsProbablyNeverNull, "z0").WithLocation(11, 9) ); } @@ -58164,6 +59537,7 @@ void M0(T x0, T y0) if (y0 == null) return; T z0 = true ? y0 : M2(); M1(z0) = x0; + M1(z0).ToString(); z0?.ToString(); z0.ToString(); } @@ -58176,12 +59550,12 @@ void M0(T x0, T y0) "; CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (8,18): warning CS8601: Possible null reference assignment. - // M1(z0) = x0; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "x0").WithLocation(8, 18), - // (9,9): hidden CS8607: Expression is probably never null. + // (9,9): warning CS8602: Possible dereference of a null reference. + // M1(z0).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M1(z0)").WithLocation(9, 9), + // (10,9): hidden CS8607: Expression is probably never null. // z0?.ToString(); - Diagnostic(ErrorCode.HDN_ExpressionIsProbablyNeverNull, "z0").WithLocation(9, 9) + Diagnostic(ErrorCode.HDN_ExpressionIsProbablyNeverNull, "z0").WithLocation(10, 9) ); } @@ -58331,6 +59705,7 @@ void M0(T x0, T y0, T a0) if (y0 == null) return; T z0 = false ? x0 : y0; M1(z0) = a0; + M1(z0).ToString(); z0?.ToString(); z0.ToString(); } @@ -58340,12 +59715,12 @@ void M0(T x0, T y0, T a0) "; CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (9,18): warning CS8601: Possible null reference assignment. - // M1(z0) = a0; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "a0").WithLocation(9, 18), - // (10,9): hidden CS8607: Expression is probably never null. + // (10,9): warning CS8602: Possible dereference of a null reference. + // M1(z0).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M1(z0)").WithLocation(10, 9), + // (11,9): hidden CS8607: Expression is probably never null. // z0?.ToString(); - Diagnostic(ErrorCode.HDN_ExpressionIsProbablyNeverNull, "z0").WithLocation(10, 9) + Diagnostic(ErrorCode.HDN_ExpressionIsProbablyNeverNull, "z0").WithLocation(11, 9) ); } @@ -58360,6 +59735,7 @@ void M0(T x0, T y0) if (y0 == null) return; T z0 = false ? M2() : y0; M1(z0) = x0; + M1(z0).ToString(); z0?.ToString(); z0.ToString(); } @@ -58372,12 +59748,12 @@ void M0(T x0, T y0) "; CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (8,18): warning CS8601: Possible null reference assignment. - // M1(z0) = x0; - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "x0").WithLocation(8, 18), - // (9,9): hidden CS8607: Expression is probably never null. + // (9,9): warning CS8602: Possible dereference of a null reference. + // M1(z0).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M1(z0)").WithLocation(9, 9), + // (10,9): hidden CS8607: Expression is probably never null. // z0?.ToString(); - Diagnostic(ErrorCode.HDN_ExpressionIsProbablyNeverNull, "z0").WithLocation(9, 9) + Diagnostic(ErrorCode.HDN_ExpressionIsProbablyNeverNull, "z0").WithLocation(10, 9) ); } @@ -62068,6 +63444,8 @@ static void F(object o) assertDiagnosticOptions(NullableContextOptions.Enable); assertDiagnosticOptions(NullableContextOptions.SafeOnly); + assertDiagnosticOptions(NullableContextOptions.Warnings); + assertDiagnosticOptions(NullableContextOptions.SafeOnlyWarnings); void assertDiagnosticOptions(NullableContextOptions nullableContextOptions) { @@ -62251,6 +63629,8 @@ static object M() assertDiagnosticOptions(NullableContextOptions.Enable); assertDiagnosticOptions(NullableContextOptions.SafeOnly); + assertDiagnosticOptions(NullableContextOptions.Warnings); + assertDiagnosticOptions(NullableContextOptions.SafeOnlyWarnings); void assertDiagnosticOptions(NullableContextOptions nullableContextOptions) { @@ -62398,6 +63778,8 @@ static void F(object o) assertDiagnosticOptions(NullableContextOptions.Disable); assertDiagnosticOptions(NullableContextOptions.Enable); assertDiagnosticOptions(NullableContextOptions.SafeOnly); + assertDiagnosticOptions(NullableContextOptions.Warnings); + assertDiagnosticOptions(NullableContextOptions.SafeOnlyWarnings); void assertDiagnosticOptions(NullableContextOptions nullableContextOptions) { @@ -63084,139 +64466,148 @@ static object M() string id = MessageProvider.Instance.GetIdForErrorCode((int)ErrorCode.WRN_ConvertingNullableToNonNullable); - var comp = CreateCompilation(new[] { source, source2 }, options: WithNonNullTypesTrue()); - var diagnostics = comp.GetDiagnostics(); - diagnostics.Verify( - // (8,13): warning CS8600: Converting null literal or possible null value to non-nullable type. - // x = null; - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null") - ); - Assert.Equal(DiagnosticSeverity.Warning, diagnostics.Single().Severity); + assertDiagnosticOptions1(NullableContextOptions.Enable); + assertDiagnosticOptions1(NullableContextOptions.Warnings); - comp = CreateCompilation(new[] { source, source2 }, options: WithNonNullTypesTrue().WithSpecificDiagnosticOptions(id, ReportDiagnostic.Default)); - diagnostics = comp.GetDiagnostics(); - diagnostics.Verify( - // (8,13): warning CS8600: Converting null literal or possible null value to non-nullable type. - // x = null; - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null") - ); - Assert.Equal(DiagnosticSeverity.Warning, diagnostics.Single().Severity); + assertDiagnosticOptions2(NullableContextOptions.Disable); + assertDiagnosticOptions2(NullableContextOptions.SafeOnly); + assertDiagnosticOptions2(NullableContextOptions.SafeOnlyWarnings); - comp = CreateCompilation(new[] { source, source2 }, options: WithNonNullTypesTrue().WithGeneralDiagnosticOption(ReportDiagnostic.Default)); - diagnostics = comp.GetDiagnostics(); - diagnostics.Verify( - // (8,13): warning CS8600: Converting null literal or possible null value to non-nullable type. - // x = null; - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null") - ); - Assert.Equal(DiagnosticSeverity.Warning, diagnostics.Single().Severity); + void assertDiagnosticOptions1(NullableContextOptions nullableContextOptions) + { + CSharpCompilationOptions compilationOptions = WithNonNullTypes(nullableContextOptions); - comp = CreateCompilation(new[] { source, source2 }, options: WithNonNullTypesTrue(). - WithSpecificDiagnosticOptions(id, ReportDiagnostic.Default). - WithGeneralDiagnosticOption(ReportDiagnostic.Error)); - diagnostics = comp.GetDiagnostics(); - diagnostics.Verify( - // (8,13): warning CS8600: Converting null literal or possible null value to non-nullable type. - // x = null; - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null") - ); - Assert.Equal(DiagnosticSeverity.Warning, diagnostics.Single().Severity); + var comp = CreateCompilation(new[] { source, source2 }, options: compilationOptions); + var diagnostics = comp.GetDiagnostics(); + diagnostics.Verify( + // (8,13): warning CS8600: Converting null literal or possible null value to non-nullable type. + // x = null; + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null") + ); + Assert.Equal(DiagnosticSeverity.Warning, diagnostics.Single().Severity); - comp = CreateCompilation(new[] { source, source2 }, options: WithNonNullTypesTrue().WithSpecificDiagnosticOptions(id, ReportDiagnostic.Error)); - diagnostics = comp.GetDiagnostics(); - diagnostics.Verify( - // (8,13): error CS8600: Converting null literal or possible null value to non-nullable type. - // x = null; - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithWarningAsError(true) - ); - Assert.Equal(DiagnosticSeverity.Error, diagnostics.Single().Severity); + comp = CreateCompilation(new[] { source, source2 }, options: compilationOptions.WithSpecificDiagnosticOptions(id, ReportDiagnostic.Default)); + diagnostics = comp.GetDiagnostics(); + diagnostics.Verify( + // (8,13): warning CS8600: Converting null literal or possible null value to non-nullable type. + // x = null; + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null") + ); + Assert.Equal(DiagnosticSeverity.Warning, diagnostics.Single().Severity); - comp = CreateCompilation(new[] { source, source2 }, options: WithNonNullTypesTrue(). - WithSpecificDiagnosticOptions(id, ReportDiagnostic.Error). - WithGeneralDiagnosticOption(ReportDiagnostic.Default)); - diagnostics = comp.GetDiagnostics(); - diagnostics.Verify( - // (8,13): error CS8600: Converting null literal or possible null value to non-nullable type. - // x = null; - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithWarningAsError(true) - ); - Assert.Equal(DiagnosticSeverity.Error, diagnostics.Single().Severity); + comp = CreateCompilation(new[] { source, source2 }, options: compilationOptions.WithGeneralDiagnosticOption(ReportDiagnostic.Default)); + diagnostics = comp.GetDiagnostics(); + diagnostics.Verify( + // (8,13): warning CS8600: Converting null literal or possible null value to non-nullable type. + // x = null; + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null") + ); + Assert.Equal(DiagnosticSeverity.Warning, diagnostics.Single().Severity); - comp = CreateCompilation(new[] { source, source2 }, options: WithNonNullTypesTrue(). - WithGeneralDiagnosticOption(ReportDiagnostic.Error)); - diagnostics = comp.GetDiagnostics(); - diagnostics.Verify( - // (8,13): error CS8600: Converting null literal or possible null value to non-nullable type. - // x = null; - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithWarningAsError(true) - ); - Assert.Equal(DiagnosticSeverity.Error, diagnostics.Single().Severity); + comp = CreateCompilation(new[] { source, source2 }, options: compilationOptions. + WithSpecificDiagnosticOptions(id, ReportDiagnostic.Default). + WithGeneralDiagnosticOption(ReportDiagnostic.Error)); + diagnostics = comp.GetDiagnostics(); + diagnostics.Verify( + // (8,13): warning CS8600: Converting null literal or possible null value to non-nullable type. + // x = null; + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null") + ); + Assert.Equal(DiagnosticSeverity.Warning, diagnostics.Single().Severity); - comp = CreateCompilation(new[] { source, source2 }, options: WithNonNullTypesTrue(). - WithSpecificDiagnosticOptions(id, ReportDiagnostic.Suppress)); - diagnostics = comp.GetDiagnostics(); - diagnostics.Verify(); + comp = CreateCompilation(new[] { source, source2 }, options: compilationOptions.WithSpecificDiagnosticOptions(id, ReportDiagnostic.Error)); + diagnostics = comp.GetDiagnostics(); + diagnostics.Verify( + // (8,13): error CS8600: Converting null literal or possible null value to non-nullable type. + // x = null; + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithWarningAsError(true) + ); + Assert.Equal(DiagnosticSeverity.Error, diagnostics.Single().Severity); - comp = CreateCompilation(new[] { source, source2 }, options: WithNonNullTypesTrue(). - WithSpecificDiagnosticOptions(id, ReportDiagnostic.Suppress). - WithGeneralDiagnosticOption(ReportDiagnostic.Error)); - diagnostics = comp.GetDiagnostics(); - diagnostics.Verify(); + comp = CreateCompilation(new[] { source, source2 }, options: compilationOptions. + WithSpecificDiagnosticOptions(id, ReportDiagnostic.Error). + WithGeneralDiagnosticOption(ReportDiagnostic.Default)); + diagnostics = comp.GetDiagnostics(); + diagnostics.Verify( + // (8,13): error CS8600: Converting null literal or possible null value to non-nullable type. + // x = null; + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithWarningAsError(true) + ); + Assert.Equal(DiagnosticSeverity.Error, diagnostics.Single().Severity); - comp = CreateCompilation(new[] { source, source2 }, options: WithNonNullTypesTrue(). - WithGeneralDiagnosticOption(ReportDiagnostic.Suppress)); - diagnostics = comp.GetDiagnostics(); - diagnostics.Verify(); + comp = CreateCompilation(new[] { source, source2 }, options: compilationOptions. + WithGeneralDiagnosticOption(ReportDiagnostic.Error)); + diagnostics = comp.GetDiagnostics(); + diagnostics.Verify( + // (8,13): error CS8600: Converting null literal or possible null value to non-nullable type. + // x = null; + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithWarningAsError(true) + ); + Assert.Equal(DiagnosticSeverity.Error, diagnostics.Single().Severity); - comp = CreateCompilation(new[] { source, source2 }, options: WithNonNullTypesTrue().WithSpecificDiagnosticOptions(id, ReportDiagnostic.Hidden)); - diagnostics = comp.GetDiagnostics(); - diagnostics.Verify( - // (8,13): hidden CS8600: Converting null literal or possible null value to non-nullable type. - // x = null; - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null") - ); - Assert.Equal(DiagnosticSeverity.Hidden, diagnostics.Single().Severity); + comp = CreateCompilation(new[] { source, source2 }, options: compilationOptions. + WithSpecificDiagnosticOptions(id, ReportDiagnostic.Suppress)); + diagnostics = comp.GetDiagnostics(); + diagnostics.Verify(); - comp = CreateCompilation(new[] { source, source2 }, options: WithNonNullTypesTrue(). - WithSpecificDiagnosticOptions(id, ReportDiagnostic.Hidden). - WithGeneralDiagnosticOption(ReportDiagnostic.Error)); - diagnostics = comp.GetDiagnostics(); - diagnostics.Verify( - // (8,13): hidden CS8600: Converting null literal or possible null value to non-nullable type. - // x = null; - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null") - ); - Assert.Equal(DiagnosticSeverity.Hidden, diagnostics.Single().Severity); + comp = CreateCompilation(new[] { source, source2 }, options: compilationOptions. + WithSpecificDiagnosticOptions(id, ReportDiagnostic.Suppress). + WithGeneralDiagnosticOption(ReportDiagnostic.Error)); + diagnostics = comp.GetDiagnostics(); + diagnostics.Verify(); - comp = CreateCompilation(new[] { source, source2 }, options: WithNonNullTypesTrue(). - WithGeneralDiagnosticOption(ReportDiagnostic.Hidden)); - diagnostics = comp.GetDiagnostics(); - diagnostics.Verify( - // (8,13): warning CS8600: Converting null literal or possible null value to non-nullable type. - // x = null; - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null") - ); - Assert.Equal(DiagnosticSeverity.Warning, diagnostics.Single().Severity); + comp = CreateCompilation(new[] { source, source2 }, options: compilationOptions. + WithGeneralDiagnosticOption(ReportDiagnostic.Suppress)); + diagnostics = comp.GetDiagnostics(); + diagnostics.Verify(); - comp = CreateCompilation(new[] { source, source2 }, options: WithNonNullTypesTrue(). - WithSpecificDiagnosticOptions(id, ReportDiagnostic.Default). - WithGeneralDiagnosticOption(ReportDiagnostic.Hidden)); - diagnostics = comp.GetDiagnostics(); - diagnostics.Verify( - // (8,13): warning CS8600: Converting null literal or possible null value to non-nullable type. - // x = null; - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null") - ); - Assert.Equal(DiagnosticSeverity.Warning, diagnostics.Single().Severity); + comp = CreateCompilation(new[] { source, source2 }, options: compilationOptions.WithSpecificDiagnosticOptions(id, ReportDiagnostic.Hidden)); + diagnostics = comp.GetDiagnostics(); + diagnostics.Verify( + // (8,13): hidden CS8600: Converting null literal or possible null value to non-nullable type. + // x = null; + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null") + ); + Assert.Equal(DiagnosticSeverity.Hidden, diagnostics.Single().Severity); - assertDiagnosticOptions(NullableContextOptions.Disable); - assertDiagnosticOptions(NullableContextOptions.SafeOnly); + comp = CreateCompilation(new[] { source, source2 }, options: compilationOptions. + WithSpecificDiagnosticOptions(id, ReportDiagnostic.Hidden). + WithGeneralDiagnosticOption(ReportDiagnostic.Error)); + diagnostics = comp.GetDiagnostics(); + diagnostics.Verify( + // (8,13): hidden CS8600: Converting null literal or possible null value to non-nullable type. + // x = null; + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null") + ); + Assert.Equal(DiagnosticSeverity.Hidden, diagnostics.Single().Severity); - void assertDiagnosticOptions(NullableContextOptions nullableContextOptions) + comp = CreateCompilation(new[] { source, source2 }, options: compilationOptions. + WithGeneralDiagnosticOption(ReportDiagnostic.Hidden)); + diagnostics = comp.GetDiagnostics(); + diagnostics.Verify( + // (8,13): warning CS8600: Converting null literal or possible null value to non-nullable type. + // x = null; + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null") + ); + Assert.Equal(DiagnosticSeverity.Warning, diagnostics.Single().Severity); + + comp = CreateCompilation(new[] { source, source2 }, options: compilationOptions. + WithSpecificDiagnosticOptions(id, ReportDiagnostic.Default). + WithGeneralDiagnosticOption(ReportDiagnostic.Hidden)); + diagnostics = comp.GetDiagnostics(); + diagnostics.Verify( + // (8,13): warning CS8600: Converting null literal or possible null value to non-nullable type. + // x = null; + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null") + ); + Assert.Equal(DiagnosticSeverity.Warning, diagnostics.Single().Severity); + } + + void assertDiagnosticOptions2(NullableContextOptions nullableContextOptions) { CSharpCompilationOptions compilationOptions = WithNonNullTypes(nullableContextOptions); - comp = CreateCompilation(new[] { source, source2 }, options: compilationOptions); + var comp = CreateCompilation(new[] { source, source2 }, options: compilationOptions); comp.VerifyDiagnostics(); foreach (ReportDiagnostic option in Enum.GetValues(typeof(ReportDiagnostic))) @@ -63280,6 +64671,8 @@ static object M() assertDiagnosticOptions(NullableContextOptions.Disable); assertDiagnosticOptions(NullableContextOptions.Enable); assertDiagnosticOptions(NullableContextOptions.SafeOnly); + assertDiagnosticOptions(NullableContextOptions.Warnings); + assertDiagnosticOptions(NullableContextOptions.SafeOnlyWarnings); void assertDiagnosticOptions(NullableContextOptions nullableContextOptions) { @@ -63822,15 +65215,233 @@ static void Test() } private readonly static NullableAnnotation[] s_AllNullableAnnotations = (NullableAnnotation[])Enum.GetValues(typeof(NullableAnnotation)); + private readonly static NullableAnnotation[] s_AllSpeakableNullableAnnotations = new[] { NullableAnnotation.Unknown, NullableAnnotation.NotAnnotated, NullableAnnotation.Annotated }; + + [Fact] + public void TestJoinForFixingLowerBounds() + { + var inputs = new[] { NullableAnnotation.Annotated, NullableAnnotation.Unknown, NullableAnnotation.NotAnnotated }; + Func getResult = (i, j) => NullableAnnotationExtensions.JoinForFixingLowerBounds(inputs[i], inputs[j]); + + var expected = new NullableAnnotation[3, 3] + { + { NullableAnnotation.Annotated, NullableAnnotation.Annotated, NullableAnnotation.Annotated }, + { NullableAnnotation.Annotated, NullableAnnotation.Unknown, NullableAnnotation.Unknown }, + { NullableAnnotation.Annotated, NullableAnnotation.Unknown, NullableAnnotation.NotAnnotated }, + }; + + AssertEqual(expected, getResult, inputs.Length); + } + + [Fact] + public void TestJoinForFlowAnalysisBranches_IsPossiblyNullableReferenceTypeTypeParameterFalse() + { + var inputs = new[] { NullableAnnotation.Annotated, NullableAnnotation.Nullable, NullableAnnotation.Unknown, NullableAnnotation.NotNullable, NullableAnnotation.NotAnnotated }; + + Func getResult = + (i, j) => NullableAnnotationExtensions.JoinForFlowAnalysisBranches( + inputs[i], inputs[j], type: null, isPossiblyNullableReferenceTypeTypeParameter: _ => false); + + var expected = new NullableAnnotation[5, 5] + { + { NullableAnnotation.Annotated, NullableAnnotation.Annotated, NullableAnnotation.Annotated, NullableAnnotation.Annotated, NullableAnnotation.Annotated }, + { NullableAnnotation.Annotated, NullableAnnotation.Nullable, NullableAnnotation.Nullable, NullableAnnotation.Nullable, NullableAnnotation.Nullable }, + { NullableAnnotation.Annotated, NullableAnnotation.Nullable, NullableAnnotation.Unknown, NullableAnnotation.Unknown, NullableAnnotation.Unknown }, + { NullableAnnotation.Annotated, NullableAnnotation.Nullable, NullableAnnotation.Unknown, NullableAnnotation.NotNullable, NullableAnnotation.NotAnnotated }, + { NullableAnnotation.Annotated, NullableAnnotation.Nullable, NullableAnnotation.Unknown, NullableAnnotation.NotAnnotated, NullableAnnotation.NotAnnotated }, + }; + + AssertEqual(expected, getResult, inputs.Length); + } + + [Fact] + public void TestJoinForFlowAnalysisBranches_IsPossiblyNullableReferenceTypeTypeParameterTrue() + { + var inputs = new[] { NullableAnnotation.Annotated, NullableAnnotation.Nullable, NullableAnnotation.Unknown, NullableAnnotation.NotNullable, NullableAnnotation.NotAnnotated }; + + Func getResult = + (i, j) => NullableAnnotationExtensions.JoinForFlowAnalysisBranches( + inputs[i], inputs[j], type: null, isPossiblyNullableReferenceTypeTypeParameter: _ => true); + + var expected = new NullableAnnotation[5, 5] + { + { NullableAnnotation.Annotated, NullableAnnotation.Annotated, NullableAnnotation.Annotated, NullableAnnotation.Annotated, NullableAnnotation.Annotated }, + { NullableAnnotation.Annotated, NullableAnnotation.Nullable, NullableAnnotation.Nullable, NullableAnnotation.Nullable, NullableAnnotation.Nullable }, + { NullableAnnotation.Annotated, NullableAnnotation.Nullable, NullableAnnotation.Unknown, NullableAnnotation.Unknown, NullableAnnotation.NotAnnotated }, + { NullableAnnotation.Annotated, NullableAnnotation.Nullable, NullableAnnotation.Unknown, NullableAnnotation.NotNullable, NullableAnnotation.NotAnnotated }, + { NullableAnnotation.Annotated, NullableAnnotation.Nullable, NullableAnnotation.NotAnnotated, NullableAnnotation.NotAnnotated, NullableAnnotation.NotAnnotated }, + }; + + AssertEqual(expected, getResult, inputs.Length); + } + + [Fact] + public void TestMeetForFixingUpperBounds() + { + var inputs = new[] { NullableAnnotation.Annotated, NullableAnnotation.Unknown, NullableAnnotation.NotAnnotated }; + Func getResult = (i, j) => NullableAnnotationExtensions.MeetForFixingUpperBounds(inputs[i], inputs[j]); + + var expected = new NullableAnnotation[3, 3] + { + { NullableAnnotation.Annotated, NullableAnnotation.Unknown, NullableAnnotation.NotAnnotated }, + { NullableAnnotation.Unknown, NullableAnnotation.Unknown, NullableAnnotation.NotAnnotated }, + { NullableAnnotation.NotAnnotated, NullableAnnotation.NotAnnotated, NullableAnnotation.NotAnnotated }, + }; + + AssertEqual(expected, getResult, inputs.Length); + } + + [Fact] + public void TestJoinMeetForFlowAnalysisFinally() + { + var inputs = new[] { NullableAnnotation.Annotated, NullableAnnotation.Nullable, NullableAnnotation.Unknown, NullableAnnotation.NotNullable, NullableAnnotation.NotAnnotated }; + Func getResult = (i, j) => NullableAnnotationExtensions.MeetForFlowAnalysisFinally(inputs[i], inputs[j]); + + var expected = new NullableAnnotation[5, 5] + { + { NullableAnnotation.Annotated, NullableAnnotation.Nullable, NullableAnnotation.Unknown, NullableAnnotation.NotNullable, NullableAnnotation.NotAnnotated }, + { NullableAnnotation.Nullable, NullableAnnotation.Nullable, NullableAnnotation.Unknown, NullableAnnotation.NotNullable, NullableAnnotation.NotAnnotated }, + { NullableAnnotation.Unknown, NullableAnnotation.Unknown, NullableAnnotation.Unknown, NullableAnnotation.NotNullable, NullableAnnotation.NotAnnotated }, + { NullableAnnotation.NotNullable, NullableAnnotation.NotNullable, NullableAnnotation.NotNullable, NullableAnnotation.NotNullable, NullableAnnotation.NotNullable }, + { NullableAnnotation.NotAnnotated, NullableAnnotation.NotAnnotated, NullableAnnotation.NotAnnotated, NullableAnnotation.NotNullable, NullableAnnotation.NotAnnotated }, + }; + + AssertEqual(expected, getResult, inputs.Length); + } + + [Fact] + public void TestEnsureCompatible() + { + var inputs = new[] { NullableAnnotation.Annotated, NullableAnnotation.Unknown, NullableAnnotation.NotAnnotated }; + Func getResult = (i, j) => NullableAnnotationExtensions.EnsureCompatible(inputs[i], inputs[j], out _); + + var expected = new NullableAnnotation[3, 3] + { + { NullableAnnotation.Annotated, NullableAnnotation.Annotated, NullableAnnotation.NotAnnotated }, + { NullableAnnotation.Annotated, NullableAnnotation.Unknown, NullableAnnotation.NotAnnotated }, + { NullableAnnotation.NotAnnotated, NullableAnnotation.NotAnnotated, NullableAnnotation.NotAnnotated }, + }; + + AssertEqual(expected, getResult, inputs.Length); + } + + [Fact] + public void TestEnsureCompatible_IsPossiblyNullableReferenceTypeTypeParameterTrue_HadNullabilityMismatch() + { + var inputs = new[] { NullableAnnotation.Annotated, NullableAnnotation.Unknown, NullableAnnotation.NotAnnotated }; + Func getResult = (i, j) => { NullableAnnotationExtensions.EnsureCompatible(inputs[i], inputs[j], out var hadNullabilityMismatch); return hadNullabilityMismatch; }; + + var expected = new bool[3, 3] + { + { false, false, true }, + { false, false, false }, + { true, false, false }, + }; + + AssertEx.Equal(expected, getResult, inputs.Length); + } + + [Fact] + public void TestEnsureCompatibleForTuples_IsPossiblyNullableReferenceTypeTypeParameterFalse() + { + var inputs = new[] { NullableAnnotation.Annotated, NullableAnnotation.Nullable, NullableAnnotation.Unknown, NullableAnnotation.NotNullable, NullableAnnotation.NotAnnotated }; + + Func getResult = + (i, j) => NullableAnnotationExtensions.EnsureCompatibleForTuples( + inputs[i], inputs[j], type: null, isPossiblyNullableReferenceTypeTypeParameter: _ => false, hadNullabilityMismatch: out _); + + var expected = new NullableAnnotation[5, 5] + { + { NullableAnnotation.Annotated, NullableAnnotation.Annotated, NullableAnnotation.Annotated, NullableAnnotation.NotAnnotated, NullableAnnotation.NotAnnotated }, + { NullableAnnotation.Annotated, NullableAnnotation.Nullable, NullableAnnotation.Nullable, NullableAnnotation.NotAnnotated, NullableAnnotation.NotAnnotated }, + { NullableAnnotation.Annotated, NullableAnnotation.Nullable, NullableAnnotation.Unknown, NullableAnnotation.NotNullable, NullableAnnotation.NotAnnotated }, + { NullableAnnotation.NotAnnotated, NullableAnnotation.NotAnnotated, NullableAnnotation.NotNullable, NullableAnnotation.NotNullable, NullableAnnotation.NotAnnotated }, + { NullableAnnotation.NotAnnotated, NullableAnnotation.NotAnnotated, NullableAnnotation.NotAnnotated, NullableAnnotation.NotAnnotated, NullableAnnotation.NotAnnotated }, + }; + + AssertEqual(expected, getResult, inputs.Length); + } + + [Fact] + public void TestEnsureCompatibleForTuples_IsPossiblyNullableReferenceTypeTypeParameterFalse_HadNullabilityMismatch() + { + var inputs = new[] { NullableAnnotation.Annotated, NullableAnnotation.Nullable, NullableAnnotation.Unknown, NullableAnnotation.NotNullable, NullableAnnotation.NotAnnotated }; + Func getResult = (i, j) => + { + NullableAnnotationExtensions.EnsureCompatibleForTuples( + inputs[i], inputs[j], type: null, isPossiblyNullableReferenceTypeTypeParameter: _ => false, hadNullabilityMismatch: out var hadNullabilityMismatch); + return hadNullabilityMismatch; + }; + + var expected = new bool[5, 5] + { + { false, false, false, true, true }, + { false, false, false, true, true }, + { false, false, false, false, false }, + { true, true, false, false, false }, + { true, true, false, false, false }, + }; + + AssertEx.Equal(expected, getResult, inputs.Length); + } + + [Fact] + public void TestEnsureCompatibleForTuples_IsPossiblyNullableReferenceTypeTypeParameterTrue() + { + var inputs = new[] { NullableAnnotation.Annotated, NullableAnnotation.Nullable, NullableAnnotation.Unknown, NullableAnnotation.NotNullable, NullableAnnotation.NotAnnotated }; + + Func getResult = + (i, j) => NullableAnnotationExtensions.EnsureCompatibleForTuples( + inputs[i], inputs[j], type: null, isPossiblyNullableReferenceTypeTypeParameter: _ => true, hadNullabilityMismatch: out _); + + var expected = new NullableAnnotation[5, 5] + { + { NullableAnnotation.Annotated, NullableAnnotation.Annotated, NullableAnnotation.Annotated, NullableAnnotation.NotAnnotated, NullableAnnotation.NotAnnotated }, + { NullableAnnotation.Annotated, NullableAnnotation.Nullable, NullableAnnotation.Nullable, NullableAnnotation.NotAnnotated, NullableAnnotation.NotAnnotated }, + { NullableAnnotation.Annotated, NullableAnnotation.Nullable, NullableAnnotation.Unknown, NullableAnnotation.NotNullable, NullableAnnotation.NotAnnotated }, + { NullableAnnotation.NotAnnotated, NullableAnnotation.NotAnnotated, NullableAnnotation.NotNullable, NullableAnnotation.NotNullable, NullableAnnotation.NotAnnotated }, + { NullableAnnotation.NotAnnotated, NullableAnnotation.NotAnnotated, NullableAnnotation.NotAnnotated, NullableAnnotation.NotAnnotated, NullableAnnotation.NotAnnotated }, + }; + + AssertEqual(expected, getResult, inputs.Length); + } + + [Fact] + public void TestEnsureCompatibleForTuples_IsPossiblyNullableReferenceTypeTypeParameterTrue_HadNullabilityMismatch() + { + var inputs = new[] { NullableAnnotation.Annotated, NullableAnnotation.Nullable, NullableAnnotation.Unknown, NullableAnnotation.NotNullable, NullableAnnotation.NotAnnotated }; + Func getResult = (i, j) => + { + NullableAnnotationExtensions.EnsureCompatibleForTuples( + inputs[i], inputs[j], type: null, isPossiblyNullableReferenceTypeTypeParameter: _ => true, hadNullabilityMismatch: out var hadNullabilityMismatch); + return hadNullabilityMismatch; + }; + + var expected = new bool[5, 5] + { + { false, false, false, true, true }, + { false, false, false, true, true }, + { false, false, false, false, false }, + { true, true, false, false, true }, + { true, true, false, true, false }, + }; + + AssertEx.Equal(expected, getResult, inputs.Length); + } + + private static void AssertEqual(NullableAnnotation[,] expected, Func getResult, int size) + { + AssertEx.Equal(expected, getResult, (na1, na2) => na1 == na2, na => $"NullableAnnotation.{na}", "{0,-32:G}", size); + } [Fact] public void TestJoinForFixingLowerBoundsIsAssociative() { - foreach (var a in s_AllNullableAnnotations) + foreach (var a in s_AllSpeakableNullableAnnotations) { - foreach (var b in s_AllNullableAnnotations) + foreach (var b in s_AllSpeakableNullableAnnotations) { - foreach (var c in s_AllNullableAnnotations) + foreach (var c in s_AllSpeakableNullableAnnotations) { var leftFirst = a.JoinForFixingLowerBounds(b).JoinForFixingLowerBounds(c); var rightFirst = a.JoinForFixingLowerBounds(b.JoinForFixingLowerBounds(c)); @@ -63864,11 +65475,11 @@ public void TestJoinForFlowAnalysisBranchesIsAssociative() [Fact] public void TestMeetForFixingUpperBoundsIsAssociative() { - foreach (var a in s_AllNullableAnnotations) + foreach (var a in s_AllSpeakableNullableAnnotations) { - foreach (var b in s_AllNullableAnnotations) + foreach (var b in s_AllSpeakableNullableAnnotations) { - foreach (var c in s_AllNullableAnnotations) + foreach (var c in s_AllSpeakableNullableAnnotations) { var leftFirst = a.MeetForFixingUpperBounds(b).MeetForFixingUpperBounds(c); var rightFirst = a.MeetForFixingUpperBounds(b.MeetForFixingUpperBounds(c)); @@ -63899,16 +65510,16 @@ public void TestMeetForFlowAnalysisFinallyIsAssociative() public void TestEnsureCompatibleIsAssociative() { Func identity = x => x; - foreach (var a in s_AllNullableAnnotations) + foreach (var a in s_AllSpeakableNullableAnnotations) { - foreach (var b in s_AllNullableAnnotations) + foreach (var b in s_AllSpeakableNullableAnnotations) { - foreach (var c in s_AllNullableAnnotations) + foreach (var c in s_AllSpeakableNullableAnnotations) { foreach (bool isPossiblyNullableReferenceTypeTypeParameter in new[] { true, false }) { - var leftFirst = a.EnsureCompatible(b, isPossiblyNullableReferenceTypeTypeParameter, identity, out var w1a).EnsureCompatible(c, isPossiblyNullableReferenceTypeTypeParameter, identity, out var w1b); - var rightFirst = a.EnsureCompatible(b.EnsureCompatible(c, isPossiblyNullableReferenceTypeTypeParameter, identity, out var w2a), isPossiblyNullableReferenceTypeTypeParameter, identity, out var w2b); + var leftFirst = a.EnsureCompatible(b, out var w1a).EnsureCompatible(c, out var w1b); + var rightFirst = a.EnsureCompatible(b.EnsureCompatible(c, out var w2a), out var w2b); Assert.Equal(leftFirst, rightFirst); Assert.Equal(w1a | w1b, w2a | w2b); } @@ -63920,9 +65531,9 @@ public void TestEnsureCompatibleIsAssociative() [Fact] public void TestJoinForFixingLowerBoundsIsCommutative() { - foreach (var a in s_AllNullableAnnotations) + foreach (var a in s_AllSpeakableNullableAnnotations) { - foreach (var b in s_AllNullableAnnotations) + foreach (var b in s_AllSpeakableNullableAnnotations) { var leftFirst = a.JoinForFixingLowerBounds(b); var rightFirst = b.JoinForFixingLowerBounds(a); @@ -63952,9 +65563,9 @@ public void TestJoinForFlowAnalysisBranchesIsCommutative() [Fact] public void TestMeetForFixingUpperBoundsIsCommutative() { - foreach (var a in s_AllNullableAnnotations) + foreach (var a in s_AllSpeakableNullableAnnotations) { - foreach (var b in s_AllNullableAnnotations) + foreach (var b in s_AllSpeakableNullableAnnotations) { var leftFirst = a.MeetForFixingUpperBounds(b); var rightFirst = b.MeetForFixingUpperBounds(a); @@ -63984,14 +65595,14 @@ public void TestMeetForFlowAnalysisFinallyIsCommutative() public void TestEnsureCompatibleIsCommutative() { Func identity = x => x; - foreach (var a in s_AllNullableAnnotations) + foreach (var a in s_AllSpeakableNullableAnnotations) { - foreach (var b in s_AllNullableAnnotations) + foreach (var b in s_AllSpeakableNullableAnnotations) { foreach (bool isPossiblyNullableReferenceTypeTypeParameter in new[] { true, false }) { - var leftFirst = a.EnsureCompatible(b, isPossiblyNullableReferenceTypeTypeParameter, identity, out var w1); - var rightFirst = b.EnsureCompatible(a, isPossiblyNullableReferenceTypeTypeParameter, identity, out var w2); + var leftFirst = a.EnsureCompatible(b, out var w1); + var rightFirst = b.EnsureCompatible(a, out var w2); Assert.Equal(leftFirst, rightFirst); Assert.Equal(w1, w2); } @@ -67917,9 +69528,12 @@ static void F(bool b, T? x, object y) } [WorkItem(32006, "https://github.com/dotnet/roslyn/issues/32006")] - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/32006")] public void LambaReturnType_DifferentTupleNullability_01() { + // See https://github.com/dotnet/roslyn/issues/32006 + // need to relax assertion in GetImplicitTupleLiteralConversion + var source = @"using System; class Program @@ -67964,9 +69578,11 @@ static void G() } [WorkItem(32006, "https://github.com/dotnet/roslyn/issues/32006")] - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/32006")] public void LambaReturnType_DifferentTupleNullability_02() { + // See https://github.com/dotnet/roslyn/issues/32006 + // need to relax assertion in GetImplicitTupleLiteralConversion var source = @"using System; class Program @@ -67995,9 +69611,11 @@ static void G() } [WorkItem(32006, "https://github.com/dotnet/roslyn/issues/32006")] - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/32006")] public void LambaReturnType_DifferentTupleNullability_03() { + // See https://github.com/dotnet/roslyn/issues/32006 + // need to relax assertion in GetImplicitTupleLiteralConversion var source = @"using System; class Program @@ -68027,9 +69645,11 @@ static void G(T x, T? y) } [WorkItem(32006, "https://github.com/dotnet/roslyn/issues/32006")] - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/32006")] public void LambaReturnType_DifferentTupleNullability_04() { + // See https://github.com/dotnet/roslyn/issues/32006 + // need to relax assertion in GetImplicitTupleLiteralConversion var source = @"using System; class Program diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/CSharpCompilationOptionsTests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/CSharpCompilationOptionsTests.cs index e3b24662069d5..b86873c87ddee 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/CSharpCompilationOptionsTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/CSharpCompilationOptionsTests.cs @@ -443,80 +443,39 @@ public void WithCryptoPublicKey() [Fact] public void WithNullable() { - CSharpCompilationOptions a = CreateCSharpCompilationOptions(); - - Assert.Equal(NullableContextOptions.Disable, a.NullableContextOptions); - Assert.Equal(a, a); - Assert.Equal(a.GetHashCode(), a.GetHashCode()); - Assert.Same(a, a.WithNullableContextOptions(NullableContextOptions.Disable)); - - CSharpCompilationOptions b = a.WithNullableContextOptions(NullableContextOptions.Enable); - Assert.Equal(NullableContextOptions.Enable, b.NullableContextOptions); - Assert.NotEqual(a, b); - Assert.Equal(b, b); - Assert.Equal(b.GetHashCode(), b.GetHashCode()); - Assert.Same(b, b.WithNullableContextOptions(NullableContextOptions.Enable)); - - CSharpCompilationOptions c = a.WithNullableContextOptions(NullableContextOptions.Enable); - Assert.Equal(NullableContextOptions.Enable, c.NullableContextOptions); - Assert.NotEqual(a, c); - Assert.NotSame(b, c); - Assert.Equal(b, c); - Assert.Equal(b.GetHashCode(), c.GetHashCode()); - - CSharpCompilationOptions e = b.WithNullableContextOptions(NullableContextOptions.Disable); - Assert.Equal(NullableContextOptions.Disable, e.NullableContextOptions); - Assert.NotSame(a, e); - Assert.Equal(a, e); - Assert.Equal(a.GetHashCode(), e.GetHashCode()); - Assert.NotEqual(b, e); - Assert.NotEqual(c, e); - - CSharpCompilationOptions d = a.WithNullableContextOptions(NullableContextOptions.SafeOnly); - Assert.Equal(NullableContextOptions.SafeOnly, d.NullableContextOptions); - Assert.NotEqual(a, d); - Assert.NotEqual(b, d); - Assert.NotEqual(c, d); - Assert.Equal(d, d); - Assert.Equal(d.GetHashCode(), d.GetHashCode()); - Assert.Same(d, d.WithNullableContextOptions(NullableContextOptions.SafeOnly)); - - CSharpCompilationOptions f = b.WithNullableContextOptions(NullableContextOptions.SafeOnly); - Assert.Equal(NullableContextOptions.SafeOnly, f.NullableContextOptions); - Assert.NotEqual(a, f); - Assert.NotEqual(b, f); - Assert.NotEqual(c, f); - Assert.NotSame(d, f); - Assert.Equal(d, f); - Assert.Equal(d.GetHashCode(), f.GetHashCode()); - - CSharpCompilationOptions g = d.WithNullableContextOptions(NullableContextOptions.Enable); - Assert.Equal(NullableContextOptions.Enable, g.NullableContextOptions); - Assert.NotEqual(a, g); - Assert.NotSame(b, g); - Assert.Equal(b, g); - Assert.Equal(b.GetHashCode(), g.GetHashCode()); - Assert.NotEqual(d, g); - - CSharpCompilationOptions h = d.WithNullableContextOptions(NullableContextOptions.Disable); - Assert.Equal(NullableContextOptions.Disable, h.NullableContextOptions); - Assert.Equal(a, h); - Assert.Equal(a.GetHashCode(), h.GetHashCode()); - Assert.NotEqual(b, h); - Assert.NotEqual(c, h); - Assert.NotEqual(d, h); - - var i = new CSharpCompilationOptions(OutputKind.ConsoleApplication); - Assert.Equal(NullableContextOptions.Disable, i.NullableContextOptions); - - var j = new CSharpCompilationOptions(OutputKind.ConsoleApplication, nullableContextOptions: NullableContextOptions.Enable); - Assert.Equal(NullableContextOptions.Enable, j.NullableContextOptions); - - var k = new CSharpCompilationOptions(OutputKind.ConsoleApplication, nullableContextOptions: NullableContextOptions.Disable); - Assert.Equal(NullableContextOptions.Disable, k.NullableContextOptions); - - var l = new CSharpCompilationOptions(OutputKind.ConsoleApplication, nullableContextOptions: NullableContextOptions.SafeOnly); - Assert.Equal(NullableContextOptions.SafeOnly, l.NullableContextOptions); + Assert.Equal(NullableContextOptions.Disable, new CSharpCompilationOptions(OutputKind.ConsoleApplication).NullableContextOptions); + + var values = (NullableContextOptions[])System.Enum.GetValues(typeof(NullableContextOptions)); + var options = new CSharpCompilationOptions[values.Length]; + + for (int i = 0; i < values.Length; i++) + { + options[i] = new CSharpCompilationOptions(OutputKind.ConsoleApplication, nullableContextOptions: values[i]); + Assert.Equal(values[i], options[i].NullableContextOptions); + } + + for (int i = 0; i < values.Length; i++) + { + var oldOptions = options[i]; + + for (int j = 0; j < values.Length; j++) + { + var newOptions = oldOptions.WithNullableContextOptions(values[j]); + Assert.Equal(values[j], newOptions.NullableContextOptions); + Assert.Equal(options[j], newOptions); + Assert.Equal(options[j].GetHashCode(), newOptions.GetHashCode()); + + if (i == j) + { + Assert.Same(oldOptions, newOptions); + } + else + { + Assert.NotSame(oldOptions, newOptions); + Assert.NotEqual(oldOptions, newOptions); + } + } + } } } } diff --git a/src/Compilers/Core/MSBuildTask/CommandLineBuilderExtension.cs b/src/Compilers/Core/MSBuildTask/CommandLineBuilderExtension.cs index 5023505ef26a3..e7a9e6eb26849 100644 --- a/src/Compilers/Core/MSBuildTask/CommandLineBuilderExtension.cs +++ b/src/Compilers/Core/MSBuildTask/CommandLineBuilderExtension.cs @@ -188,8 +188,8 @@ internal static bool IsParameterEmpty(string parameter, params char[] splitOn) /// /// Designed to handle the /link and /embed switches: /// - /// /embed[resource]:<filename>[,<name>[,Private]] - /// /link[resource]:<filename>[,<name>[,Private]] + /// /resource:<filename>[,<name>[,Private]] + /// /linkresource:<filename>[,<name>[,Private]] /// /// Where the last flag--Private--is either present or not present /// depending on whether the ITaskItem has a Private="True" attribute. diff --git a/src/Compilers/Core/MSBuildTaskTests/CscTests.cs b/src/Compilers/Core/MSBuildTaskTests/CscTests.cs index 6c1e9a7836036..2430480c1c86a 100644 --- a/src/Compilers/Core/MSBuildTaskTests/CscTests.cs +++ b/src/Compilers/Core/MSBuildTaskTests/CscTests.cs @@ -360,6 +360,24 @@ public void NullableReferenceTypes_Safeonly() Assert.Equal("/nullable:safeonly /out:test.exe test.cs", csc.GenerateResponseFileContents()); } + [Fact] + public void NullableReferenceTypes_Warnings() + { + var csc = new Csc(); + csc.Sources = MSBuildUtil.CreateTaskItems("test.cs"); + csc.NullableContextOptions = "warnings"; + Assert.Equal("/nullable:warnings /out:test.exe test.cs", csc.GenerateResponseFileContents()); + } + + [Fact] + public void NullableReferenceTypes_Safeonlywarnings() + { + var csc = new Csc(); + csc.Sources = MSBuildUtil.CreateTaskItems("test.cs"); + csc.NullableContextOptions = "safeonlywarnings"; + Assert.Equal("/nullable:safeonlywarnings /out:test.exe test.cs", csc.GenerateResponseFileContents()); + } + [Fact] public void NullableReferenceTypes_Default_01() { diff --git a/src/Compilers/Core/MSBuildTaskTests/VbcTests.cs b/src/Compilers/Core/MSBuildTaskTests/VbcTests.cs index 74b535c7b129f..a99f2ba24fe35 100644 --- a/src/Compilers/Core/MSBuildTaskTests/VbcTests.cs +++ b/src/Compilers/Core/MSBuildTaskTests/VbcTests.cs @@ -292,6 +292,18 @@ public void Embed() vbc.DebugType = "full"; vbc.EmbeddedFiles = MSBuildUtil.CreateTaskItems(); Assert.Equal(@"/optionstrict:custom /debug:full /out:test.exe test.vb", vbc.GenerateResponseFileContents()); + + vbc = new Vbc(); + vbc.Sources = MSBuildUtil.CreateTaskItems("a;b.vb"); + vbc.DebugType = "full"; + vbc.EmbeddedFiles = MSBuildUtil.CreateTaskItems("a;b.vb"); + Assert.Equal(@"/optionstrict:custom /debug:full /out:""a;b.exe"" /embed:""a;b.vb"" ""a;b.vb""", vbc.GenerateResponseFileContents()); + + vbc = new Vbc(); + vbc.Sources = MSBuildUtil.CreateTaskItems("a, b.vb"); + vbc.DebugType = "full"; + vbc.EmbeddedFiles = MSBuildUtil.CreateTaskItems("a, b.vb"); + Assert.Equal(@"/optionstrict:custom /debug:full /out:""a, b.exe"" /embed:""a, b.vb"" ""a, b.vb""", vbc.GenerateResponseFileContents()); } [Fact] diff --git a/src/Compilers/Core/Portable/Diagnostic/CommonMessageProvider.cs b/src/Compilers/Core/Portable/Diagnostic/CommonMessageProvider.cs index 03795613214f1..cb0b393a12cf6 100644 --- a/src/Compilers/Core/Portable/Diagnostic/CommonMessageProvider.cs +++ b/src/Compilers/Core/Portable/Diagnostic/CommonMessageProvider.cs @@ -1,8 +1,8 @@ // 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.Concurrent; using System.Globalization; -using System.IO; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis @@ -13,6 +13,11 @@ namespace Microsoft.CodeAnalysis /// internal abstract class CommonMessageProvider { + /// + /// Caches the return values for . + /// + private static readonly ConcurrentDictionary<(string prefix, int code), string> s_errorIdCache = new ConcurrentDictionary<(string prefix, int code), string>(); + /// /// Given an error code, get the severity (warning or error) of the code. /// @@ -99,9 +104,13 @@ public Diagnostic CreateDiagnostic(int code, Location location) /// /// Given an error code (like 1234) return the identifier (CS1234 or BC1234). /// + [PerformanceSensitive( + "https://github.com/dotnet/roslyn/issues/31964", + AllowCaptures = false, + Constraint = "Frequently called by error list filtering; avoid allocations")] public string GetIdForErrorCode(int errorCode) { - return CodePrefix + errorCode.ToString("0000"); + return s_errorIdCache.GetOrAdd((CodePrefix, errorCode), key => key.prefix + key.code.ToString("0000")); } /// diff --git a/src/Compilers/Test/Utilities/CSharp/CompilationTestUtils.cs b/src/Compilers/Test/Utilities/CSharp/CompilationTestUtils.cs index c9d1cbfe77bc1..4f88d935201c8 100644 --- a/src/Compilers/Test/Utilities/CSharp/CompilationTestUtils.cs +++ b/src/Compilers/Test/Utilities/CSharp/CompilationTestUtils.cs @@ -321,6 +321,14 @@ internal static void VerifyTypes(this CSharpCompilation compilation, SyntaxTree // Consider reporting the correct source with annotations on mismatch. AssertEx.Equal(expectedTypes, actualTypes, message: method.ToTestDisplayString()); + foreach (var entry in dictionary.Values.Where(v => !v.IsNull)) + { + // Result types cannot have nested types that are unspeakables + Assert.Null(entry.VisitType(typeOpt: null, + typeWithAnnotationsPredicateOpt: (tswa, a, b) => !tswa.Equals(entry, TypeCompareKind.ConsiderEverything) && !tswa.NullableAnnotation.IsSpeakable(), + typePredicateOpt: (ts, _, b) => false, arg: (object)null, canDigThroughNullable: true)); + } + string toDisplayString(SyntaxNode syntaxOpt) { return (syntaxOpt != null) && dictionary.TryGetValue(syntaxOpt, out var type) ? diff --git a/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb b/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb index 69f87ed47aa0c..532205666ab03 100644 --- a/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb +++ b/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb @@ -1167,7 +1167,6 @@ lVbRuntimePlus: Continue For Case "additionalfile" - value = RemoveQuotesAndSlashes(value) If String.IsNullOrEmpty(value) Then AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, ":") Continue For @@ -1177,7 +1176,6 @@ lVbRuntimePlus: Continue For Case "embed" - value = RemoveQuotesAndSlashes(value) If String.IsNullOrEmpty(value) Then embedAllSourceFiles = True Continue For diff --git a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb index 7482318c45f21..a2bcb31f79e00 100644 --- a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb +++ b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb @@ -2939,6 +2939,30 @@ End Class") {"a.vb", "b.vb"}.Select(Function(f) Path.Combine(_baseDirectory, f)), parsedArgs.EmbeddedFiles.Select(Function(f) f.Path)) + parsedArgs = DefaultParse({"/embed:a.vb,b.vb", "/debug:portable", "a.vb", "b.vb", "c.vb"}, _baseDirectory) + parsedArgs.Errors.Verify() + AssertEx.Equal( + {"a.vb", "b.vb"}.Select(Function(f) Path.Combine(_baseDirectory, f)), + parsedArgs.EmbeddedFiles.Select(Function(f) f.Path)) + + parsedArgs = DefaultParse({"/embed:""a,b.vb""", "/debug:portable", "a,b.vb", "c.vb"}, _baseDirectory) + parsedArgs.Errors.Verify() + AssertEx.Equal( + {"a,b.vb"}.Select(Function(f) Path.Combine(_baseDirectory, f)), + parsedArgs.EmbeddedFiles.Select(Function(f) f.Path)) + + parsedArgs = DefaultParse({"/embed:\""a,b.vb\""", "/debug:portable", "a,b.vb", "c.vb"}, _baseDirectory) + parsedArgs.Errors.Verify() + AssertEx.Equal( + {"a,b.vb"}.Select(Function(f) Path.Combine(_baseDirectory, f)), + parsedArgs.EmbeddedFiles.Select(Function(f) f.Path)) + + parsedArgs = DefaultParse({"/embed:\""""a.vb,b.vb""\""", "/debug:portable", "a.vb", "b.vb", "c.vb"}, _baseDirectory) + parsedArgs.Errors.Verify() + AssertEx.Equal( + {"a.vb", "b.vb"}.Select(Function(f) Path.Combine(_baseDirectory, f)), + parsedArgs.EmbeddedFiles.Select(Function(f) f.Path)) + parsedArgs = DefaultParse({"/embed:a.txt", "/embed", "/debug:portable", "a.vb", "b.vb", "c.vb"}, _baseDirectory) parsedArgs.Errors.Verify() AssertEx.Equal( @@ -7294,6 +7318,22 @@ C:\*.vb(100) : error BC30451: 'Goo' is not declared. It may be inaccessible due Assert.Equal(Path.Combine(_baseDirectory, "web.config"), args.AdditionalFiles(0).Path) Assert.Equal(Path.Combine(_baseDirectory, "app.manifest"), args.AdditionalFiles(1).Path) + args = DefaultParse({"/additionalfile:""web.config,app.manifest""", "a.vb"}, _baseDirectory) + args.Errors.Verify() + Assert.Equal(1, args.AdditionalFiles.Length) + Assert.Equal(Path.Combine(_baseDirectory, "web.config,app.manifest"), args.AdditionalFiles(0).Path) + + args = DefaultParse({"/additionalfile:\""web.config,app.manifest\""", "a.vb"}, _baseDirectory) + args.Errors.Verify() + Assert.Equal(1, args.AdditionalFiles.Length) + Assert.Equal(Path.Combine(_baseDirectory, "web.config,app.manifest"), args.AdditionalFiles(0).Path) + + args = DefaultParse({"/additionalfile:\""""web.config,app.manifest""\""", "a.vb"}, _baseDirectory) + args.Errors.Verify() + Assert.Equal(2, args.AdditionalFiles.Length) + Assert.Equal(Path.Combine(_baseDirectory, "web.config"), args.AdditionalFiles(0).Path) + Assert.Equal(Path.Combine(_baseDirectory, "app.manifest"), args.AdditionalFiles(1).Path) + args = DefaultParse({"/additionalfile:web.config:app.manifest", "a.vb"}, _baseDirectory) args.Errors.Verify() Assert.Equal(1, args.AdditionalFiles.Length) diff --git a/src/Compilers/VisualBasic/Test/Semantic/Semantics/AsyncAwait.vb b/src/Compilers/VisualBasic/Test/Semantic/Semantics/AsyncAwait.vb index f90e9e6cf9b42..259315ef4700b 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Semantics/AsyncAwait.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Semantics/AsyncAwait.vb @@ -42,6 +42,15 @@ End Module AssertTheseDiagnostics(compilation, ) End Sub + + Public Sub DefaultAwaitExpressionInfo() + Dim awaitInfo As AwaitExpressionInfo = Nothing + + Assert.Null(awaitInfo.GetAwaiterMethod) + Assert.Null(awaitInfo.IsCompletedProperty) + Assert.Null(awaitInfo.GetResultMethod) + End Sub + Public Sub AwaitableType01() Dim source = diff --git a/src/EditorFeatures/CSharpTest/CodeRefactorings/AddMissingImports/CSharpAddMissingImportsRefactoringProviderTests.cs b/src/EditorFeatures/CSharpTest/CodeRefactorings/AddMissingImports/CSharpAddMissingImportsRefactoringProviderTests.cs index f76773c791745..f7186492d9dc6 100644 --- a/src/EditorFeatures/CSharpTest/CodeRefactorings/AddMissingImports/CSharpAddMissingImportsRefactoringProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/CodeRefactorings/AddMissingImports/CSharpAddMissingImportsRefactoringProviderTests.cs @@ -336,5 +336,51 @@ public class D { } await TestMissingInRegularAndScriptAsync(code); } + + [WorkItem(31768, "https://github.com/dotnet/roslyn/issues/31768")] + [WpfFact] + public async Task AddMissingImports_AddMultipleImports_NoPreviousImports() + { + var code = @" +class C +{ + [|public D Foo { get; } + public E Bar { get; }|] +} + +namespace A +{ + public class D { } +} + +namespace B +{ + public class E { } +} +"; + + var expected = @" +using A; +using B; + +class C +{ + public D Foo { get; } + public E Bar { get; } +} + +namespace A +{ + public class D { } +} + +namespace B +{ + public class E { } +} +"; + + await TestInRegularAndScriptAsync(code, expected, placeSystemNamespaceFirst: false, separateImportDirectiveGroups: false); + } } } diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs index 2413ebe144228..c333ddf4b8a07 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs @@ -162,7 +162,7 @@ private class ThrowingDoNotCatchDiagnosticAnalyzer : Throwing public DiagnosticAnalyzerCategory GetAnalyzerCategory() { - return DiagnosticAnalyzerCategory.SyntaxAnalysis | DiagnosticAnalyzerCategory.SemanticDocumentAnalysis | DiagnosticAnalyzerCategory.ProjectAnalysis; + return DiagnosticAnalyzerCategory.SyntaxTreeWithoutSemanticsAnalysis | DiagnosticAnalyzerCategory.SemanticDocumentAnalysis | DiagnosticAnalyzerCategory.ProjectAnalysis; } } diff --git a/src/EditorFeatures/Core/Implementation/RenameTracking/RenameTrackingDiagnosticAnalyzer.cs b/src/EditorFeatures/Core/Implementation/RenameTracking/RenameTrackingDiagnosticAnalyzer.cs index 4b34a4ffc8133..cbb077dc7c741 100644 --- a/src/EditorFeatures/Core/Implementation/RenameTracking/RenameTrackingDiagnosticAnalyzer.cs +++ b/src/EditorFeatures/Core/Implementation/RenameTracking/RenameTrackingDiagnosticAnalyzer.cs @@ -19,12 +19,15 @@ internal sealed class RenameTrackingDiagnosticAnalyzer : DiagnosticAnalyzer, IBu internal const string RenameToPropertyKey = "RenameTo"; public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptor); - public bool OpenFileOnly(Workspace workspace) => true; + + public DiagnosticAnalyzerCategory GetAnalyzerCategory() + => DiagnosticAnalyzerCategory.SyntaxTreeWithoutSemanticsAnalysis; + + public bool OpenFileOnly(Workspace workspace) + => true; public override void Initialize(AnalysisContext context) - { - context.RegisterSyntaxTreeAction(AnalyzeSyntaxTree); - } + => context.RegisterSyntaxTreeAction(AnalyzeSyntaxTree); private void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) { @@ -35,8 +38,5 @@ private void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) context.ReportDiagnostic(diagnostic); } } - - public DiagnosticAnalyzerCategory GetAnalyzerCategory() - => DiagnosticAnalyzerCategory.SyntaxAnalysis; } } diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index a4204d84a9a44..09425c2a19c7d 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -430,7 +430,7 @@ public override void Initialize(AnalysisContext context) public DiagnosticAnalyzerCategory GetAnalyzerCategory() { - return DiagnosticAnalyzerCategory.SyntaxAnalysis; + return DiagnosticAnalyzerCategory.SyntaxTreeWithoutSemanticsAnalysis; } public bool OpenFileOnly(Workspace workspace) diff --git a/src/EditorFeatures/TestUtilities/Diagnostics/AbstractSuppressionAllCodeTests.cs b/src/EditorFeatures/TestUtilities/Diagnostics/AbstractSuppressionAllCodeTests.cs index 18e6d8ad3a78e..3838b9e931214 100644 --- a/src/EditorFeatures/TestUtilities/Diagnostics/AbstractSuppressionAllCodeTests.cs +++ b/src/EditorFeatures/TestUtilities/Diagnostics/AbstractSuppressionAllCodeTests.cs @@ -151,9 +151,7 @@ public override ImmutableArray SupportedDiagnostics } public DiagnosticAnalyzerCategory GetAnalyzerCategory() - { - return DiagnosticAnalyzerCategory.SyntaxAnalysis; - } + => DiagnosticAnalyzerCategory.SyntaxTreeWithoutSemanticsAnalysis; public override void Initialize(AnalysisContext analysisContext) { diff --git a/src/EditorFeatures/Text/Microsoft.CodeAnalysis.EditorFeatures.Text.csproj b/src/EditorFeatures/Text/Microsoft.CodeAnalysis.EditorFeatures.Text.csproj index f60a83e4e3ac4..d09af2e957c01 100644 --- a/src/EditorFeatures/Text/Microsoft.CodeAnalysis.EditorFeatures.Text.csproj +++ b/src/EditorFeatures/Text/Microsoft.CodeAnalysis.EditorFeatures.Text.csproj @@ -58,6 +58,15 @@ + + + + + + + + + @@ -86,4 +95,4 @@ - \ No newline at end of file + diff --git a/src/EditorFeatures/VisualBasicTest/CodeRefactorings/AddMissingImports/VisualBasicAddMissingImportsRefactoringProviderTests.vb b/src/EditorFeatures/VisualBasicTest/CodeRefactorings/AddMissingImports/VisualBasicAddMissingImportsRefactoringProviderTests.vb index acfb142216d55..4ebae15e1b9fe 100644 --- a/src/EditorFeatures/VisualBasicTest/CodeRefactorings/AddMissingImports/VisualBasicAddMissingImportsRefactoringProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/CodeRefactorings/AddMissingImports/VisualBasicAddMissingImportsRefactoringProviderTests.vb @@ -315,5 +315,48 @@ End Namespace Await TestInRegularAndScriptAsync(code, expected) End Function + + + + Public Async Function AddMissingImports_AddMultipleImports_NoPreviousImports() As Task + Dim code = " +Class C + [|Dim foo As D + Dim bar As E|] +End Class + +Namespace A + Public Class D + End Class +End Namespace + +Namespace B + Public Class E + End Class +End Namespace +" + + Dim expected = " +Imports A +Imports B + +Class C + Dim foo As D + Dim bar As E +End Class + +Namespace A + Public Class D + End Class +End Namespace + +Namespace B + Public Class E + End Class +End Namespace +" + + Await TestInRegularAndScriptAsync(code, expected, placeSystemNamespaceFirst:=False, separateImportDirectiveGroups:=False) + End Function End Class End Namespace diff --git a/src/Features/Core/Portable/AddAccessibilityModifiers/AbstractAddAccessibilityModifiersDiagnosticAnalyzer.cs b/src/Features/Core/Portable/AddAccessibilityModifiers/AbstractAddAccessibilityModifiersDiagnosticAnalyzer.cs index b1ec01e1ff8b9..007665311bbee 100644 --- a/src/Features/Core/Portable/AddAccessibilityModifiers/AbstractAddAccessibilityModifiersDiagnosticAnalyzer.cs +++ b/src/Features/Core/Portable/AddAccessibilityModifiers/AbstractAddAccessibilityModifiersDiagnosticAnalyzer.cs @@ -20,7 +20,7 @@ protected AbstractAddAccessibilityModifiersDiagnosticAnalyzer() } public sealed override DiagnosticAnalyzerCategory GetAnalyzerCategory() - => DiagnosticAnalyzerCategory.SyntaxAnalysis; + => DiagnosticAnalyzerCategory.SyntaxTreeWithoutSemanticsAnalysis; public sealed override bool OpenFileOnly(Workspace workspace) => false; diff --git a/src/Features/Core/Portable/CodeRefactorings/AddMissingImports/AbstractAddMissingImportsFeatureService.cs b/src/Features/Core/Portable/CodeRefactorings/AddMissingImports/AbstractAddMissingImportsFeatureService.cs index 5803754e571e1..3c5596f82b733 100644 --- a/src/Features/Core/Portable/CodeRefactorings/AddMissingImports/AbstractAddMissingImportsFeatureService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/AddMissingImports/AbstractAddMissingImportsFeatureService.cs @@ -2,11 +2,15 @@ using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.AddImport; using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Formatting.Rules; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Packaging; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -150,13 +154,67 @@ private async Task ApplyFixesAsync(Document document, ImmutableArray change.Span.IsEmpty) .OrderBy(change => change.NewText); + // Capture each location where we are inserting imports as well as the total + // length of the text we are inserting so that we can format the span afterwards. + var insertSpans = allTextChanges + .GroupBy(change => change.Span) + .Select(changes => new TextSpan(changes.Key.Start, changes.Sum(change => change.NewText.Length))); + var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var newText = text.WithChanges(orderedTextInserts); var newDocument = newProject.GetDocument(document.Id).WithText(newText); + // When imports are added to a code file that has no previous imports, extra + // newlines are generated between each import because the fix is expecting to + // separate the imports from the rest of the code file. We need to format the + // imports to remove these extra newlines. + return await CleanUpNewLinesAsync(newDocument, insertSpans, cancellationToken).ConfigureAwait(false); + } + + private async Task CleanUpNewLinesAsync(Document document, IEnumerable insertSpans, CancellationToken cancellationToken) + { + var languageFormatter = document.GetLanguageService(); + var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); + + var newDocument = document; + + // Since imports can be added at both the CompilationUnit and the Namespace level, + // format each span individually so that we can retain each newline that was intended + // to separate the import section from the other content. + foreach (var insertSpan in insertSpans) + { + newDocument = await CleanUpNewLinesAsync(newDocument, insertSpan, languageFormatter, options, cancellationToken).ConfigureAwait(false); + } + return newDocument; } + private async Task CleanUpNewLinesAsync(Document document, TextSpan insertSpan, ISyntaxFormattingService languageFormatter, OptionSet options, CancellationToken cancellationToken) + { + var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + + var textChanges = languageFormatter.Format(root, new[] { insertSpan }, options, new[] { new CleanUpNewLinesFormatter(text) }, cancellationToken).GetTextChanges(); + + // If there are no changes then, do less work. + if (textChanges.Count == 0) + { + return document; + } + + // The last text change should include where the insert span ends + Debug.Assert(textChanges.Last().Span.IntersectsWith(insertSpan.End)); + + // If there are changes then, this was a case where there were no + // previous imports statements. We need to retain the final extra + // newline because that separates the imports section from the rest + // of the code. + textChanges.RemoveAt(textChanges.Count - 1); + + var newText = text.WithChanges(textChanges); + return document.WithText(newText); + } + private async Task<(ProjectChanges, IEnumerable)> GetChangesForCodeActionAsync( Document document, CodeAction codeAction, @@ -175,5 +233,31 @@ private async Task ApplyFixesAsync(Document document, ImmutableArray nextOperation) + { + // Since we know the general shape of these new import statements, we simply look for where + // tokens are not on the same line and force them to only be separated by a single newline. + + _text.GetLineAndOffset(previousToken.Span.Start, out int previousLine, out _); + _text.GetLineAndOffset(currentToken.Span.Start, out int currentLine, out _); + + if (previousLine != currentLine) + { + return FormattingOperations.CreateAdjustNewLinesOperation(1, AdjustNewLinesOption.ForceLines); + } + + return null; + } + } } } diff --git a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerCategory.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerCategory.cs index 3d180351a9be3..7b5399ab946e4 100644 --- a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerCategory.cs +++ b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerCategory.cs @@ -14,8 +14,13 @@ internal enum DiagnosticAnalyzerCategory /// /// Analyzer reports syntax diagnostics (i.e. registers a SyntaxTree action). + /// Note: an that uses this will not work properly if + /// it registers a and then ends + /// up needing to use the . If a + /// is needed, use or + /// . /// - SyntaxAnalysis = 0x0001, + SyntaxTreeWithoutSemanticsAnalysis = 0x0001, /// /// Analyzer reports semantic diagnostics and also supports incremental span based method body analysis. diff --git a/src/Features/Core/Portable/Formatting/FormattingDiagnosticAnalyzer.cs b/src/Features/Core/Portable/Formatting/FormattingDiagnosticAnalyzer.cs index 9c094df504e8d..65a850b9e96bc 100644 --- a/src/Features/Core/Portable/Formatting/FormattingDiagnosticAnalyzer.cs +++ b/src/Features/Core/Portable/Formatting/FormattingDiagnosticAnalyzer.cs @@ -18,7 +18,7 @@ public FormattingDiagnosticAnalyzer() } public override DiagnosticAnalyzerCategory GetAnalyzerCategory() - => DiagnosticAnalyzerCategory.SyntaxAnalysis; + => DiagnosticAnalyzerCategory.SyntaxTreeWithoutSemanticsAnalysis; public override bool OpenFileOnly(Workspace workspace) => false; diff --git a/src/Features/Core/Portable/OrderModifiers/AbstractOrderModifiersDiagnosticAnalyzer.cs b/src/Features/Core/Portable/OrderModifiers/AbstractOrderModifiersDiagnosticAnalyzer.cs index 84d0f7d541d0e..91db5d238e9e1 100644 --- a/src/Features/Core/Portable/OrderModifiers/AbstractOrderModifiersDiagnosticAnalyzer.cs +++ b/src/Features/Core/Portable/OrderModifiers/AbstractOrderModifiersDiagnosticAnalyzer.cs @@ -28,13 +28,14 @@ protected AbstractOrderModifiersDiagnosticAnalyzer( _helpers = helpers; } - public override DiagnosticAnalyzerCategory GetAnalyzerCategory() => DiagnosticAnalyzerCategory.SyntaxAnalysis; - public override bool OpenFileOnly(Workspace workspace) => false; + public override DiagnosticAnalyzerCategory GetAnalyzerCategory() + => DiagnosticAnalyzerCategory.SyntaxTreeWithoutSemanticsAnalysis; + + public override bool OpenFileOnly(Workspace workspace) + => false; protected override void InitializeWorker(AnalysisContext context) - { - context.RegisterSyntaxTreeAction(AnalyzeSyntaxTree); - } + => context.RegisterSyntaxTreeAction(AnalyzeSyntaxTree); private void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) { diff --git a/src/Features/Core/Portable/Shared/Extensions/DiagnosticAnalyzerExtensions.cs b/src/Features/Core/Portable/Shared/Extensions/DiagnosticAnalyzerExtensions.cs index f88149593b4d4..f6cd0b8684c46 100644 --- a/src/Features/Core/Portable/Shared/Extensions/DiagnosticAnalyzerExtensions.cs +++ b/src/Features/Core/Portable/Shared/Extensions/DiagnosticAnalyzerExtensions.cs @@ -12,7 +12,7 @@ public static DiagnosticAnalyzerCategory GetDiagnosticAnalyzerCategory(this Diag if (analyzer is DocumentDiagnosticAnalyzer) { - category |= DiagnosticAnalyzerCategory.SyntaxAnalysis | DiagnosticAnalyzerCategory.SemanticDocumentAnalysis; + category |= DiagnosticAnalyzerCategory.SyntaxTreeWithoutSemanticsAnalysis | DiagnosticAnalyzerCategory.SemanticDocumentAnalysis; } else if (analyzer is ProjectDiagnosticAnalyzer) { @@ -28,7 +28,7 @@ public static DiagnosticAnalyzerCategory GetDiagnosticAnalyzerCategory(this Diag { // It is not possible to know the categorization for a public analyzer, // so return a worst-case categorization. - category = (DiagnosticAnalyzerCategory.SyntaxAnalysis | DiagnosticAnalyzerCategory.SemanticDocumentAnalysis | DiagnosticAnalyzerCategory.ProjectAnalysis); + category = (DiagnosticAnalyzerCategory.SyntaxTreeWithoutSemanticsAnalysis | DiagnosticAnalyzerCategory.SemanticDocumentAnalysis | DiagnosticAnalyzerCategory.ProjectAnalysis); } } @@ -38,7 +38,7 @@ public static DiagnosticAnalyzerCategory GetDiagnosticAnalyzerCategory(this Diag public static bool SupportsSyntaxDiagnosticAnalysis(this DiagnosticAnalyzer analyzer) { var category = analyzer.GetDiagnosticAnalyzerCategory(); - return (category & DiagnosticAnalyzerCategory.SyntaxAnalysis) != 0; + return (category & DiagnosticAnalyzerCategory.SyntaxTreeWithoutSemanticsAnalysis) != 0; } public static bool SupportsSemanticDiagnosticAnalysis(this DiagnosticAnalyzer analyzer) diff --git a/src/Test/Utilities/Portable/Assert/AssertEx.cs b/src/Test/Utilities/Portable/Assert/AssertEx.cs index 2c1864ed1b461..88c71b833bf58 100644 --- a/src/Test/Utilities/Portable/Assert/AssertEx.cs +++ b/src/Test/Utilities/Portable/Assert/AssertEx.cs @@ -674,5 +674,52 @@ public static void Throws(Action action, Action checker checker?.Invoke((TException)e); } } + + public static void Equal(bool[,] expected, Func getResult, int size) + { + Equal(expected, getResult, (b1, b2) => b1 == b2, b => b ? "true" : "false", "{0,-6:G}", size); + } + + public static void Equal(T[,] expected, Func getResult, Func valuesEqual, Func printValue, string format, int size) + { + bool mismatch = false; + for (int i = 0; i < size; i++) + { + for (int j = 0; j < size; j++) + { + if (!valuesEqual(expected[i, j], getResult(i, j))) + { + mismatch = true; + } + } + } + + if (mismatch) + { + var builder = new StringBuilder(); + builder.AppendLine("Actual result: "); + for (int i = 0; i < size; i++) + { + builder.Append("{ "); + for (int j = 0; j < size; j++) + { + string resultWithComma = printValue(getResult(i, j)); + if (j < size - 1) + { + resultWithComma += ","; + } + + builder.Append(string.Format(format, resultWithComma)); + if (j < size - 1) + { + builder.Append(' '); + } + } + builder.AppendLine("},"); + } + + Assert.True(false, builder.ToString()); + } + } } } diff --git a/src/VisualStudio/Core/Def/Implementation/FindReferences/StreamingFindUsagesPresenter.cs b/src/VisualStudio/Core/Def/Implementation/FindReferences/StreamingFindUsagesPresenter.cs index 17d80e1170a5f..92c97f61abf17 100644 --- a/src/VisualStudio/Core/Def/Implementation/FindReferences/StreamingFindUsagesPresenter.cs +++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/StreamingFindUsagesPresenter.cs @@ -120,7 +120,7 @@ private AbstractTableDataSourceFindUsagesContext StartSearchWorker(string title, var window = _vsFindAllReferencesService.StartSearch(title); // Keep track of the users preference for grouping by definition if we don't already know it. - // We need this because we disable the Definition column when wer're not showing references + // We need this because we disable the Definition column when we're not showing references // (i.e. GoToImplementation/GoToDef). However, we want to restore the user's choice if they // then do another FindAllReferences. var desiredGroupingPriority = _workspace.Options.GetOption(FindUsagesOptions.DefinitionGroupingPriority); diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs index fc937a02cf72b..2f81e23d0e16e 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs @@ -935,7 +935,7 @@ private static AttributeListSyntax AsAttributeList(SyntaxNode node) } else { - return ((AttributeListSyntax)node).WithTarget(null); + return (AttributeListSyntax)node; } } diff --git a/src/Workspaces/CSharp/Portable/Formatting/CSharpFormattingOptions.Parsers.cs b/src/Workspaces/CSharp/Portable/Formatting/CSharpFormattingOptions.Parsers.cs index 2055ee5ca5a84..98e0645efef37 100644 --- a/src/Workspaces/CSharp/Portable/Formatting/CSharpFormattingOptions.Parsers.cs +++ b/src/Workspaces/CSharp/Portable/Formatting/CSharpFormattingOptions.Parsers.cs @@ -47,13 +47,13 @@ private static string GetSpacingWithParenthesesEditorConfigString(OptionSet opti } internal static BinaryOperatorSpacingOptions ParseEditorConfigSpacingAroundBinaryOperator(string binaryOperatorSpacingValue) - => s_binaryOperatorSpacingOptionsEditorConfigMap.TryGetValue(binaryOperatorSpacingValue, out var value) ? value : BinaryOperatorSpacingOptions.Single; + => s_binaryOperatorSpacingOptionsEditorConfigMap.TryGetValue(binaryOperatorSpacingValue.Trim(), out var value) ? value : BinaryOperatorSpacingOptions.Single; private static string GetSpacingAroundBinaryOperatorEditorConfigString(BinaryOperatorSpacingOptions value) => s_binaryOperatorSpacingOptionsEditorConfigMap.TryGetKey(value, out string key) ? key : null; internal static LabelPositionOptions ParseEditorConfigLabelPositioning(string labelIndentationValue) - => s_labelPositionOptionsEditorConfigMap.TryGetValue(labelIndentationValue, out var value) ? value : LabelPositionOptions.NoIndent; + => s_labelPositionOptionsEditorConfigMap.TryGetValue(labelIndentationValue.Trim(), out var value) ? value : LabelPositionOptions.NoIndent; private static string GetLabelPositionOptionEditorConfigString(LabelPositionOptions value) => s_labelPositionOptionsEditorConfigMap.TryGetKey(value, out string key) ? key : null; diff --git a/src/Workspaces/CSharpTest/CodeGeneration/AddAttributesTests.cs b/src/Workspaces/CSharpTest/CodeGeneration/AddAttributesTests.cs new file mode 100644 index 0000000000000..6f4d906fa5f39 --- /dev/null +++ b/src/Workspaces/CSharpTest/CodeGeneration/AddAttributesTests.cs @@ -0,0 +1,78 @@ +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGeneration +{ + [UseExportProvider] + public class AddAttributesTests + { + private Document GetDocument(string code) + { + var ws = new AdhocWorkspace(); + var emptyProject = ws.AddProject( + ProjectInfo.Create( + ProjectId.CreateNewId(), + VersionStamp.Default, + "test", + "test.dll", + LanguageNames.CSharp, + metadataReferences: new[] { TestReferences.NetFx.v4_0_30319.mscorlib })); + + return emptyProject.AddDocument("test.cs", code); + } + + private async Task TestAsync(string initialText, string attributeAddedText) + { + var doc = GetDocument(initialText); + var options = await doc.GetOptionsAsync(); + + var attributeList = + SyntaxFactory.AttributeList( + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.Attribute( + SyntaxFactory.IdentifierName("System.Reflection.AssemblyVersion(\"1.0.0.0\")")))) + .WithTarget( + SyntaxFactory.AttributeTargetSpecifier( + SyntaxFactory.Token(SyntaxKind.AssemblyKeyword))); + + var syntaxRoot = await doc.GetSyntaxRootAsync(); + var editor = await DocumentEditor.CreateAsync(doc); + + editor.AddAttribute(syntaxRoot, attributeList); + + var changedDoc = editor.GetChangedDocument(); + + if (attributeAddedText != null) + { + var formatted = await Formatter.FormatAsync(changedDoc, SyntaxAnnotation.ElasticAnnotation, options); + var actualText = (await formatted.GetTextAsync()).ToString(); + + Assert.Equal(attributeAddedText, actualText); + } + } + + [Fact] + public async Task TestAddAssemblyAttributeListToEmptyDocument() + { + await TestAsync( +string.Empty, +@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] + +"); + } + + [Fact] + public async Task TestAddAssemblyAttributeListToDocumentWithOtherAssemblyAttribute() + { + await TestAsync( +@"[assembly: System.Reflection.AssemblyName(""Test"")]", +@"[assembly: System.Reflection.AssemblyName(""Test"")] +[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] + +"); + } + } +} diff --git a/src/Workspaces/CSharpTest/CodeStyle/CSharpEditorConfigCodeStyleParserTests.cs b/src/Workspaces/CSharpTest/CodeStyle/CSharpEditorConfigCodeStyleParserTests.cs new file mode 100644 index 0000000000000..c68b11d58531d --- /dev/null +++ b/src/Workspaces/CSharpTest/CodeStyle/CSharpEditorConfigCodeStyleParserTests.cs @@ -0,0 +1,60 @@ +// 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.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.Formatting; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeStyle +{ + public class CSharpEditorConfigCodeStyleParserTests + { + [Theory] + [InlineData("ignore", BinaryOperatorSpacingOptions.Ignore)] + [InlineData("none", BinaryOperatorSpacingOptions.Remove)] + [InlineData("before_and_after", BinaryOperatorSpacingOptions.Single)] + + [WorkItem(27685, "https://github.com/dotnet/roslyn/issues/27685")] + [InlineData(" ignore ", BinaryOperatorSpacingOptions.Ignore)] + [InlineData(" none ", BinaryOperatorSpacingOptions.Remove)] + [InlineData(" before_and_after ", BinaryOperatorSpacingOptions.Single)] + public void TestParseSpacingAroundBinaryOperator(string rawValue, BinaryOperatorSpacingOptions parsedValue) + { + Assert.Equal(parsedValue, CSharpFormattingOptions.ParseEditorConfigSpacingAroundBinaryOperator(rawValue)); + } + + [Theory] + [InlineData("flush_left", LabelPositionOptions.LeftMost)] + [InlineData("no_change", LabelPositionOptions.NoIndent)] + [InlineData("one_less_than_current", LabelPositionOptions.OneLess)] + + [WorkItem(27685, "https://github.com/dotnet/roslyn/issues/27685")] + [InlineData(" flush_left ", LabelPositionOptions.LeftMost)] + [InlineData(" no_change ", LabelPositionOptions.NoIndent)] + [InlineData(" one_less_than_current ", LabelPositionOptions.OneLess)] + public void TestParseLabelPositioning(string rawValue, LabelPositionOptions parsedValue) + { + Assert.Equal(parsedValue, CSharpFormattingOptions.ParseEditorConfigLabelPositioning(rawValue)); + } + + [Theory] + [InlineData("false:none", (int)ExpressionBodyPreference.Never, ReportDiagnostic.Suppress)] + [InlineData("true:warning", (int)ExpressionBodyPreference.WhenPossible, ReportDiagnostic.Warn)] + [InlineData("when_on_single_line:error", (int)ExpressionBodyPreference.WhenOnSingleLine, ReportDiagnostic.Error)] + + [WorkItem(27685, "https://github.com/dotnet/roslyn/issues/27685")] + [InlineData("false : none", (int)ExpressionBodyPreference.Never, ReportDiagnostic.Suppress)] + [InlineData("true : warning", (int)ExpressionBodyPreference.WhenPossible, ReportDiagnostic.Warn)] + [InlineData("when_on_single_line : error", (int)ExpressionBodyPreference.WhenOnSingleLine, ReportDiagnostic.Error)] + public void TestParseExpressionBodyPreference(string optionString, int parsedValue, ReportDiagnostic severity) + { + var defaultValue = new CodeStyleOption(ExpressionBodyPreference.Never, NotificationOption.Error); + var codeStyleOption = CSharpCodeStyleOptions.ParseExpressionBodyPreference(optionString, defaultValue); + + Assert.NotSame(defaultValue, codeStyleOption); + Assert.Equal((ExpressionBodyPreference)parsedValue, codeStyleOption.Value); + Assert.Equal(severity, codeStyleOption.Notification.Severity); + } + } +} diff --git a/src/Workspaces/Core/Portable/CodeStyle/CodeStyleHelpers.cs b/src/Workspaces/Core/Portable/CodeStyle/CodeStyleHelpers.cs index cf6cd2019e87f..93c02ae11a1fa 100644 --- a/src/Workspaces/Core/Portable/CodeStyle/CodeStyleHelpers.cs +++ b/src/Workspaces/Core/Portable/CodeStyle/CodeStyleHelpers.cs @@ -28,7 +28,7 @@ public static bool TryParseBoolEditorConfigCodeStyleOption(string arg, out CodeS arg, out string value, out NotificationOption notificationOpt)) { // First value has to be true or false. Anything else is unsupported. - if (bool.TryParse(value.Trim(), out var isEnabled)) + if (bool.TryParse(value, out var isEnabled)) { // We allow 'false' to be provided without a notification option. However, // 'true' must always be provided with a notification option. diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs index dab79f648c4a7..dd1ba2a161d2e 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs @@ -91,10 +91,10 @@ private async Task ProcessAsync(ProjectToDocumentMap projectToDocumentMap) // it. // For each connected component, we'll process the individual projects from bottom to // top. i.e. we'll first process the projects with no dependencies. Then the projects - // that depend on those projects, and so and. This way we always have creates the + // that depend on those projects, and so on. This way we always have created the // dependent compilations when they're needed by later projects. If we went the other // way (i.e. processed the projects with lots of project dependencies first), then we'd - // have to create all their depedent compilations in order to get their compilation. + // have to create all their dependent compilations in order to get their compilation. // This would be very expensive and would take a lot of time before we got our first // result. var connectedProjects = _dependencyGraph.GetDependencySets(_cancellationToken); diff --git a/src/Workspaces/Core/Portable/Formatting/FormattingOptions.cs b/src/Workspaces/Core/Portable/Formatting/FormattingOptions.cs index fc493457cad3b..69548e1b239e2 100644 --- a/src/Workspaces/Core/Portable/Formatting/FormattingOptions.cs +++ b/src/Workspaces/Core/Portable/Formatting/FormattingOptions.cs @@ -92,7 +92,7 @@ private static Option CreateOption(OptionGroup group, string name, T defau }); private static Optional ParseEditorConfigEndOfLine(string endOfLineValue) - => s_parenthesesPreferenceMap.TryGetValue(endOfLineValue, out var parsedOption) ? parsedOption : NewLine.DefaultValue; + => s_parenthesesPreferenceMap.TryGetValue(endOfLineValue.Trim(), out var parsedOption) ? parsedOption : NewLine.DefaultValue; private static string GetEndOfLineEditorConfigString(string option) => s_parenthesesPreferenceMap.TryGetKey(option, out var editorConfigString) ? editorConfigString : null; diff --git a/src/Workspaces/Core/Portable/Utilities/Documentation/XmlDocumentationProvider.cs b/src/Workspaces/Core/Portable/Utilities/Documentation/XmlDocumentationProvider.cs index 50e3e840bff05..073391b403686 100644 --- a/src/Workspaces/Core/Portable/Utilities/Documentation/XmlDocumentationProvider.cs +++ b/src/Workspaces/Core/Portable/Utilities/Documentation/XmlDocumentationProvider.cs @@ -37,6 +37,8 @@ public static XmlDocumentationProvider CreateFromBytes(byte[] xmlDocCommentBytes return new ContentBasedXmlDocumentationProvider(xmlDocCommentBytes); } + private static XmlDocumentationProvider DefaultXmlDocumentationProvider { get; } = new NullXmlDocumentationProvider(); + /// /// Creates an from an XML documentation file. /// @@ -44,6 +46,11 @@ public static XmlDocumentationProvider CreateFromBytes(byte[] xmlDocCommentBytes /// An . public static XmlDocumentationProvider CreateFromFile(string xmlDocCommentFilePath) { + if (!File.Exists(xmlDocCommentFilePath)) + { + return DefaultXmlDocumentationProvider; + } + return new FileBasedXmlDocumentationProvider(xmlDocCommentFilePath); } @@ -180,5 +187,32 @@ public override int GetHashCode() return _filePath.GetHashCode(); } } + + /// + /// A trivial XmlDocumentationProvider which never returns documentation. + /// + private sealed class NullXmlDocumentationProvider : XmlDocumentationProvider + { + protected override string GetDocumentationForSymbol(string documentationMemberID, CultureInfo preferredCulture, CancellationToken cancellationToken = default(CancellationToken)) + { + return ""; + } + + protected override Stream GetSourceStream(CancellationToken cancellationToken) + { + return new MemoryStream(); + } + + public override bool Equals(object obj) + { + // Only one instance is expected to exist, so reference equality is fine. + return (object)this == obj; + } + + public override int GetHashCode() + { + return 0; + } + } } } diff --git a/src/Workspaces/CoreTest/CodeStyle/EditorConfigCodeStyleParserTests.cs b/src/Workspaces/CoreTest/CodeStyle/EditorConfigCodeStyleParserTests.cs index 90b6eeaaf0a04..1bae6f86620b7 100644 --- a/src/Workspaces/CoreTest/CodeStyle/EditorConfigCodeStyleParserTests.cs +++ b/src/Workspaces/CoreTest/CodeStyle/EditorConfigCodeStyleParserTests.cs @@ -1,11 +1,11 @@ // 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.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Options; +using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.UnitTests.CodeStyle @@ -29,7 +29,13 @@ public class EditorConfigCodeStyleParserTests [InlineData("false", false, ReportDiagnostic.Hidden)] [InlineData("*", false, ReportDiagnostic.Hidden)] [InlineData("false:false", false, ReportDiagnostic.Hidden)] - static void TestParseEditorConfigCodeStyleOption(string args, bool isEnabled, ReportDiagnostic severity) + + [WorkItem(27685, "https://github.com/dotnet/roslyn/issues/27685")] + [InlineData("true : warning", true, ReportDiagnostic.Warn)] + [InlineData("false : warning", false, ReportDiagnostic.Warn)] + [InlineData("true : error", true, ReportDiagnostic.Error)] + [InlineData("false : error", false, ReportDiagnostic.Error)] + public void TestParseEditorConfigCodeStyleOption(string args, bool isEnabled, ReportDiagnostic severity) { var notificationOption = NotificationOption.Silent; switch (severity) @@ -56,5 +62,49 @@ static void TestParseEditorConfigCodeStyleOption(string args, bool isEnabled, Re Assert.True(result.Notification.Severity == severity, $"Expected {nameof(severity)} to be {severity}, was {result.Notification.Severity}"); } + + [Theory] + [InlineData("never:none", (int)AccessibilityModifiersRequired.Never, ReportDiagnostic.Suppress)] + [InlineData("always:suggestion", (int)AccessibilityModifiersRequired.Always, ReportDiagnostic.Info)] + [InlineData("for_non_interface_members:warning", (int)AccessibilityModifiersRequired.ForNonInterfaceMembers, ReportDiagnostic.Warn)] + [InlineData("omit_if_default:error", (int)AccessibilityModifiersRequired.OmitIfDefault, ReportDiagnostic.Error)] + + [WorkItem(27685, "https://github.com/dotnet/roslyn/issues/27685")] + [InlineData("never : none", (int)AccessibilityModifiersRequired.Never, ReportDiagnostic.Suppress)] + [InlineData("always : suggestion", (int)AccessibilityModifiersRequired.Always, ReportDiagnostic.Info)] + [InlineData("for_non_interface_members : warning", (int)AccessibilityModifiersRequired.ForNonInterfaceMembers, ReportDiagnostic.Warn)] + [InlineData("omit_if_default : error", (int)AccessibilityModifiersRequired.OmitIfDefault, ReportDiagnostic.Error)] + public void TestParseEditorConfigAccessibilityModifiers(string args, int value, ReportDiagnostic severity) + { + var storageLocation = CodeStyleOptions.RequireAccessibilityModifiers.StorageLocations + .OfType>>() + .Single(); + var allRawConventions = new Dictionary { { storageLocation.KeyName, args } }; + + Assert.True(storageLocation.TryGetOption(null, allRawConventions, typeof(CodeStyleOption), out var parsedCodeStyleOption)); + var codeStyleOption = (CodeStyleOption)parsedCodeStyleOption; + Assert.Equal((AccessibilityModifiersRequired)value, codeStyleOption.Value); + Assert.Equal(severity, codeStyleOption.Notification.Severity); + } + + [Theory] + [InlineData("lf", "\n")] + [InlineData("cr", "\r")] + [InlineData("crlf", "\r\n")] + + [WorkItem(27685, "https://github.com/dotnet/roslyn/issues/27685")] + [InlineData(" lf ", "\n")] + [InlineData(" cr ", "\r")] + [InlineData(" crlf ", "\r\n")] + public void TestParseEditorConfigEndOfLine(string configurationString, string newLine) + { + var storageLocation = FormattingOptions.NewLine.StorageLocations + .OfType>() + .Single(); + var allRawConventions = new Dictionary { { storageLocation.KeyName, configurationString } }; + + Assert.True(storageLocation.TryGetOption(null, allRawConventions, typeof(string), out var parsedNewLine)); + Assert.Equal(newLine, (string)parsedNewLine); + } } }