diff --git a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs index fc3f831bd67a35e08134619cf14186d94b5e3eb4..fb72b11227243d10d8330a241afdbb3810fa9229 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics; +using System.Linq; using System.Runtime.CompilerServices; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.Semantics; @@ -115,11 +115,17 @@ internal static IArgument CreateArgumentOperation(ArgumentKind kind, IParameterS return (ImmutableArray) s_callToArgumentsMappings.GetValue( boundNode, (n) => - { + { //TODO: https://github.com/dotnet/roslyn/issues/18722 // Right now, for erroneous code, we exposes all expression in place of arguments as IArgument with Parameter set to null, - // so user needs to check IsInvalid first before using anything we returned. Need to implement a new interface for invalid invocation instead. - if (n.HasErrors || (object)optionalParametersMethod == null) + // so user needs to check IsInvalid first before using anything we returned. Need to implement a new interface for invalid + // invocation instead. + // Note this check doesn't cover all scenarios. For example, when a parameter is a generic type but the type of the type argument + // is undefined. + if ((object)optionalParametersMethod == null + || n.HasAnyErrors + || parameters.Any(p => p.Type.IsErrorType()) + || optionalParametersMethod.GetUseSiteDiagnostic()?.DefaultSeverity == DiagnosticSeverity.Error) { // optionalParametersMethod can be null if we are writing to a readonly indexer or reading from an writeonly indexer, // in which case HasErrors property would be true, but we still want to treat this as invalid invocation. diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IArgument.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IArgument.cs index 80d13fe227fd40ce019440cc04e7692629424eb8..65254ef357b01c815687a9b5f2421db6c0d8e98d 100644 --- a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IArgument.cs +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IArgument.cs @@ -1711,6 +1711,164 @@ public void M2() VerifyOperationTreeAndDiagnosticsForTestWithIL(csharp, il, expectedOperationTree, expectedDiagnostics); } + [Fact, WorkItem(20330, "https://github.com/dotnet/roslyn/issues/20330")] + public void DefaultValueNullForNullableTypeParameterWithMissingNullableReference() + { + string source = @" +class P +{ + static void M1() + { + /**/M2()/**/; + } + + static void M2(bool? x = null) + { + } +} +"; + string expectedOperationTree = @"IInvocationExpression (static void P.M2([System.Boolean[missing]? x = null])) (OperationKind.InvocationExpression, Type: System.Void[missing]) (Syntax: 'M2()')"; + + var expectedDiagnostics = new DiagnosticDescription[] { + // (3,7): error CS0518: Predefined type 'System.Object' is not defined or imported + // class P + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "P").WithArguments("System.Object").WithLocation(2, 7), + // (10,20): error CS0518: Predefined type 'System.Nullable`1' is not defined or imported + // static void M2(bool? x = null) + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "bool?").WithArguments("System.Nullable`1").WithLocation(9, 20), + // (10,20): error CS0518: Predefined type 'System.Boolean' is not defined or imported + // static void M2(bool? x = null) + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "bool").WithArguments("System.Boolean").WithLocation(9, 20), + // (10,12): error CS0518: Predefined type 'System.Void' is not defined or imported + // static void M2(bool? x = null) + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "void").WithArguments("System.Void").WithLocation(9, 12), + // (5,12): error CS0518: Predefined type 'System.Void' is not defined or imported + // static void M1() + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "void").WithArguments("System.Void").WithLocation(4, 12), + // (7,19): error CS0518: Predefined type 'System.Object' is not defined or imported + // /**/M2()/**/; + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "M2").WithArguments("System.Object").WithLocation(6, 19), + // (3,7): error CS1729: 'object' does not contain a constructor that takes 0 arguments + // class P + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "P").WithArguments("object", "0").WithLocation(2, 7) + }; + + var compilation = CreateCompilation(source, options: Test.Utilities.TestOptions.ReleaseDll); + VerifyOperationTreeAndDiagnosticsForTest(compilation, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(20330, "https://github.com/dotnet/roslyn/issues/20330")] + public void DefaultValueWithParameterErrorType() + { + string source = @" +class P +{ + static void M1() + { + /**/M2(1)/**/; + } + + static void M2(int x, S s = 0) + { + } +} +"; + string expectedOperationTree = @" +IInvocationExpression (static void P.M2(System.Int32 x, [S s = null])) (OperationKind.InvocationExpression, Type: System.Void) (Syntax: 'M2(1)') + Arguments(1): IArgument (ArgumentKind.Explicit, Matching Parameter: null) (OperationKind.Argument, IsInvalid) (Syntax: '1') + ILiteralExpression (Text: 1) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 1) (Syntax: '1') +"; + + var expectedDiagnostics = new DiagnosticDescription[] { + // file.cs(9,27): error CS0246: The type or namespace name 'S' could not be found (are you missing a using directive or an assembly reference?) + // static void M2(int x, S s = 0) + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "S").WithArguments("S").WithLocation(9, 27), + // file.cs(9,29): error CS1750: A value of type 'int' cannot be used as a default parameter because there are no standard conversions to type 'S' + // static void M2(int x, S s = 0) + Diagnostic(ErrorCode.ERR_NoConversionForDefaultParam, "s").WithArguments("int", "S").WithLocation(9, 29) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + [WorkItem(18722, "https://github.com/dotnet/roslyn/issues/18722")] + public void DefaultValueForGenericWithUndefinedTypeArgument() + { + // TODO: https://github.com/dotnet/roslyn/issues/18722 + // This should be treated as invalid invocation. + string source = @" +class P +{ + static void M1() + { + /**/M2(1)/**/; + } + + static void M2(int x, G s = null) + { + } +} + +class G +{ +} +"; + string expectedOperationTree = @" +IInvocationExpression (static void P.M2(System.Int32 x, [G s = null])) (OperationKind.InvocationExpression, Type: System.Void) (Syntax: 'M2(1)') + Arguments(2): IArgument (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument) (Syntax: '1') + ILiteralExpression (Text: 1) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 1) (Syntax: '1') + IArgument (ArgumentKind.DefaultValue, Matching Parameter: s) (OperationKind.Argument) (Syntax: 'M2(1)') + ILiteralExpression (OperationKind.LiteralExpression, Type: G, Constant: null) (Syntax: 'M2(1)')"; + + var expectedDiagnostics = new DiagnosticDescription[] { + // file.cs(9,29): error CS0246: The type or namespace name 'S' could not be found (are you missing a using directive or an assembly reference?) + // static void M2(int x, G s = null) + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "S").WithArguments("S").WithLocation(9, 29) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + [WorkItem(18722, "https://github.com/dotnet/roslyn/issues/18722")] + public void DefaultValueForNullableGenericWithUndefinedTypeArgument() + { + // TODO: https://github.com/dotnet/roslyn/issues/18722 + // This should be treated as invalid invocation. + string source = @" +class P +{ + static void M1() + { + /**/M2(1)/**/; + } + + static void M2(int x, G? s = null) + { + } +} + +struct G +{ +} +"; + string expectedOperationTree = @" +IInvocationExpression (static void P.M2(System.Int32 x, [G? s = null])) (OperationKind.InvocationExpression, Type: System.Void) (Syntax: 'M2(1)') + Arguments(2): IArgument (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument) (Syntax: '1') + ILiteralExpression (Text: 1) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 1) (Syntax: '1') + IArgument (ArgumentKind.DefaultValue, Matching Parameter: s) (OperationKind.Argument) (Syntax: 'M2(1)') + IDefaultValueExpression (OperationKind.DefaultValueExpression, Type: G?) (Syntax: 'M2(1)')"; + + var expectedDiagnostics = new DiagnosticDescription[] { + // file.cs(9,29): error CS0246: The type or namespace name 'S' could not be found (are you missing a using directive or an assembly reference?) + // static void M2(int x, G s = null) + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "S").WithArguments("S").WithLocation(9, 29) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + private class IndexerAccessArgumentVerifier : OperationWalker { public static readonly IndexerAccessArgumentVerifier Instance = new IndexerAccessArgumentVerifier();